Java IO
编码问题
gbk编码中文占2个字节,英文占一个字节。
utf-8编码中文占3个字节,英文占一个字节。
Java是双字节编码utf-16be,utf-16be中文占用2个字节,英文占用2个字节
当字节序列是某种编码时,这个时候想把字节序列变成字符串,也需要用这种编码形式,否则会出现乱码。
文本文件就是字节序列,可以是任意编码字节序列,如果在中文机器上直接创建文本文件,那么该文本文件只认识ansi编码。
在Eclipse中,Java项目创建时是什么编码,用的就是什么编码。不同编码项目之间复制文件,会出现乱码问题,但如果直接复制文字,则不会出现乱码,因为文字会被自动转码。
中文系统下,ANSI编码代表GBK编码
不同字符序列转成字符串时,要用字符序列的编码
File类的使用
java.io.File类用于表示文件(目录)
File类只用于表示文件(目录)的信息(名称、大小等),不能用于文件内容的访问。
RandomAccessFile的使用
RandomAccessFile :
java提供的对文件内容的访问 既可以读文件 也可以写文件
支持随机访问文件 可以访问文件的任意位置
(1)java文件模型 :
在硬盘上的文件是byte byte byte 存储的 是数据的集合
(2)打开文件
有两种模式 rw 读写 r只读
RandomAccessFile raf=new RandomeAccessFile(file,"rw");
文件指针,打开文件时文件在开头 pointer=0;
(3)写方法
raf.write(int) ----> 只写一个字节 (后8位) 同时指针指向下一个字节位置 准备再次写入
(4)读方法
int b=raf.read(); --->读一个字节
(5)文件读写完成后一定要关闭 (oracle官方说明)
raf.getFilePointer()获取当前指针位置
raf.length()获取文件长度
raf.seek()把指针指定到某一位置
注意write方法每次只能写入一个字节:
raf.write('A'); //此时指针后移
System.out.println(raf.getFilePointer()); //此时输出为1
这时只写入了一个字节而不是完整的char,只是因为后八位刚好能够表示A
raf.write('B');
若要写入一个整数i则需要写四次
int i=0x7fffffff;
raf.write(i>>>24); //高八位
raf.write(i>>>16);
raf.write(i>>>8);
raf.write(i); //写入最低的八位
System.out.println(raf.getFilePointer());
此时打印输出6
可以直接写入一个int
raf.writeInt(i);
String s="中";
byte[] gbk=s.getBytes("gbk");
raf.write(gbk);
System.out.println(raf.length(0);
此时打印输出12(中文占俩字节)
读文件,必须把指针移到头部
raf.seek();
//一次性读取:
byte[] buf= new byte[(int)raf.length()];
raf.read(buf);
System.out.println(Arrays.toString(buf));
此时打印输出
[65,66,127,-1,-1,-1,127,-1,-1,-1,-42,-48]
开头的65,66是正确的AB,因为后八位已经能表示AB了
也可按字符串输出
String s1=new String(buf);
System.out.println(s1,"gbk");
打印输出AB?????
因为“中”的前后都有字节,只有定位到中的两个字节,才能读出他
最后要加上raf.close();
字节流的使用
IO流(输入流、输出流)
字节流、字符流
1、字节流
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[],int start,int size) 读取数据到字节数组buf从buf的start位置开始,存放size长度的数据
4)输出流基本方法
out.write(int b) 写出一个byte到流,b的低8位
out.write(byte[] buf) 将buf字节数组都写入到流
out.write(byte[] buf,int start,size) 字节数组buf从start位置开始写size长度的字节到流
5)FileInputStream--->具体实现了在文件上读取数据
6)FileOutPutStream--->实现了向文件中写出byte数据的方法
7)DataOutputStream/DataInputStream
readInt、readLong /writeInt writeLong 等方法 对FileInputStream/FileOutputStream的方法进行包装,对“流”功能的扩展,可以更加方便的读取int、long、字符等类型数据
DataOutputStream
writeInt()/writeDouble()/writeUTF()
8)字节缓冲流 BufferInputStream&BufferOutputStream
这两个流类为IO提供了带缓冲区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能
从应用程序中把输入放入文件,相当于将一缸水倒入到另一个缸中:
FileOutStream--->write方法 相当于一滴一滴地把水“转移”过去
DataOutputStream--->writeXxx()方法 会方便一些,相当于一瓢一瓢把水“转移”过去
BufferedOutputStream--->write 方法更方便,相当于一瓢一瓢水先放入桶中,再从桶中倒入到另一个缸中
批量字节读取,作文件拷贝还是最快的
字符流的使用
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
4)字符流的过滤器
BufferedReader--->redline 一次读一行,读完了就是一个字符串,但不能识别换行,需单独写出换行操作->newline()。
BufferedWriter / PrintWriter--->写一行。PrintWriter构造更简单一些,可直接操作文件,输出的时候换行就用println()。
对象的序列化和反序列化
1)对象序列化,就是将Object转换成byte序列,反之叫对象的反序列化
2)序列化流(ObjectOutputStream),是过滤流--->writeObject
反序列化流(ObjectInputStream)--->readObject
3)序列化接口(Serializable)
对象必须实现序列化接口,才能进行序列化,否则将出现异常。在设计类时实现: implements Serializable
这个接口,没有任何方法,只是一个标准
transient关键字:加上transient关键字修饰的元素不会进行jvm默认的序列化;但可以自己完成这个元素的序列化
增加ObjectInputStream方法签名:
private void readObject(java.io.ObjectInputStream s) throws java.IO.Exception, ClassNotFoundException
s.defaultWriteObject();把jvm能默认序列化的元素进行序列化操作
s.writeInt(stuage);自己完成stuage的序列化
增加ObjectOutputStream方法签名:
private void writeObject(java.io.ObjectOutputStream s) throws java.IO.Exception
s.defaultReadObject();把jvm能默认反序列化的元素进行反序列化操作
this.stuage = s.readInt();自己完成stuage的反序列化操作
4)分析ArrayList源码中序列化和反序列化的问题:
ArrayList没有放满的数组元素是不需要进行序列化的,只把有效元素一个一个的自己完成序列化。
5)序列化中,子类和父类构造函数的调用问题
一个类实现了序列化接口,那么其子类都可以进行序列化
对子类对象进行反序列化操作时,如果其父类没有实现序列化接口,那么其父类的构造函数就会被显示地调用。
浙公网安备 33010602011771号