(一) InputStreamReader和OutputStreamWriter:
1. 将字节流(8位)转换成字符流(16位)可以使用InputStreamReader和OutputStreamWriter来进行读和写。
2. InputStreamReader与OutputStreamWriter是字节流通向字符流的桥梁。
(二) DataInputStream和DataOutputStream读写Java基本类型(int,double…..)的数据。不可以读写复杂的引用类型实例,如Employee,Book….。
(三) ObjectInputStream与ObjectOutputStream能够使内存中的对象保存到文件里或从文件里读回原来的对象,即完成对象的序列化与反序列化(将内存中的对象永久保存在媒介上)。
JVM: Employees e1, e2,e3 ----序列化----> emp.bin
文件:Emp.bin ----反序列化----> e1,e2,e3对象
1. 所有需要被序列化对象的类必须要实现java.io.Serializable(我们把这种接口称为标识接口)接口。Serializable接口中没有方法我们将这种特殊的接口叫做标识接口(标识一种可序列化的类型)。
如:
public class Employee implementsjava.io.Serializable {
......
}
2. 在序列化时需要忽略的属性可以用transient关键字修饰。
3. 被序列化对象的属性要求也要能被序列化。
例子:
下面是2个测试类,一个员工类。首先是将一个员工类利用构造函数初始化,把其中的信息通过序列化存入一个指定的文件,然后通过反序列化读入JVM内存,进行控制台输出。
首先是Employee,必须实现Serializable接口:
/**
* java.io.Serializable接口中没有声明任何方法,我们把这种接口称为标识接口,
* 标识一个对象只要实现了这个接口,它才能被写入到文件。并且还要求类中的属性也是能被序列化的。
*
*/
public class Employee implements Serializable{
private Integer empno;
//transient表示临时的性的,这个关键字定义的属性在序列化过程中会被忽略。
private transient String password; // 敏感数据
private String ename;
private Double sal;
public Employee() {
super();
}
public Employee(Integer empno, String password, String ename, Double sal) {
super();
this.empno = empno;
this.password = password;
this.ename = ename;
this.sal = sal;
}
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
@Override
public String toString() {
return "Employee [empno=" + empno + ", password=" + password
+ ", ename=" + ename + ", sal=" + sal + "]";
}
}
然后是序列化实现类:
public static void main(String[] args) {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("emp.bin"));
//对象要能被序列化就必须实现java.io.Serializable接口
Employee e1 = new Employee(1000, "abcd", "小花", 4000.0);
Employee e2 = new Employee(1002, "1234", "小燕", 4900.0);
Employee e3 = new Employee(1003, "xxxx", "小丽", 4500.0);
Employee e4 = new Employee(1004, "yyyy", "小刘", 5000.0);
System.out.println(e1);
oos.writeObject(e1);
oos.writeObject(e2);
oos.writeObject(e3);
oos.writeObject(e4);
oos.writeObject(null);//作为结束标记
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
最后是反序列化:
public static void main(String[] args) {
// 反序列化(即将存在文件中的对象状态还原的JVM内存)
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("emp.bin"));
Object obj = null;
while ((obj = ois.readObject()) != null) {
Employee emp = (Employee)obj;
System.out.println(emp);
}
//对象读完出来后报了一个:java.io.EOFException, 已读到文件结束位置了还继续读,就会出这个异常。
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally{
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
最后可以看到Employee类中的password前面加了transient而没有被写入文件,因此也不会被读出来了。