相关链接
System类
特点
- 包含很多有用的类字段和方法
- 不能被实例化(类的构造函数未对外提供,私有化了)
- 类中的方法和属性都是静态的
获取程序执行时间
先获取一个时间,程序然后执行,执行完在获取一个时间,相减就是程序运行时间。
折叠代码块JAVA
复制代码
1 2 3 4
| long currentTime_1 = System.currentTimeMillis();
long currentTime_2 = System.currentTimeMillis(); System.out.println("code running uses " + (currentTime_2 - currentTime_1 + "ms"));
|
不同系统用不同换行符
通过系统信息,用键获取对应系统换行的符号值,常用,可以写成全局常量。
折叠代码块JAVA
复制代码
1 2 3
|
System.out.println("hello " + System.getProperty("line.separator") + " world");
|
常用字段(属性)
折叠代码块YAML
复制代码
1 2 3 4 5
| err: 标准错误输出流(少用) in: 标准输入流,使用键盘等设备输入 out: 标准输出流,将系统信息输出来 - 本身代表一个对象(public static final PrintStream out),拥有打印方法。 - 通常在显示器的控制台进行输出
|
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10
| currentTimeMillis(): 获取当前时间毫秒值。与1970年1月1日0点之间的差值,返回长整形 exit(): 终止当前运行的Java虚拟机 gc(): 运行垃圾回收器 getProperties(): 获取当前系统属性信息,存储到了Properties集合中。 - Properties是HashTable子类(即Map子类),用于属性集。 - 该类没有指定泛型,是因为键值都是String类型 - Map取的时候一般用的entrySet()和keySet()来取,但都有泛型,这里可以使用其自有方法进行存取。 - JVM是跨平台的,不同平台下获取的信息不同 getProperty(String key): 根据某键获取系统属性中该键的值 setProperty("myclasscpath", "c:\myclasspath"): 系统启动时存放自定义系统信息,这是全局信息,其他程序也可以使用
|
系统属性中的键的解释
键都是固定的,系统版本不同,获取的值不同
折叠代码块YAML
复制代码
1 2 3 4 5 6 7
| sun.boot.library.path: 系统启动类库路径 user.dir: 用户目录 os.name: 操作系统名称 path.separator: 路径分隔符(windows和linux不同) line.separator: 行分隔符(windows和linux不同,windows是\r\n,linux是\n) file.separator: 文件分隔符(windows和linux不同) file.encoding: 系统默认字符集
|
常用方法
折叠代码块YAML
复制代码
1 2
| Set<String> stringPropertyNames(): 获取Properties的键集 getProperty(String key): 通过key来获取Properties中的value
|
Runtime类
特点
- 每个Java应用程序都有一个Runtime类实例,可以与运行环境相连接
- 没有构造函数,但有非静态方法,那么至少有一个静态方法来返回本类对象。(单例模式,保证对象唯一性)
- 使用getRuntime()方法获取当前对象
- Runtime类就是单例设计模式的类
常用方法
折叠代码块YAML
复制代码
1 2
| Runtime getRuntime(): 获取当前Runtime对象 Process exec(String cmd): 执行命令(可以用来执行本地程序),返回一个进程
|
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12
| Runtime r = Runtime.getRuntime();
try { Process p = r.exec("/usr/bin/date"); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); String line; while ((line = br.readLine())!= null) { System.out.print(line); } } catch (IOException e) { e.printStackTrace(); }
|
Process类
特点
- 由Runtime类的exec()方法执行后返回的对象
常用方法
折叠代码块YAML
复制代码
1
| destory(): 杀死Java程序开启的子进程
|
Math类
特点
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7
| Math.random(): 返回一个0到1的double伪随机数(次数多了有规律) - 乘x返回大于等于0,小于x的数 - 也可以使用Random类,在util包中 Math.ceil(12.56): 返回大于该小数的最小整数。 Math.floor(12.56): 返回小于该小数的最大整数。 Math.round(12.56): 四舍五入 Math.pow(a, b): 返回a的b次方,a和b为double
|
Random(util包中)
常用方法
折叠代码块YAML
复制代码
1 2 3
| nextInt(): 返回一个随机int值,有正有负 nextInt(num): 返回0到num(不包)的整数 nextDouble(): 返回0到1(不包)的小数
|
Date(util包中)
特点
- 月:0-11表示1-12月
- 格式化:将事物按照指定的格式进行转换
创建
折叠代码块YAML
复制代码
1 2
| Date(): 将当前日期和时间封装为对象 Date(date): 将指定毫秒值封装为对象。long型需要加l
|
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9
| getTime(): 将当前日期和时间转为毫秒值,返回long型整数 - 方便日期运算,查看两个日期间隔 setTime(date): 为Date对象设置毫秒值,可用于日期和时间转换 - 毫秒转日期对象主要是为了对年月日时分秒方便操作 after(date): 比较此日期对象是否在指定日期对象之后,返回boolean before(date): 比较此日期对象是否在指定日期对象之前,返回boolean equals(date): 比较两个日期对象是否相等,返回boolean compareTo(date): 比较此日期与指定日期对象。相等返回0,小于返回负数,大于返回正数 toString(): 显示的是当前日期字符串。Tue Aug 11 14:34:11 CST 2020
|
DateFormat(text包中)
特点
- 可以用于日期对象和日期字符串之间的转换
- 是一个抽象类,不能创建对象,但可以通过静态工厂返回实例。
- 工厂:设计模式中的词汇,表示专门用于生产对象的地方。就像单例返回对象就是静态工厂。
- windows中日期风格主要获取的是更改日期,时间或数字格式的信息
![image]()
常用字段
getDateInstance(style)和getDateTimeInstance(dateStyle, timeStyle)的参数都可以放
折叠代码块YAML
复制代码
1 2 3 4 5
| FULL: 2020年8月11日星期二 LONG: 2020年8月11日 MEDIUM: 2020年8月11日 SHORT: 2020/8/11 DEFAULT: (默认不用写)2020年8月11日
|
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6
| DateFormat getDateInstance(): 获取日期格式化实例。默认风格为年月日和当前语言。 DateFormat getDateInstance(style): 指定日期风格 DateFormat getDateTimeInstance(): 获取日期/时间格式化实例,默认风格为年月日时分秒和当前语言。 DateFormat getDateTimeInstance(dateStyle, timeStyle): 可以同时指定日期和时间风格 String format(date): 将日期对象格式化为日期字符串 Date parse(str): 将日期字符串转换为日期对象。
|
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Date date = new Date();
DateFormat df = DateFormat.getDateInstance(); String dateStr = df.format(date);
String str = "2012年4月19日"; DateFormat df = DateFormat.getDateInstance(); Date date = df.parse(str); System.out.println(date);
String str = "2012--04--19"; DateFormat df = new SimpleDateFormat("yyyy--MM--dd"); Date date = df.parse(str); System.out.println(date);
|
特点
格式化字符
输入几个字母就有几位,yyyy是四位,yy是两位
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10 11
| y: 年,1996;96 M: 月,July; Jul;07 w: 年中的周数 W: 月中的周数 D: 年中的天 d: 月中的天 H: 24小时的小时数 h: 12小时的小时数 m: 小时中的分钟数 s: 分钟中的秒数 S: 毫秒数
|
将日期转为自定义格式化字符串
折叠代码块JAVA
复制代码
1 2 3 4 5
| Date date = new Date();
DateFormat df = new SimpleDateFormat("yyyy--MM--dd"); String dateStr = df.format(date); System.out.println(dateStr);
|
Calendar(util包中)
特点
- 日历对象,用于特定瞬间与日期或时间字段时间的转换
- 是一个抽象类,
常用字段
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10 11
| DAY_OF_MONTH: 月中的第几天 DAY_OF_WEEK: 星期中的第几天。星期日是1,星期六是7 DAY_OF_YEAR: 年中的第几天 YEAR: 获取年份 MONTH: 获取月份,需要加1 DATE: 获取日期 HOUR: 获取12小时制小时 HOUR_OF_DAY: 获取24小时制小时 MINUTE: 获取分钟 SECOND: 获取秒 MILLISECOND: 获取毫秒
|
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10 11
| getInstance(): 获取日历类的实例,返回Calendar toString(): 返回创建的类名和所有日期时间字段的键值 get(key): 根据常用字段中的键获取值 set(key, value): 设置某个字段的值 set(year, month, date): 设置年月日 add(field, amount): 进行某个日期字段的偏移。几年后(前)或几小时后(前) - 获取2月有几天可以set该年3月一号,然后add回去一天,输出该天日期 setTime(date): 设置时间为Date对象,用于详细字段的操作 getTime(): 获取Calendar的Date对象 setTimeInMillis(num): 以毫秒值设置日历对象 getTimeInMillis(): 获取日历对象的毫秒值,返回长整型
|
IO流
作用
- 处理设备之间的数据传输
- Java对数据的操作都是通过流的方式
- Java操作流的对象都在
IO包中
- 流按照流向分为输入流(进行读操作)和输出流(进行写操作)。
- 输入流和输出流是相对于内存而言
- 外设的数据读取到内存就是输入
- 将内存的数据写入到外设中就是输出
- 流按操作数据分为字节流和字符流
- Java使用unicode码表,每个字符使用两个字节表示
- 因为码表不同,同一个汉字在不同的码表中对应的数字不同,所以会有乱码
- 因为码表过多,所以读取字节流之后为了正确显示数据,需要和指定码表结合找到对应字符,从而形成了字符流
- 字节流+编码表 = 字符流
IO流图解
- 有一个数据源,创建一个与该源关联的读的流
- 有一个目的源,创建一个与该源关联的写的流
- 两个流并没有关联,所以通过中转站进行数据交接。
- 读的流读到数据,把数据装载到中转站,写的流将中转站的数据写入到目的源,从而实现IO流操作
![image]()
IO流顶层基类
- 字节流顶层基类
- 字符流的顶层基类
- Reader(输入流,读入内存)
- Writer(输出流,从内存写出)
- 特点
- 基类的子类名都以父类名做后缀
- 子类名的前缀就是该对象的功能
- 注意
- 如果操作文字数据,优先考虑字符流
- 硬盘数据的基本体现是文件
- resolved:无法被识别
- catch到异常,处理不了可以进行throw操作
IO小知识
复制的原理就是边读边写。
移动或剪切的原理就是读写完成后删掉原文件
- 如果IO流操作一个文件,没有关流,该文件删除不掉。
- 做代码有两个优化
设计优化:代码重构(重新构建),让代码拥有更强的灵活性,扩展性,复用性
性能优化:最常用手段之一就是缓冲区
Writer
Writer常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10 11 12
| write(String): 写入数据。调用后数据写入到了临时存储缓冲区,相当于写入到流中 write(String, offset, length): 写入一个字符串,从offset位置开始写length个 write(char[]): 写入字符数组 write(char[], offset, length): 写入字符数组,从offset位置开始写length个 write(int c): 写入一个字符 - write使用换行可以使用System.getProperty("line.separator") - 可能写入失败,所以会抛IOException
flush(): 将临时缓冲区的数据写入到目标中 close(): 写入数据的时候调用了windows或linux的资源,如果不关闭会影响系统效率 - 可能关闭失败,所以会抛IOException - 关闭之前会先调用flush()。关闭后再write()或flush()会有IOException
|
Reader
Reader常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8
| read(): 每次调用读取单个字符,返回int。如果没有字符返回-1 - 文件有结束标识,windows或linux底层读取到该标识,告诉JVM,JVM返回前台-1 read(char[]): 将读取到的字符存储到字符数组中,返回读取到的字符个数 - 不够字符数组个数返回字符数,读取完或没有字符返回-1。 - 用于每次读取指定数量的字符。 - 多次使用该字符数组存储,会从0下标开始覆盖 - 效率更高。chs取的时候一般用1024的整数倍。可以把该数值设置为一个常量。
|
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13
|
int ch ; while ((ch = fr.read()) != -1) { System.out.println((char)ch); }
char[] chs = new char[3]; int num; while ((num = fr.read(chs)) != -1) { System.out.println(new String(chs, 0, num)); }
|
FileWriter
字符流操作对象,父类OutputStreamWriter,基类Writer
作用
特点
构造方法演示
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7
|
FileWriter("demo.txt")
FileWriter("demo.txt",boolean)
|
常用方法
参考Writer的常用方法
流操作流程
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| FileWriter fw = null; try { fw = new FileWriter("demo1.txt", true); fw.write("asdf" + System.getProperty("line.separator") + "asdfadf"); fw.flush(); } catch (IOException e) { } finally { try { if (fw != null) fw.close(); } catch (IOException e) { throw new RuntimeException("close fail"); } }
|
FileReader
字符流操作对象,父类InputStreamReader,基类Reader
作用
特点
构造方法演示
折叠代码块JAVA
复制代码
1 2 3
|
FileReader("demo.txt")
|
常用方法
参考Reader的常用方法
BufferedWriter
字符流缓冲区对象,装饰类,父类Writer
- 反复读写耗费资源。缓冲区可以提高数据的读写效率,一次读写更多内容。
- 可以自建缓冲区,也可以使用Java缓冲区对象
- 缓冲区就是对要操作的内容进行临时缓存
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10 11 12
| 构造方法: BufferedWriter(Writer out): 创建一个默认大小的输出缓冲区,缓冲传入的输出对象。 - 本质就是封装了数组的对象,用数组缓存流的数据 - 默认缓冲区8K,满了就自动刷新写出。 常用方法: write(String, offset, length): 写入字符串从offset位置的length个字符 write(char[], offset, length): 写入字符数组从offset位置的length个字符 write(int c): 写入一个字符,写到缓冲区,满了会自动flush newLine(): 写入一个换行符 flush(): 刷新并写入流。最好写一次,刷新一次。防止断电后,缓存数据全部丢失。 close(): 刷新流,然后关闭流 - 输出缓冲区关闭流的同时会关闭掉输出对象的流
|
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| FileWriter fw = null;
BufferedWriter bw = null; try { fw = new FileWriter("demo.txt"); bw = new BufferedWriter(fw); bw.write("asdf"); bw.flush(); } catch (IOException e) { } finally { if (bw != null) { try { bw.close(); } catch (IOException e) { } } }
|
BufferedReader
字符流缓冲区对象,装饰类,父类Reader
- 反复读写耗费资源。缓冲区可以提高数据的读写效率,一次读写更多内容。
- 可以自建缓冲区,也可以使用Java缓冲区对象
- 缓冲区就是对要操作的内容进行临时缓存
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9
| 构造方法: BufferedReader(Reader in): 创建一个默认大小的输入缓冲区,缓冲传入的输入对象。 - 本质就是封装了数组的对象,用数组缓存流的数据 - 可以读取数组,字符,行 - 创建对象后会默认从输入流中读取一批字符到缓冲区,用完之后再从输入流中读取。 常用方法: read(): 从缓冲区读取一个字符,返回字符int标识,没有返回-1 read(char[], offset, length): 从缓冲区读取字符到字符数组中,从offset位置存length个,返回读取的字符数。没有则返回-1 readLine(): 从缓冲区读取一行文本,不包含换行符。没有返回null
|
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7
| FileReader fr = new FileReader("demo.txt"); br = new BufferedReader(fr); String line; while ((line = br.readLine()) != null) { System.out.println(line); }
|
BufferedReader模拟实现
流程:对象创建之后,从输入流中默认读取一批字符到缓冲区,可供读取。如果没有数据了。先从输入流中再读取一批数据到缓冲区,然后继续读取,直到获取到所有数据,则在缓冲区标记-1即可。
代码如下(以装饰类实现)
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| public class MyBufferedReader extends Reader {
private Reader fr;
private static final int BUFFER_LENGTH = 1024;
private char[] buf = new char[BUFFER_LENGTH];
private int pos = 0;
private int count = 0; public MyBufferedReader(Reader fr) throws IOException { this.fr = fr; }
public int read() throws IOException { if (count == 0) { writeDataToBuffer(); } if (count == -1) { return -1; } char ch = buf[pos++]; count--; return ch; }
public String readLine() throws IOException { StringBuilder sb = new StringBuilder(); int ch = 0; while ((ch = read()) != -1) { if (ch == '\r') { continue; } if (ch == '\n') { return sb.toString(); } sb.append((char)ch); } if (sb.length() != 0) { return sb.toString(); } return null; }
public void close() throws IOException { fr.close(); }
private void writeDataToBuffer() throws IOException { count = fr.read(buf); pos = 0; } }
|
缓冲区评价
缓冲区对IO流中的很多类都进行了功能增强(也可以叫做装饰设计模式)
装饰设计模式
对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。并未改变其功能
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class PersonDemo { public static void main(String[] args) { Person p = new Person(); NewPerson np = new NewPerson(p); np.chiFan(); } } class Person { void chiFan() { System.out.println("吃饭"); } }
class NewPerson { private Person p; NewPerson(Person p) { this.p = p; }
public void chiFan() { System.out.println("开胃酒"); p.chiFan(); System.out.println("甜点"); } }
|
装饰和继承都可以实现功能的扩展增强(对于IO流就是缓冲实现),但
- 通过继承来实现功能扩展增强,不断地产生子类,虽然扩展了功能,但继承体系会越来越臃肿,不够灵活
- 通过将扩展功能单独封装(装饰),哪个对象需要该功能就和哪个对象关联,扩展了功能,也更灵活
- 装饰类和被装饰类必须属于同一个接口或父类
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
class BufferWriter extends Writer { private Writer w; BufferWriter(Writer w) { this.w = w; } }
|
不要随便修改原来文件中的代码,因为很可能会修改多处,造成问题。通过继承或装饰来扩展功能
LineNumberReader
BufferedReader子类。这个只有Reader,没有Writer,因为写入的时候就顺序写入
作用
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8
| 构造方法: LineNumberReader(fr): 创建一个行号阅读器。传入了输入流对象 常用方法: readLine(): 读取缓冲区一行文字 read(): 从缓冲区读取一个字符,返回字符int标识,没有返回-1 read(char[], offset, length): 从缓冲区读取字符到字符数组中,从offset位置存length个,返回读取的字符数。没有则返回-1 getLineNumber(): 获取当前行号。例如读取代码的行号。 setLineNumber(int): 设置当前行号,下一个获取getLineNumber()从该行号下一个开始获取
|
FileOutputStream
字节流操作对象,与字符操作对象思想相同。OutputStream的子类
可以操作字符,也可以操作其他媒体文件
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10 11 12
| 构造方法: FileOutputStream("demo.txt"): 创建文件输出流对象(字节流对象) - 可以直接写绝对路径 - 不需要编解码,数据不需要临时缓冲,会直接写到目的源中 - OutputStream中flush()和close()方法并没有写具体代码,该子类只重写了close()。所以它调用close()才有用 - 缓冲区需要flush()的重写 常用方法: write(byte[]): 写入字节数组 write(byte[], offset, length): 从offset写入length长度的字节数组 write(int): 写入字节 - write后数据被直接写入目的源中,不需要flush() close(): 关闭资源
|
字节流操作对象,与字符操作对象思想相同。InputStream的子类
可以操作字符,也可以操作其他媒体文件
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9
| 构造方法: FileInputStream("demo.txt"): 创建文件输入流对象(字节流对象) - 可以直接写绝对路径 常用方法: read(): 读取一个字节的数据 read(byte[]): 读取一个字节数组 read(byte[], offset, length): 读取一个字节数组。从offset位置存length字节 close(): 关闭流 available(): 获取输入流还可以读取的剩余字节数。用于获取文件大小,也可以用于分段获取字节数据。
|
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| FileInputStream fis = null; try { fis = new FileInputStream("byteTest.txt"); byte[] buf = new byte[1024]; int length = 0; while ((length = fis.read(buf)) != -1) { System.out.println(new String(buf, 0, length)); } } catch (IOException e) { } finally { try { fis.close(); } catch (IOException e) { } }
FileInputStream fis = new FileInputStream("byteTest.txt");
byte[] buf = new byte[fis.available()];
fis.read(buf);
|
BufferedOutputStream
字节流缓冲区对象,用于字节流的缓冲,父类FilterOutputStream,基类OutputStream
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8
| 构造方法: BufferedOutputStream(OutputStream out): 创建一个默认大小的字节输出缓冲区,缓冲传入字节输出对象。 - 本质就是封装了数组的对象,用数组缓存流的数据 常用数据: write(byte[], offset, length): 从offset写入length长度的字节数组 write(int): 写入字节,缓冲区满了自动flush。不满想要刷新就手动flush flush(): 刷新缓冲流,将数据写入目的源 close(): 关闭资源
|
字节流缓冲区对象,用于字节流的缓冲,父类FilterInputStream,基类InputStream
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8
| 构造方法: BufferedInputStream(InputStream out): 创建一个默认大小的字节输入缓冲区,缓冲传入字节输入对象。 - 本质就是封装了数组的对象,用数组缓存流的数据 常用数据: read(): 读取一个字节的数据。检测到流的末尾前或抛出异常前,一直阻塞 read(byte[], offset, length): 读取一个字节数组。从offset位置存length字节 close(): 关闭流 available(): 获取输入流还可以读取的剩余字节数。用于获取文件大小,也可以用于分段获取字节数据。
|
实现复制
这里使用字节流复制。用字符流来复制,得到一个字符会去查表。
如果查表找不到对应数据,就使用未知字符区的数据来标识,源数据和目的数据将不一致。很可能最后大小也不同。
使用自建缓冲区复制
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\刀马旦.mp3"); fos = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\刀马旦_1.mp3"); byte[] buf = new byte[BUFFER_SIZE]; int length = 0; while ((length = fis.read(buf)) != -1) { fos.write(buf, 0, length); } } catch (IOException e) { } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { } } if (fis != null) { try { fis.close(); } catch (IOException e) { } } }
|
加了Java缓冲区再复制文件
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| BufferedInputStream bis = null; BufferedOutputStream bos = null; try { FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\刀马旦.mp3"); bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\刀马旦_1.mp3"); bos = new BufferedOutputStream(fos); int ch = 0; while ((ch = bis.read()) != -1) { bos.write(ch); } } catch (IOException e) { } finally { if (bos != null) { try { bos.close(); } catch (IOException e) { } } if (bis != null) { try { bis.close(); } catch (IOException e) { } } }
|
直接读取所有字节,然后写入另一个文件
不建议使用,可能内存溢出
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\刀马旦.mp3"); fos = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\刀马旦_3.mp3"); byte[] buf = new byte[fis.available()]; fis.read(buf); fos.write(buf); } catch (IOException e) { } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { } } if (fos != null) { try { fos.close(); } catch (IOException e) { } } }
|
读一个字节,写一个字节
很慢,千万不要用
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\刀马旦.mp3"); fos = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\刀马旦_3.mp3"); int ch = 0; while ((ch = fis.read()) != -1) { fos.write(ch); } } catch (IOException e) { } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { } } if (fos != null) { try { fos.close(); } catch (IOException e) { } } }
|
读取键盘数据并打印
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9
|
InputStream is = System.in;
int ch = is.read(); System.out.println(ch);
|
字节流通向字符流的桥梁,叫做转换流,解码
特点
- 本身是字符流
- 使用指定
charset读取字节,并将其解析为字符
- 直接使用
FileReader读使用系统默认码表,转换之后使用转换后的码表读,然后使用FileInputStream
字节流+编码表组成的对象就是InputStreamReader
构造函数
折叠代码块YAML
复制代码
1 2
| InputStreamReader(InputStream out): 创建一个字节流转字符流的对象。传入的是字节输入流对象 InputStreamReader(InputStream out, String charset): 以某种编码将字节输入流解码为字符,大小写均可以(gbk或GBK)
|
读取标准输入流转为字符流输出
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| InputStream in = System.in; InputStreamReader isr = new InputStreamReader(in); BufferedReader br = new BufferedReader(isr);
String line = null; try { while ((line = br.readLine()) != null) { if ("over".equals(line)) break; System.out.println(line.toUpperCase()); } } catch (IOException e) { }
|
字节流通过InputStreamReader转为字符流之后,每次调用read()读取中文会读取一个字符,也就是两个字节
OutputStreamWriter
字符流通向字节流的桥梁,叫做转换流,编码
特点
- 本身是字符流,但可以输出字节流
- 使用指定
charset将写入流的字符编码为字节。默认使用操作系统的编码格式(windows简体中文默认GBK)
- 直接使用
FileWriter写使用系统默认码表,转换之后使用转换后的码表写,然后使用FileOutputStream
- 字节到字符是解码(看得懂),字符到字节是编码(看不懂)
构造函数
折叠代码块YAML
复制代码
1 2 3
| OutputStreamWriter(OutputStream out): 创建一个字符流转字节流的对象。传入的是字节输出流对象 OutputStreamWriter(OutputStream out, String charset): 以某种编码将字符输出流编码为字节,大小写均可以(gbk或GBK) - 不同编码保存后,文件大小不同。
|
标准输入流获取数据,标准输出流输出数据
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| InputStream in = System.in;
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
OutputStream os = System.out;
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw); String line = null; while ((line = br.readLine()) != null) { if ("over".equals(line)) { break; } bw.write(line.toUpperCase()); bw.newLine(); bw.flush(); }
|
输入流与输出流的简化
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); String line = null; while ((line = br.readLine()) != null) { if ("over".equals(line)) { break; } bw.write(line.toUpperCase()); bw.newLine(); bw.flush(); }
|
将控制台录入字符保存到文件中
折叠代码块JAVA
复制代码
1 2 3
|
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("demo.txt")));
|
输入流与输出流的简化
将文件中的数据显示到控制台
折叠代码块JAVA
复制代码
1 2 3
|
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("demo.txt")));")));
|
输入流与输出流的简化
文本复制
折叠代码块JAVA
复制代码
1 2 3 4 5
| BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("demo.txt")));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("demoCopy.txt")));
|
输入流与输出流的简化
转换流的使用场景
- 源或目的对应设备是字节流,但操作的是文本数据,可以作为转换桥梁,提高文本操作便捷性
- 操作文本涉及到编码表,需要使用转换流。
常识
Unicode所有字符都用两个字节表示。a也是两个字节。所以比较浪费空间。后期改进为UTF-8,一个字节装不下用两个,两个字节装不下用三个。UTF-8有编码头,所以可能两个字节不够。
- 中文UTF-8是三个字节,GBK是两个字节。
- 流写入默认使用系统默认编码表写入
- 如果读入读出需要指定明确编码,需要使用转换流
流的操作规律
可以更好的进行流对象的创建
- 流太多,开发时不知道用哪个对象合适。所以需要总结一下
- 使用四个明确来获得使用对象。
- 明确是否需要源和目的(源和目的统称为汇)
折叠代码块YAML
复制代码
1 2
| 源: InputStream或Reader两个体系 目的: OutputStream或Writer两个体系
|
- 明确数据是否纯文本数据
折叠代码块YAML
复制代码
1 2
| 源: 是纯文本,使用Reader;不是纯文本,使用InputStream 目的: 是纯文本,使用Writer;不是纯文本,使用OutputStream
|
- 明确具体设备
折叠代码块YAML
复制代码
1 2
| 源设备相关对象: 硬盘,File;键盘,System.in;内存,数组;网络,Socket流; 目的设备相关对象: 硬盘,File;控制台,System.out;内存,数组;网络,Socket流;
|
- 是否需要其他额外功能(装饰)
折叠代码块YAML
复制代码
1 2 3
| 是否需要高效(缓冲区),是就加上buffer。 是否需要操作基本数据类型 是否需要对象存储化
|
- 流只能操作设备数据,文件夹,文件属性(创建属性,只读等),文件名等需要使用File对象操作
常用流汇总
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
|
File
特点
- 将文件或文件夹封装成对象
- 方便操作文件或文件夹的属性信息
- 可以作为参数传递给流的构造函数
- 最好先将所有数据存储,最后一起进行IO操作,降低资源使用。
构造函数
折叠代码块YAML
复制代码
1 2 3
| File("a.txt"): 可以将一个已存在的或不存在的文件或目录封装为对象。传入文件或目录路径。 File("c:\\", "a.txt"): 创建一个文件对象,传入路径和文件名,表示在使用该路径下的该文件或在该路径下创建该文件 File(File, "a.txt"): 创建一个文件对象,传入File对象和文件名
|
折叠代码块JAVA
复制代码
1 2
| File file_3 = new File(new File("c:\\"), "a.txt");
|
常用字段
折叠代码块YAML
复制代码
1 2 3 4 5
| pathSeparatorChar: 多个路径分隔符。windows(;) linux(:) - 就是环境变量中路径之间的分隔符号 pathSeparator: 多个路径分隔符。windows(;) linux(:) separator: 路径分隔符。windows(\\) linux(/) separatorChar: 路径分隔符。windows(\\) linux(/)
|
常用方法
can和is开头的都是用来判断的方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 获取: getName(): 获取文件名称(demo.txt) getAbsolutePath(): 获取文件绝对路径 getPath(): 获取File对象传入的路径 length(): 获取文件大小,返回长整型 lastModified(): 获取文件修改时间。用于自动更新已修改的文件。可以开一个线程来操作。 getParent(): 获取文件父目录。如果传入相对路径则返回null,绝对路径可以获取父目录 File getAbsoluteFile() 将获取到的文件绝对路径封装为File对象 创建与删除: createNewFile(): 创建一个新文件。返回创建结果。路径已被封装到File对象中,不需要传路径。 - 文件存在就创建,文件不存在就不创建 createTempFile(prefix, suffix): 创建临时文件到默认临时文件夹中,需要指定前缀和后缀。是静态方法 createTempFile(prefix, suffix, directory): 创建临时文件到指定目录中 delete(): 删除File封装好的文件或文件夹。如果文件夹内有文件或文件夹或该文件或文件夹正在使用,则不会被删除 - 慎用,删除就没了。 - 删除创建的多级文件夹,只会删除最内层的文件夹。 deleteOnExit(): 在虚拟机结束的时候删除该文件。如果文件出现IOException,则请求虚拟机在退出前删除文件 mkdir(): 创建一个单级目录 mkdirs(): 创建多级目录 判断: exists(): 判断文件(夹)是否存在。在操作文件前判断一下 isAbsolute(): 判断是否是绝对路径名 isFile(): 判断是否是文件。不存在返回false。最好先判断存在性。文件也可以没有后缀 isDirectory(): 判断是否是目录。不存在返回false。最好先判断存在性。文件夹也可以是demo.txt isHidden(): 判断是否被隐藏。不存在返回false。最好先判断存在性 重命名: renameTo(File): 将一个File对象重命名为另一个File对象。也可以将文件重命名到另一个目录,相当于剪切。 获取所有分区及空间信息: listRoots(): 列出所有文件系统根目录,返回File数组 getFreeSpace(): 获取某个根目录的空闲空间大小 getTotalSpace(): 获取某个根目录的总空间大小 getUsableSpace(): 获取某个根目录的可用于虚拟机的空间大小 获取某个目录下所有文件和文件夹名称: list(): 获取某个目录下所有文件和文件夹名称,包括系统隐藏文件名称。返回字符串数组 - System Volume Information系统卷标信息,无法访问,有权限。 - 如果传入的是文件路径,则数组创建失败,空指针异常 - 系统级目录访问也是返回空。 - 目录存在,但没有内容,返回数组长度为0 list(new FilterByJava): 获取某个目录下符合过滤器的所有文件和文件夹名称,返回String数组 listFiles(): 获取某个目录下所有文件或文件夹的对象,返回File数组 listFiles(FileFilter): 获取某个目录下符合过滤器所有文件或文件夹的对象,返回File数组 listFiles(FilenameFilter): 获取某个目录下符合过滤文件名的所有文件或文件夹的对象,返回File数组
|
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| File f = new File("/home/easul/test/test2/test23"); f.mkdirs();
public class FilterByJava implements FilenameFilter { @Override public boolean accept(File dir, String name) { return name.endsWith(".java"); }
}
public class SuffixFilter implements FilenameFilter { private String suffix; @Override public boolean accept(File dir, String name) { return name.endsWith(suffix); }
public SuffixFilter(String suffix) { this.suffix = suffix; } }
public class FilterByHidden implements FileFilter { @Override public boolean accept(File pathname) { return pathname.isHidden(); } }
|
递归遍历文件夹(深度遍历)
递归太多会栈溢出
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public void showAllFile() { File dir = new File("/home/easul/test"); getAllFile(dir, 0); }
public void getAllFile(File dir, int count) { System.out.println(getSpace(count) + "" + dir.getName()); File[] files = dir.listFiles(); for (File file : files) { if (file.isDirectory()) { getAllFile(file, count + 1); } else { System.out.println(getSpace(count + 1) + "" + file.getName()); } } }
public String getSpace(int count) { StringBuilder sb = new StringBuilder(); sb.append("|--"); for (int i = 0; i < count; i++) { sb.insert(0, "| "); } return sb.toString(); }
|
递归删除目录下所有文件和文件夹
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void doDelete() { File dir = new File("C:\\src"); deleteAllFile(dir); System.out.println("success"); }
public void deleteAllFile(File dir) { File[] files = dir.listFiles(); for (File file : files) { if (file.isDirectory()) { deleteAllFile(file); } else { file.delete(); } } dir.delete(); }
|
Properties
父类Hashtable,基类Map
特点
- 键值都是String类型,所以不用加泛型
- 最好使用自身的存储和取出方法来进行元素操作。
- 该集合的数据可以保存到流,或者从流获取
- 通常用于操作以键值对存在的配置文件,xml也可以
- 持久化:把数据存到硬盘,数据可以一直存在。
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8
| setProperty(key, value): 设置键值 getProperty(key): 获取键的值 stringPropertyNames(): 获取某个Properties的所有键值对的Set集 list(PrintStream): 将所有属性信息输出。用于调试。 store(OutputStream, comments): 将属性写到输出流,同时需要进行属性描述。如果写中文,会输出成\u523D,转为了Unicode码 store(Writer, comments): 将属性写入字符流。如果写中文,会输出成\u523D,转为了Unicode码 load(InputStream): 将输入流的属性读入 load(Reader): 将输入字符流的属性读入
|
简单演示
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| Properties prop = new Properties();
prop.setProperty("name", "easul"); prop.setProperty("age", "12");
prop.setProperty("age", "24");
Set<String> names = prop.stringPropertyNames(); for (String name : names) { String value = prop.getProperty(name); System.out.println(name + ":" + value); }
prop.list(System.out);
prop.load(new FileReader("test.txt"));
prop.store(new FileWriter("test.txt"), "comment");
|
修改已有配置信息
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| File file = new File("prop.ini"); if (!file.exists()) { file.createNewFile(); } Properties prop = new Properties(); FileReader fr = new FileReader(file); prop.load(fr); prop.list(System.out);
prop.setProperty("name", "aboive");
FileWriter fw = new FileWriter(file); prop.store(fw, "");
fw.close(); fr.close();
|
IO流常用对象
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 打印流(用于目的): - PrintWriter - PrintStream 序列流(用于源): - SequenceInputStream 操作对象的流: - ObjectInputStream - ObjectOutputStream 管道流: 输入流和输出流可以直接连接 - PipedInputStream - PipedOutputStream 操作基本数据类型流: - DataInputStream - DataOutputStream 操作字节数组: 源和目的都是内存,都是字节数组 - ByteArrayInputStream - ByteArrayOutputStream 操作字符数组: 源和目的都是内存,都是字符数组 - CharArrayInputStream - CharArrayOutputStream 操作字符串: 源和目的都是内存,都是字符串 - StringReader - StringWriter 其他: - RandomAccessFile
|
PrintStream
特点
- 不会抛出IOException
- 可以打印多种数据类型值,并保持数据表示形式
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8
| 构造方法: PrintStream(path): 可以直接将流打印到路径文件中 PrintStream(OutputStream): 将数据打印到另一个输出流中 PrintStream(File): 将数据打印到File对象指定的文件中 常用方法: write(): 将数据写出。如果写出int,只写出int的低8位,高24位被忽略 print(): 参数是什么样,打印出的是什么样。底层就是将数据转为字符串打印出去。省得打印字节数组太麻烦。 println(): 参数是什么样,打印出的是什么样。打印完顺便换行。
|
PrintWriter
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10 11 12
| 构造方法: PrintWriter(path): 可以直接将字符流打印到路径文件中 PrintWriter(OutputStream): 将字符流打印到另一个输出流中 PrintWriter(OutputStream, true): 自动刷新,不需要加flush() PrintWriter(File): 将字符流打印到File对象指定的文件中 PrintWriter(Writer): 将字符流打印到另一个Writer对象中 PrintWriter(Writer, true): 自动刷新,不需要加flush() 常用方法: write(): 将数据写出。如果写出int,只写出int的低8位,高24位被忽略 print(): 参数是什么样,打印出的是什么样。底层就是将数据转为字符串打印出去。省得打印字节数组太麻烦。 println(): 参数是什么样,打印出的是什么样。打印完顺便换行。 flush(): 打印之后会打印到缓冲区,需要刷新才能显示。一般缓冲区满了会自动写出,缓冲区不满想写出需要flush
|
特点
- 可以依次读取多个流,直到最后一个流
- 相当于多个流合并为一个
- 可用于文件合并(例如文件分割后再进行合并)
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8
| 构造方法: SequenceInputStream(Enumeration<? extends InputStream> e): 合并多个流 SequenceInputStream(InputStream s1, InputStream s2): 合并两个流 常用方法: read(): 读取一个字节的数据。检测到流的末尾前或抛出异常前,一直阻塞 read(byte[], offset, length): 读取一个字节数组。从offset位置存length字节 close(): 关闭流。因为可能合并多个流,所以会关闭所有被合并的流 available(): 获取输入流还可以读取的剩余字节数。用于获取文件大小,也可以用于分段获取字节数据。
|
简单操作
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| Vector<FileInputStream> v = new Vector<FileInputStream>(); v.add(new FileInputStream("1.txt")); v.add(new FileInputStream("2.txt")); v.add(new FileInputStream("3.txt")); Enumeration<FileInputStream> en = v.elements(); SequenceInputStream sis = new SequenceInputStream(en); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("4.txt")); int len = 0; while ((len = sis.read()) != -1) { bos.write(len); } bos.close(); sis.close();
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>(); for (int i = 1; i <= 3; i++) { al.add(new FileInputStream(i + ".txt")); }
Enumeration<FileInputStream> en = Collections.enumeration(al);
SequenceInputStream sis = new SequenceInputStream(en);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("4.txt")); int len = 0; while ((len = sis.read()) != -1) { bos.write(len); } bos.close(); sis.close();
|
文件切割器
- 将文件切割为文件碎片(固定每份大小或固定总份数)
- 文件碎片的扩展名可以自己进行定义,例如:part
- 文件相关信息需要保存到配置文件中
- 流要关联文件,所以有几个文件,就需要几个流
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| FileInputStream fis = new FileInputStream(file); byte[] buf = new byte[BUFFER_SIZE * BUFFER_SIZE];
FileOutputStream fos = null;
int len = 0;
int count = 0;
Properties prop = new Properties();
File dir = new File("C:\\partfiles"); if (!dir.exists()) { dir.mkdirs(); } while ((len = fis.read(buf)) != -1) { fos = new FileOutputStream(new File(dir, (count++) + ".part")); fos.write(buf, 0, len); fos.close(); }
OutputStreamWriter propWriter = new OutputStreamWriter(new FileOutputStream(new File(dir, "part.properties")), "UTF-8"); prop.setProperty("partcount", count + ""); prop.setProperty("filename", file.getName()); prop.store(propWriter, "file info");
propWriter.close(); fis.close();
|
文件合并器
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| Properties prop = new Properties();
File partProp = new File(dir, "part.properties"); if (!partProp.exists()) { throw new RuntimeException(dir + "目录下未找到配置文件"); } else { prop.load(new InputStreamReader(new FileInputStream(partProp), "UTF-8")); } String filename = prop.getProperty("filename"); int partCount = Integer.parseInt(prop.getProperty("partcount")); File[] partFiles = dir.listFiles(new MyJavaFilter(".part")); if (partCount != partFiles.length) { throw new RuntimeException("碎片文件数目不符合要求,应为" + partCount +"个"); } ArrayList<FileInputStream> al = new ArrayList<FileInputStream>(); for (int i = 0; i < partCount; i++) { al.add(new FileInputStream(new File(dir, i + ".part"))); } Enumeration<FileInputStream> en = Collections.enumeration(al); SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream(new File(dir, filename)); byte[] buf = new byte[1024]; int len = 0; while ((len = sis.read(buf)) != -1) { fos.write(buf, 0, len); } fos.close(); sis.close();
|
特点
- 直接把对象读入
- 使用普通流对象,可以读入输入,但无法解析为对象。
- 只获得对象持久化文件,不能够将对象还原。因为对象在堆内存创建都需要依赖该对象所属类文件(字节码文件),没有无法创建。因此还需要获得该类文件
- 会对
ObjectOutputStream写入的基本数据和对象进行反序列化(读出来,序列化是写进去)
读入持久化对象(反序列化)
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11
| File file = new File("obj.object"); FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis);
Worker p = (Worker)ois.readObject(); System.out.println(p.getName()); System.out.println(p.getAge()); ois.close();
|
ObjectOutputStream
特点
- 直接把对象保存出去。实现对象的持久存储
- 写出多个对象需要序列化。
折叠代码块
复制代码
1 2 3 4
| Serializable是标记接口。没有实现该接口,类不能被序列化 序列化后会写出对象的类,类签名(底层实现),非瞬态和非静态字段的值。 对象修改了静态字段,因为其不被持久化存储,所以再反序列化的时候还是静态属性默认值。 可以写多个
|
- 可以将各种类型的数据包括对象写到文件中
- 用于数据库链接对象,存储特定数据的对象
对象持久化(序列化)
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7
| File file = new File("obj.object"); FileOutputStream fos = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(new Worker("easul", 24)); oos.close();
|
Serializable
- 实现了该类,就有ID号,相当于序列化了。
- 默认ID号是根据类的特征和类的签名算出来的
- 用于验证持久化的对象文件和类文件的ID版本是否一致
- 如果持久化了老对象的类,后边对该类进行了更新,反序列化会抛出异常InvalidClassException(无效类文件异常)
- 网络传输需要加上该ID
- 网络传输有些对象需要序列化。服务器如果崩了,会话会消失,所以对象会存在服务器。重启服务器,会恢复会话。数据还在。
声明方式
折叠代码块JAVA
复制代码
1 2 3
| class Test implements Serializable { private static final long serialVersionUID = 8823927758172226839L; }
|
关键字transient
这是短暂的,瞬态的
如果对象的某个字段不想存储到硬盘,也不想用静态,加transient关键字即可(ObjectOutputStream不存储瞬态字段的值)。该对象中的值还在。
折叠代码块JAVA
复制代码
1
| private transient String name;
|
RandomAccessFile
特点
- 不是IO体系(字符流或字节流)中的子类
- 支持对随机访问文件的读取或写入
- 对象内部维护了一个byte数组,可以通过指针操作数组中的元素。
- 数组可以延长
getFilePointer()可以获取指针位置,seek()可以设置指针位置。
- 该对象将
字节输入流和输出流进行了封装
- 该对象的
源或目的只能是文件(构造函数的特点)
random就是指针可以移动,想从哪里读都可以。
常用方法
折叠代码块YAML
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| 构造方法: RandomAccessFile(File file, String mode): 创建一个随机访问流。mode有r,rw,rws,rwd。 - 文件不存在,创建,文件存在,不创建 RandomAccessFile(String file, String mode): 文件使用字符串指定路径 常用方法: close(): 用于关资源 seek(long): 设置指针偏移量。移动一次走8位。用于实现随机位置读取数据或写入数据。 - 可用于追加数据或修改数据 getFilePointer(): 获取指针位置,返回长整型 write(): 按照字节写入。int只写入低八位 writeInt(int): 按照32位写入。 read(byte[]): 将数据读入字节缓冲区 readInt(): 一次读32位,并转为整数返回
|
注意
- 一般要求数据有规律。这样指针才能每次获取固定长的数据。
- 可以默认创建16字节,多出来的用0补。每次读取16字节的数据即可。
- 可以用于多线程写入,同时向一个文件写数据。指定好指针位置即可。不同的数据段属于不同线程,但是属于同一个文件。
- 可以用于文本文件,也可以用于媒体文件。
特点
- 输入输出流可以直接连接,通过结合线程使用
- 可以单线程写完就读,也可以读完就写。但是读的时候没有数据,读阻塞,写也无法执行。造成死锁
- 可以多线程同时读写(要使用多线程避免死锁)
- 管道输出流提供写到管道输入流的所有数据
常用方法
折叠代码块YAML
复制代码
1 2 3 4
| 构造方法: PipedInputStream(PipedOutputStream src): 创建管道输入流的同时就连接上一个管道输出流。 常用方法: connect(PipedOutputStream src): 连接一个管道输出流
|
PipedOutputStream
特点
- 输入输出流可以直接连接,通过结合线程使用
- 可以单线程写完就读,也可以读完就写。但是读的时候没有数据,读阻塞,写也无法执行。造成死锁
- 可以多线程同时读写(要使用多线程避免死锁)
- 管道输出流提供写到管道输入流的所有数据
管道流的使用
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public void pipedDemo() throws IOException { PipedInputStream input = new PipedInputStream(); PipedOutputStream output = new PipedOutputStream(); input.connect(output); new Thread(new Input(input)).start(); new Thread(new Output(output)).start(); }
class Input implements Runnable { private PipedInputStream in; @Override public void run() { try { byte[] buf = new byte[1024]; int len = in.read(buf); String s = new String(buf, 0, len); System.out.println("s=" + s); in.close(); } catch (Exception e) { } } public Input(PipedInputStream in) { this.in = in; } } class Output implements Runnable { private PipedOutputStream out; @Override public void run() { try { out.write("管道来了".getBytes()); } catch (Exception e) { } } public Output(PipedOutputStream out) { this.out = out; } }
|
可以将基本Java数据类型的数据读入
常用方法
折叠代码块YAML
复制代码
1
| readUTF(): 读入使用修改版UTF-8写出的数据。每一个修改版UTF-8标头读一次,返回String
|
DataOutputStream
可以将基本Java数据类型写入输出流
常用方法
折叠代码块YAML
复制代码
1 2
| writeUTF(String): 使用修改版UTF-8将数据写入输出流。存储的时候会带着编码的标头。 - 其和普通UTF-8不同。转换流无法指定修改版UTF-8,所以转换流无法读该文件
|
特点
- 实现了输入流
- 在内存操作
- 有内部缓冲区
- 该流关闭无效
- 构造的源是数组
构造方法
折叠代码块YAML
复制代码
1
| ByteArrayInputStream("abcde".getBytes()): 构造的时候传一个字符数组
|
ByteArrayOutputStream
特点
- 实现了输出流
- 在内存操作
- 缓冲区可以自增长
- 该流关闭无效。因为没有调用底层资源,只在内存操作数据。所以不用关闭。关闭了也还能使用。
- 不抛出IOException
常用方法
折叠代码块YAML
复制代码
1 2 3 4
| 构造方法: ByteArrayOutputStream(): 构造的时候传已经有了数组。不够会自动增长数组长度 常用方法: writeTo(OutputStream): 将数据写到另一个流
|
简单操作
用流的思想操作数组,这两个对象操作数据量一般不大
折叠代码块JAVA
复制代码
1 2 3 4 5 6
| ByteArrayInputStream bais = new ByteArrayInputStream("abcde".getBytes()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int ch = 0; while((ch = bais.read()) != -1) { baos.write(ch); }
|
编码表
- 二进制和生活中文字的关系
- 用于编码解码。
- 常用码表
- ASCII:美国标准信息交换码。7位即可表示完。一个字节的开头位0
- ISO8859-1:拉丁码表。欧洲码表。一个字节的8位都用上了。兼容ASCII
- GB2312:中国的中文编码表。
- GBK:中文编码表的升级版,有了更多的文字
- Unicode:国际标准码,融合了多种文字。所有文字都是两个字节,Java默认使用。
- UTF-8:Unicode Transfer Format,最低8位,一个字节。最多3个字节
- 字符串->字节数组:编码,字节数组->字符串:解码
编码表与String
String关于编码解码的方法
折叠代码块YAML
复制代码
1 2
| String(buf, "UTF-8"): 构造函数中指定编码来解析字节数组 getBytes(charset): 指定字符串转为字节数组的时候的编码表。GBK,UTF-8等
|
字符串编码的使用
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7
| String str = "你好"; byte[] buf = str.getBytes("UTF-8"); for (byte b : buf) { System.out.println(b); } String s1 = new String(buf, "UTF-8"); System.out.println(s1);
|
编码问题
- 编码编错了,无法解码(例如使用不识别中文的码表)。
- 编对了,使用码表不对,也可能解错(例如tomcat默认使用iso8859-1,就会解错)
- 可以以该码表将字符串编码,然后再使用正确的码表解码
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7
| String str = "你好"; byte[] buf = str.getBytes("GBK"); String s1 = new String(buf, "iso8859-1"); System.out.println(s1); byte[] buf2 = s1.getBytes("iso8859-1"); String s2 = new String(buf2, "GBK"); System.out.println(s2);
|
- 编码正确,解码不对,可能无法再挽回
- 使用GBK编码,UTF-8解码,再UTF-8编码,GBK解码
- 测试使用
你好 ,哈哈 ,谢谢。前两个都不能找到对应编码,第三个可以,所以第三个可以进行重新编码,前两个不可以。
- GBK编码的数据,如果在UTF-8解码的时候找不到对应的字符,就使用未知字符代替。未知字符可能使用同一个码代替,所以再使用UTF-8编码可能无法再找回源编写的编码
- UTF-8编码解析
- 一个字节,最高位为0,剩下的7位表示数据
- 两个字节,第一个字节开头是110,剩下5位数据位,第二个字节开头是10,剩下6位数据位
- 三个字节,第一个字节开头是1110,剩下四位数据位,第二个和第三个字节开头是10,都剩下6位数据位
- 读完符合的字节,然后直接用读到的字节查表
记事本写入联通两个字乱码
折叠代码块JAVA
复制代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| String s1 = "移动"; String s2 = "联通"; byte[] b1 = s1.getBytes("GBK"); byte[] b2 = s2.getBytes("GBK"); for (byte b : b1) {
System.out.println(Integer.toBinaryString(b & 255)); } System.out.println(); for (byte b : b2) {
System.out.println(Integer.toBinaryString(b & 255)); }
|
v1.5.2