之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理(
原文章),记录一下。
首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。
数据来源的操作:
来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。
File file = new File("path");
字节操作:
将数据从最原始状态转换为字节流。当然要提供字节操作。使用类
InputStream
,
FileInputStream,
BufferedInputStream。Java I/O 使用了装饰者模式来实现。以 InputStream 为例,InputStream 是抽象组件,FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作。FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能,例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
字符操作:
来源于上一层,字节流。
InputStreamReader 实现从文本文件的字节流解码成字符流;OutputStreamWriter 实现字符流编码成为文本文件的字节流。它们继承自 Reader 和 Writer。这里操作的是,字节流到字符流之间的转换。
InputStreamReader reader = new InputStreamReader(bis);
这里有个问题,字节转字符时,字符是有不同编码的。
GBK 编码中,中文占 2 个字节,英文占 1 个字节;UTF-8 编码中,中文占 3 个字节,英文占 1 个字节;Java 使用双字节编码 UTF-16be,中文和英文都占 2 个字节。
编码就是把字符转换为字节,而解码是把字节重新组合成字符。
如果编码和解码过程使用不同的编码方式那么就出现了乱码。
FileInputStream input=new FileInputStream(file);
BufferedInputStream bis=new BufferedInputStream(input);
byte[] byteArray=new byte[1024];
int tmp;
StringBuilder strBuild=new StringBuilder();
while((tmp=bis.read(byteArray))!=-1){
strBuild.append(new String (byteArray,0,tmp,"UTF-8"));//设置字符编码 解码
}
System.out.println(strBuild);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
InputStreamReader reader = new InputStreamReader(bis,"UTF-8");//设置字符编码
String str = "";
int ch;
while ((ch = reader.read() )!= -1){
str +=(char)ch;//解码
}
System.out.println(str);
对象操作:
序列化就是将一个对象转换成字节序列,方便存储和传输。
序列化:ObjectOutputStream.writeObject()
反序列化:ObjectInputStream.readObject()
序列化的类需要实现 Serializable 接口,它只是一个标准,没有任何方法需要实现。
transient 关键字可以使一些属性不会被序列化。
ArrayList 序列化和反序列化的实现 :ArrayList 中存储数据的数组是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。
总结:
Java中io操作,使用数据流操作时,思路为,先将文件流转为字节流,再将字节流转换为字符流,或者对象。也就不难理解代码的过程,不同的工具类操作不同数据对象的结果。
因为字节的操作是单个字节,为了弥补高速和面向块的操作,Java还提供了
新的输入/输出 (NIO) 。(资料)