Java IO Flow
编码的一些注意点
- GBK编码格式中,1个汉字占用2个字符,1个英文占用1个字节,1个字符占用2个字节
- UTF编码格式中,1个汉字占用2个字符,1个英文占用1个字节,1个字符占用3个字节
Java是双字节编码,即字符串中1个字符占用2个字节
采用Unicode编码格式,即utf-16be,中文占用2个字节,英文也占用2个字节
所以要注意编码解码要使用同样的编码格式,否则会出现错误。Java默认采用Unicode。
Java File类
- java.io.File类用于表示文件(目录)
- File类只用于表示文件(目录)的信息(名称、大小等),不能用于文件内容的访问
File类有许多方法,举几个例子如下的
File file = new File("xx:"+File.separator);
file.mkdir();//file.mkdirs()
file.delete();
/*这里可以判断file类型, 有很多*/
file.isFile();
这里不一一列举了,这里分隔符是一个问题,有同学经常写错,这里可以使用File.separator静态变量,并且支持Linux和Windows,这样就不用担心这个问题了。
这是一个遍历文件的Demo
文件遍历Demo
注意dir.list()和dir.lists()区别,这里用到递归调用,保证遍历
RandomAccessFile类
- 提供对文件内容的访问,完成读写操作。
- 支持随机访问文件
java文件模型
在硬盘上的文件时byte byte存储的,是数据的集合,打开文件有两种方式
打开方式 “rw” “r”
RandomAccessFile raf = new RandomAccessFile(file,"rw");
文件指针,打开文件时,指针在开头,pointer = 0;
写方法
raf.write(int)----->只写一个字节(后8位),同时指针指向下一个位置,准备再次写入
读方法
int b = raf.rea的()----->读一个字节
文件读完之后一定要关闭(小心意想不到的错误)
raf.close();
Demo
点击这里查看RAF的例子
使用一次性读取文件内容时,这里会出现乱码,是因为只有汉字是按照gbk编码,其他内容,仍旧使用默认编码,所以需要注意。
IO流
字节流
- InputStream、OutputStream分别抽象了应用数据读取和写入的方法。
- EOF=end 读到-1就读到结尾
- 有许多读写基本方法,按字节读写,或者将读写数据放入byte[].
其他输入输出流均为字节流的子类。
FileIn/OutputStream流
实现向文件中读写的方法,点击查看例子
File流读写工具类
针对一个文件读取并写入,性能比较,具体代码在Test.java中
Test.java
文件大小 | 字节批量读取 | Buffered缓冲流读取 | 逐个字节读取 |
4MB | 64ms | 12788ms | 24076ms |
37MB | 707ms | 101903ms | 时间太久 |
这个例子中的copyFile()方法,使用字节批量读取,速度房产快,远远优于其他读取方法。其次是缓冲读取,但只有在大容量文件读取时比较明显。上面的数据太明显了,所以推荐字节批量读取。
DataInputStream/DataOutPutStream
对”流功能的扩展”,可以更加方便的读取int,long,字符等类型数据
DataOutputStream
- writeInt()
- writeDouble()
- writeUTF()
这些只是装饰了基础的方法,提供一个更简单好用的统一方法。是一种装饰模式的体现。
代码测试如下
DataInputStream例子
DataOutputStream例子
BufferedIn/OutputStream流
这两个流类为IO提供了带缓冲区的操作,一般打开文件进行读写操作时,都会带上缓冲,这种流模式提高了IO的性能,从应用程序中把输入放入文件中,相当于将一缸水倒入另一个水缸中:
- FileOutputStream—>write()方法相当于一滴一滴地把水”转移”过去
- DataOutputStream—>writeXxxx()方法会方便一些,相当于一碗一碗把水”转移”过去
- BufferedOutputStream—->write方法更方便,相当于先一碗一碗把水放入桶中,再从桶中倒入到另一个水缸中,这样性能更高。
性能问题在上面已经做过比较
字符流
1. 编码问题
2. 认识文本和文本文件
Java的文本(char)是16位无符号整数,是字符的unidode编码(双字节编码)文件时byte byte byte…的数据序列
文本文件是文本(char)序列按照某种编码方案序列化为byte的集合
3. 字符流(Reader Writer)—->操作的是文本,文本文件
字符的处理,一次处理一个
字符的底层仍然是基本的字节序列
字符流的基本实现
- InputStreamReader 完成byte流解析为char流,按照编码解析。
- OutputStreamWriter 提供char流到byte流,按照编码处理。
- FileReader和FileWriter同上,FileReader无法实现设定编码类型
InputStreamReader和OutputStreamWriter
FileReader和FileWriter
字符流过滤器
- BufferedReader —->readLine一次度一行
- BufferedWriter/PrintWriter—-写一行
我们常常将BufferedReader和PrintWriter结合起来用
对象的序列化,反序列化
- 对象序列化,就是将Object对象转换成byte序列,反之叫对象的反序列化
- 序列化流(ObjectOutputStream),是过滤流 —–writeObject,反序列化流(ObjectInputStream) readObject()
对象必须实现序列化接口Serializable,才能进行序列化,否则将出现异常,这个接口没有任何方法,只是一个标准。
用法演示
这里注意写入和读取时,分别注释另一块代码,最好分开写成两个函数,方便测试。同下面代码。
transient关键字会使其修饰的元素不进行jvm默认的序列化,但可以自己完成这个元素的序列化
改写writeObject和readObject方法,可以从ArrayList中copy一个,可以看看ArrayList源码,里面有对ArrayList中有效元素的手动序列化,默认是transient的。注意是有效!!!
序列化中子类和父类构造函数的调用问题
序列化过程中子父类构造函数问题
- 一、父类实现了serializable接口,子类继承就可序列化。
- 子类在反序列化时,父类实现了序列化接口,则不会递归调用其构造函数。
- 二、父类未实现serializable接口,子类自行实现可序列化
- 子类在反序列化时,父类没有实现序列化接口,则会递归调用其构造函数。