这一篇我讲介绍一下字符流。什么是字符流,就是若干个字节组成一个字符。(为什么说是若干个,不能确定吗)这里就来说说原因和编码问题。
首先说说字符编码的问题,比较常用的编码有gbk,utf-8等。
a、.编码问题(看前面所描述的)。
1、gbk 编码中文占用2个字节,英文占用1个字节。
2、utf-8编码中文占用3个字节,英文占用1个字节。
b、认识文本与文本文件
Java是双字节编码,utf-16be编码。即char占用2个字节。注意:当你的字节序列是某种编码时,这个时候想把字节序列变成字符串,也需要用这种编码方式。否则会出现乱码。文本文件就是字节序列,可以是任意编码的字节序列。但是通常我们在中文的机器上直接创建文件,那么该文件只认识ANSI编码。
文本文件是文本(char)序列按照某种编码方案(utf-8,utf-16be,gbk)序列化为byte的存储。
c、字符流分为输入流(writer)和输出流(reader)。操作的是文本、文本文件字符的处理,一次处理一个字符。但是我们要知道字符的底层还是基本的字节序列(字节传输才是文件最终的传输)。
这里我们来上一张字符流的家族谱。
字符流的基本实现:
InputStreamReader:完成byte流解析为char流,按照编码解析
InputStreamWriter:完成char流到byte流的解析,按照编码处理
d、FileReader&FileWriter(文件的输入输出字符流)
可以直接写文件名的路径。与InputStreamReader相比坏处:无法指定读取和写出的编码,容易出现乱码。
FileReader fr=new FileReader(“Demo\\im.txt”);//输出流
FileWriter fw=new FileWriter(“Demo\\im2.txt”);输入流
e、字节流的过滤器
BufferedReader:readLine();一次读取一行,但不能识别换行。。
BufferedWriter/PrintWriter;一次写一行,PrintWriter可以自动换行
如:一次写入一行
BufferedWriter PrintWriter
bw.wrire(line) pw.println(line);//自动换行,不加ln不换行
bw.newLine();//单独换行 pw.flush();//刷新数据
bw.flush();//刷新数据
代码如下:
package com.ll.iofile;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
/**
* 这个例子是用来测试字节流的过滤器
* BufferedReader和BufferedWriter/PrintWriter类的使用
* @author LULEI
*
*/
public class testBufferedReaderAndBufferedWriter {
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
//实例化一个BufferedReader的对象
BufferedReader br=new BufferedReader(new InputStreamReader(
new FileInputStream("C:\\Users\\Administrator\\Desktop\\搜狗.txt")));
//实例化一个BufferedWriter的对象
BufferedWriter wr=new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("Demo\\immoc1.txt")));
//实例化一个PrintWriter的对象
PrintWriter pw=new PrintWriter(
new OutputStreamWriter(
new FileOutputStream("Demo\\imooc2.txt")));
String line=new String();
while((line=br.readLine())!=null){
/*测试BufferedWriter的write方法
wr.write(line);//这样直接输入的只会在一行显示
wr.newLine();//这句话起到读取一行就换行的作用
wr.flush();
*/
//System.out.println(line);//每读取一行字节,直接输出
//测试PrintWriter的方法
pw.println(line);//输出一行字节的内容,且加ln自动换行
pw.flush();
}
wr.close();
br.close();
pw.close();
}
}
这里我就先介绍几个比较常用的字符流。接下来介绍一下序列化和反序列化的知识。
f、什么是对象的序列化与反序列化?
1、对象的序列化是指将object对象转换成byte序列,反之将byte序列转换为object对象称之为反序列化。
2、序列化流(objetcOutputStream),是过滤流。方法:writeObject()
反序列化流(objectInputStream),方法:readObject()的使用需要进行强制类型的转换。
3、序列化接口(serializable)
对象必须实现序列化接口才能进行序列化,否则将会出现异常。这个接口没有任何的方法,只是一个标准。
代码如下:
package com.ll.iofile;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 这个类是用于测试对象的序列化和反序列化
* 调用ObjectOutputSrteam 和ObjectInputStream类的使用
* @author LULEI
*
*/
public class testObjectSerializableDemo {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
//1、实现对象的序列化流的对象
String file="Demo/object.txt";
ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream(file));
//实例化一个MobilePhone对象
MobilePhone mp=new MobilePhone(4, 5.5f, "红米Note");
//开始写对象
oos.writeObject(mp);
//刷新缓冲区数据
oos.flush();
oos.close();
//2实现对象的反序列化流对象
ObjectInputStream ois=new ObjectInputStream(
new FileInputStream(file));
//必须进行强制类型转换
MobilePhone mps=(MobilePhone)ois.readObject();
System.out.println(mps);
ois.close();
}
}
package com.ll.iofile;
import java.io.Serializable;
/**
* 这个类是用于参与测试对象的序列化和反序列化
* ObjectOutputSrteam 和ObjectInputStream类的使用
* @author LULEI
*
*/
//这里必须要继承serializable接口
public class MobilePhone implements Serializable {
private int cpu;
private float screen;
private String name;
public MobilePhone(){
}
public MobilePhone(int cpu, float screen, String name) {
super();
this.cpu = cpu;
this.screen = screen;
this.name = name;
}
public int getCpu() {
return cpu;
}
public void setCpu(int cpu) {
this.cpu = cpu;
}
public float getScreen() {
return screen;
}
public void setScreen(float screen) {
this.screen = screen;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MobilePhone [cpu=" + cpu + ", screen=" + screen + ", name="
+ name + "]";
}
}
g、 transient关键字
transient关键字修饰的属性默认是不能序列化的,但是可以使用writeObject()自己完成这个元素的序列化。ArrayList就是用了此方法进行了优化的操作。ArrayList最核心的容器Object[] elementData使用了transient修饰,但是在writeObject自己实现对elementData数组的序列化。只能序列化数组中真实存在的元素,对于数组中的空的元素时不能进行序列化的。
如:
private void writeObject(java.io.ObjectOutputStream s) throws
java.io.IOException{
}
private void readObject(java.io.ObjectOutputStream s) throws
java.io.IOException,classNotFoundException{
}
代码如下:
package com.ll.iofile;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 这个类是用于测试对象的序列化和反序列化
* 调用ObjectOutputSrteam 和ObjectInputStream类的使用
* 1、首先实例化ObjectOutputSrteam 和ObjectInputStream类的对象oos和ois
* 2、实例化将要序列化的对象qq
* 3、对象oos调用writeObject()方法来对对象qq进行序列化流的输出操作。
* 4、对象ois调用readObject()方法来进行反序列化的输入操作。同时要使用强制类型转换来转化
* 赋值给相应的实例化对象x。
* 4、最后直接输出x的内容
* @author LULEI
*
*/
public class testTransient {
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
//1、实例化首先实例化ObjectOutputSrteam 和ObjectInputStream类的对象
String file=new String("Demo\\testTransient");
ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream(file));
ObjectInputStream ois=new ObjectInputStream(
new FileInputStream(file));
//2、实例化将要被序列化的对象MobilePhone
MobilePhone2 mp=new MobilePhone2(4, 4.0f, "华为");
//3、oos对象调用writeObject()方法来进行序列化
oos.writeObject(mp);
oos.flush();//刷新缓冲区
oos.close();//关闭文件流
//4、ois对象调用readObject方法来进行反序列化的操作,同时赋值给相应类型的对象
try {
MobilePhone2 mps=(MobilePhone2)ois.readObject();
System.out.println(mps);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ois.close();
}
}
package com.ll.iofile;
import java.io.Serializable;
/**
* 这个类是用于参与测试对象的序列化和反序列化
* ObjectOutputSrteam 和ObjectInputStream类的使用
* 同时检测了transient关键字的使用
* @author LULEI
*
*/
//这里必须要继承serializable接口
public class MobilePhone2 implements Serializable {
private int cpu;
//使用transient关键字修饰,这时screen的值不会被序列化。这时
//如果想进行序列化,只能够自己人为的进行序列化
private transient float screen;
private String name;
public MobilePhone2(){
}
public MobilePhone2(int cpu, float screen, String name) {
super();
this.cpu = cpu;
this.screen = screen;
this.name = name;
}
/*这里是自己认为的完成序列化的操作
//人工的进行序列化
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
s.defaultWriteObject();//则是执行默认的序列化操作
s.writeFloat(screen);//自己人工的进行screen序列化操作
}
//人工的进行反序列化的操作
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();//执行默认的反序列化的操作
this.screen=s.readFloat();//自己完成screen的反序列化的操作
}
*/
public int getCpu() {
return cpu;
}
public void setCpu(int cpu) {
this.cpu = cpu;
}
public float getScreen() {
return screen;
}
public void setScreen(float screen) {
this.screen = screen;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MobilePhone [cpu=" + cpu + ", screen=" + screen + ", name="
+ name + "]";
}
}
h、序列化过程中子父类构造函数的问题
1>父类实现了serializable接口,子类继承父类就可以序列化了。
2>但子类在进行反序列化的时候,父类 实现了序列化接口,则不会递归调用其构造函数。
3>父类未实现了serializable接口,子类自己实现了接口,子类可以自行实现可序列化。
4>子类在反序列化时,父类没有实现序列化的接口,则会递归调用其构造函数。
到这里,我的Java IO部分的基础知识算是学完了。这里做了总结,方便以后来复习。。。。