JAVA学习笔记——JAVA中的IO流,
JAVA学习笔记——JAVA中的IO流,
本文内容主要根据慕课网的《文件传输基础——JAVA IO流》课程整理而成。(链接:http://www.imooc.com/learn/123)
一. 文件的编码
问题:
新建一个文本文件,输入“联通”后保存,再打开会出现乱码。而输入“联想”再打开时则不会出现乱码。
原因:
文本文件是字节序列,如果在中文机上直接创建文本文件,该文本文件只认识ANSI编码,输入”联通“时是一种巧合,刚好符合了UTF-8的编码规则,因此打开时为UTF-8编码方式,如果再点击打开选择ANSI编码方式,则不会出现乱码问题。
三种编码类型的字符占用字节数为:
中文 英文
gbk 2 1
utf-8 3 1
utf-16be 2 2
因此,当字节序列是由某种方式编码时,也需要用这种方式进行解码,否则可能出现乱码。
使用Eclipse创建项目时默认的编码是gbk编码(可以修改,但一个项目只有一种编码方式),用字节流创建字符时若未指定编码类型则使用默认编码。而utf-16be是JAVA中字符串的编码方式。
二. File类的使用
java.io.File类用于表示文件(目录),File类只用于表示文件(目录)的信息(大小,名称等),不能用于文件内容的访问。
常用方法:exists mkdir mkdirs createNewFile delete isFile isDirectory getAbsolutePath getName getParent 等,比较简单,需要时直接查看文档或提示即可。
三. RandomAccessFile类的使用
RandomAccessFile是java提供的对文件内容的访问的类,既可以读文件,也可以写文件。
RandomAccessFile支持随机访问文件,可以访问文件的任意位置。
(1)java文件模型
在硬盘上的文件是byte byte byte存储的,是数据的集合
(2)打开文件
有两种模式"rw"(读写) "r"(只读)
RandomAccessFile raf = new RandomeAccessFile(file,"rw")
文件指针,打开文件时指针在开头 pointer = 0;
(3) 写方法
raf.write(int)--->只写一个字节(后8位),同时指针指向下一个位置,准备再次写入。一个int为4字节,如果用write写一个int要配合移位算符写四次(实际上就是writeInt的底层源码)。
(4)读方法
int b = raf.read()--->无参数则只读一个字节,传入字节数组可以一起读入到字节数组中。
(5)文件读写完成以后一定要关闭(Oracle官方说明)
四. 字节流
1)InputStream、OutputStream
InputStream抽象了应用程序读取数据的方式
OutputStream抽象了应用程序写出数据的方式
2)EOF = End 读到-1就读到结尾
3)输入流基本方法
int b = in.read();读取一个字节无符号填充到int低八位.-1是 EOF
in.read(byte[] buf) 读取数据填充到字节数组buf,单个字节读取,不适合读取大文件。
in.read(byte[] buf,int start,int size) 批量读取。读取从start开始长为size的数据到字节数组buf,返回的是读到字节的个数(可能读不满),批量读取到字节缓冲数组时应用这个方法。批量读取,对大文件而言效率高(缓冲器的大小应适中)。
4)输出流基本方法
out.write(int b) 写出一个byte到流,b的低8位
out.write(byte[] buf)将buf字节数组都写入到流
out.write(byte[] buf,int start,int size) 将字节数组buf中的从start开始长为size的数据写到输出流。
5)FileInputStream
继承了Inputstream,把文件作为字节流进行读操作,具体实现了在文件上读取数据。读方法与InputStream相同。
6)FileOutputStream
继承了Inputstream,把文件作为字节流进行写操作,实现了向文件中写出byte数据的方法,写方法与OutputStream相同
7)DataOutputStream/DataInputStream
对"流"功能的扩展,可以更加方面的读取int,long,字符等类型数据
DataOutputStream
writeInt()/writeDouble()/writeUTF() 实际上包装了write方法对不同类型数据的处理,扩展了功能。
8)BufferedInputStream&BufferedOutputStream
这两个流类位IO提供了带缓冲区的操作,一般打开文件进行写入
或读取操作时,都会加上缓冲,这种流模式提高了IO的性能
从应用程序中把输入放入文件,相当于将一缸水倒入到另一个缸中:
FileOutputStream--->write()方法相当于一滴一滴地把水“转移”过去
DataOutputStream-->writeXxx()方法会方便一些,相当于一瓢一瓢把水“转移”过去
BufferedOutputStream--->write方法更方便,相当于一飘一瓢先放入桶中,再从桶中倒入到另一个缸中,性能提高了。
视频中给出了三种方法对文件进行复制,速度是2>>3>1,但是3中使用的是单字节读取,实际上使用批量读取的带缓冲的复制对大文件应该是更快的。
在使用时,注意BufferedOutputStream每次读取后都要用flush()对缓冲区进行刷新。(视频中说需要进行刷新,但有看到说不要进行刷新,否则严重印象效率。。有待进一步查阅资料)
五. 字符流
1) 编码问题
2)认识文本和文本文件
java的文本(char)是16位无符号整数,是字符的unicode编码(双字节编码)
文件是byte byte byte ...的数据序列
文本文件是文本(char)序列按照某种编码方案(utf-8,utf-16be,gbk)序列化为byte的存储结果
3)字符流(Reader Writer)
---->操作的是文本文本文件
字符的处理,一次处理一个字符
字符的底层任然是基本的字节序列
字符流的基本实现
InputStreamReader 完成byte流解析为char流,按照编码解析,在构造函数中没有指定编码时将默认为项目编码,操作文件时应与文件的编码相一致。
OutputStreamWriter 提供char流到byte流,按照编码处理,同样也要注意编码问题。
FileReader/FileWriter
主要是操作文件,API比较方便,但不能指定编码。
字符流的过滤器
BufferedReader ---->readLine 一次读一行
BufferedWriter/PrintWriter ---->写一行,直接操作时不能识别换行,BufferedWriter可以添加newLine实现换行操作,PrintWriter可以用Println实现换行。
六. 对象的序列化,反序列化
1)对象序列化,就是将Object转换成byte序列,反之叫对象的反序列化2)序列化流(ObjectOutputStream),是过滤流----writeObject
反序列化流(ObjectInputStream)---readObject
3)序列化接口(Serializable)
对象必须实现序列化接口 ,才能进行序列化,否则将出现异常
这个接口,没有任何方法,只是一个标准
4) transient关键字
使用transient关键字后该成员变量不会进行jvm默认的序列化,反序列化后对应的值为默认值,但也可以自己完成这个元素的序列化。
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException
在writeObject方法中,调用defaultWriteObject能对默认元素进行序列化,对transient的元素可写writeXXX自己来完成序列化。同理,在readObject中也可以完成transient元素的序列化。
在某些情况下,transient关键字可以提高序列化的性能,分析ArrayList源码中序列化和反序列化的问题,在elementData中用了transient关键字,由于arraylist数组的元素未定,自行根据元素的数目来序列化能避免浪费,因此提高了性能。
5)序列化中 子类和父类构造函数的调用问题
一个类实现了序列化接口,其子类都能进行序列化。子类在构造时会递归调用父类的构造函数,而反序列化时,如果其父类没有实现序列化接口,那么父类的构造函数会被调用。
相关文章
- 暂无相关文章
用户点评