一、流的分类
- 流根据数据流向分为输入流和输出流
- 输入输出是相对于Java程序来说的,如果是读取Java程序外部文件或资源,就是输入流,如果是写入外部文件或资源则就是输出流
- 流根据操作的数据类型分为字符流(char)和字节流 (byte)
- 字符流是用来处理字符,比如汉字,粒度比字节流粗,一个字符是2个字节,字节流是用来处理字节,粒度比较细
- 如果使用字节流去处理字符,如果处理的字节长度不是2的整数倍,就会出现乱码的情况
- 所以不建议使用字符流去处理二进制文件,比如视频,音频,excel等,可能会造成文件损坏无法打开
二、各个流之间的关系
三、文件流(FileInputStream、FileOutputStream、FileWriter、FileReader)
- File流根据字符和字节分别分为 FileWriter、FileReader和FileInputStream、FileOutputStream
- File流根据输入和输出分别分为 FileReader、FileOutputStream和FileWriter、FileInputStream
- FileXXX流是针对文件的处理,总的来说,必须操作一个已存在的实体文件,在操作时,如果指定的路径文件不存在,则会自动创建一个文件
四、节点流和处理流
- 节点流就是实现了InputStream、OutputStream、Writer、Reader的流,BufferedInputStream、BufferedOutStream、BufferedReader、BufferedWriter除外
- BufferedXXX的流是对其他流的包装,增强了其他流,所以也可以称其为包装流,观察BufferedXXX流的内部,可以看到BufferedXXX流注入了该类流的顶级接口,意味着,它可以包装任何一个实现了该接口的流
- 所以在使用BufferedXXX流时,需要在构造方法中注入合适的具体流
- BufferedXXX流在关闭时,只需要关闭自己就可以了,程序内部会关闭实际注入的流
- 在使用非BufferXXX的流时,遍历读取一个资源的全部内容是通过while来判断当前读取的字符或者字节的返回值是否等于-1,但当使用BufferXXX流时,只需要判断返回值是否为空即可,BufferXXX流的封装使API更符合人性化
- 在写入文件时,如果需要换行,BufferXXX流只需要
.newLine()
即可,该方法会针对不同的系统,给予不同的换行符 - 流在关闭时应该遵循先开先关原则,最先开启的,最先关闭
五、对象流(ObjectoutputStream 、ObjectInputStream )
- 如果在保存数据时,不仅要保存数据的值,还要保存数据的类型,恢复数据,也要恢复数据的值和类型时,就需要考虑到对象的序列化,而对象流提供了对基本类型或对象类型的序列化和反序列化的功能
- 序列化和反序列化要求该对象必须实现Serializable接口或者Externalizable接口(Externalizable接口也是实现了Serializable,但是实现Externalizable会必须要求实现其自带的两个方法,所以一般不推荐)
- 对象流也是一个处理流,所以也需要注入一个具体的实现类
- ObjectoutputStream 提供 序列化 功能
- ObjectInputStream 提供 反序列化 功能
- 序列化后的数据保存的文件格式, 不是文本格式,而是按照它自己的格式
- 如果数据存在多个对象或者嵌套对象,要确保每个对象都要实现Serializable接口
- 在使用ObjectInputStream 反序列化数据时,一定要按照 当初数据序列化的顺序反序列化,否则会抛出异常
- 序列化的类中建议添加SerialVersionUID, 提高序列化兼容性,当初在SerialVersionUID时,后期对类新增属性时,程序会认为该类是之前类版本的升级,不是一个全新的类
- 序列化对象时,默认将对象里面的所有属性都序列化,除了static或transient修饰的成员
- 序列化对象时,要求对象里的每一个属性都要实现序列化,基本数据类型的包装数据类型默认是实现了Serializable接口的
- 序列化具备可继承性,如果其父类实现了序列化,则它的所有子类也默认实现了序列化
六、标准输入流和输出流(System.in、System.out)
七、转换流(InputStreamReader、OutputStreamWriter )
- 在使用字符流读取文件时,默认是按照UTF-8的编码格式去读取的,如果文件的编码格式不是UTF-8,那么读取的文件内容就会出现乱码的情况
- 转换流 InputStreamReader和OutputStreamWriter 可以解决上面这种问题,通过指定编码格式来解决上述问题。
- 转换流可以将字节流包装成字符流
八、打印流(PrintWriter、PrintStream)
- 打印流只有输出流,没有输入流
- PrintStream是字节流、PrintWriter是字符流
- 标准输出System.out就是PrintStream类型,在默认情况下,PrintStream输出数据的位置是
标准输出
,即显示器 - 观察System.out.print()的print()方法,内部调用的实际是write()方法,所以
System.out.print()
等价于System.out.write()
- 修改输出位置
- PrintWriter既可以接收Writer类型的字符流,也可以接收OutputStream类型的字节流