Java核心API之对象流的使用介绍
1、对象流的基本概念
存在既有意义,对象流可以帮助我们将内存中的Java对象保存到本地磁盘,即字节序列。这种将Java对象转化为一个字节序列,就叫做对象序列化。反之,将一个序列文件转化为Java对象,这个过程称作反序列化。
听起来对象流很高大上,其实很简单。在Java程序运行过程中,Java对象的属性值都存储在内存,程序结束后释放内存,一股青烟后,任何数据都会消失,为了将内存中的Java对象的属性状态保存到本地磁盘,于是对象流就应运而生。

2、Serializable接口
说到对象序列化,必须要讲到Serializable接口。Java对象所属的类必须实现Serializable接口。实现这个接口不必重写任何方法,其只作为可序列化的标识。
小编建议每一个实现Serializable接口的对象类,需要显示添加一个常数serialVersionUID,表明该类的版本。如果不添加该常数,对象序列化时会根据对象类的信息计算出默认的serialVersionUID的值。
serialVersionUID就像我们的身份证一样,每次序列化时,java编译器会根据serialVersionUID确认java对象类。反序列化时,如果serialVersionUID不一致,会抛出InvalidClassException异常。
Serializable接口稍后还会讲到,我们接着学习对象流的操作。

3、对象流之ObjectOutputStream对象序列化
ObjectOutputStream可以将对象进行序列化的输出流。
示例代码如下:
//ObjectOutputStreamDemo类代码
public class ObjectOutputStreamDemo{
/*
* void writeObject()
* 将特定的对象转化为字节序列
*/
@Test
public void testOOS() throws Exception{
FileOutputStream fos = new FileOutputStream("boy.obj");
//构造方法ObjectOutputSteam(OutputStream out)
ObjectOutputStream oos = new ObjectOutputStream(fos);
Person boy = new Person("Peter",12,"男");
oos.writeObject(boy);
System.out.println("完成序列化");
oos.close();
}
}
//Java对象类代码
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String gender;
public Person() {
super();
}
public Person(String name, int age, String gender) {
super();
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "name=" + name + ", age=" + age + ", gender=" + gender ;
}
}
注意:可以将上述代码片段粘贴到本地,运行测试,供大家参考,如果有问题可以联系小编。


4、对象流之ObjectInputStream对象反序列化
ObjectInputStream可以将序列化转换成对象。
示例代码如下:
//ObjectInputStreamDemo类
public class ObjectInputStreamDemo{
/*
* Object readObject();
* 将对象序列化转换为对象
*/
@Test
public void testOIS() throws Exception{
FileInputStream fis = new FileInputStream("boy.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
//Object readObject()读取二进制文件转换成对象
Person boy = (Person)ois.readObject();
System.out.println(boy);
System.out.println("反序列化操作完毕");
ois.close();
}
}

5、实现Serializable接口注意事项
Java对象类实现Serializable接口后,显式声明serialVersionUID,确定Java对象所属类的版本。如果不显式声明,Java编译器会根据Java对象类的结构计算生成默认的serialVersionUID,这意味着每次修改Java对象类会重新生成默认的serialVersionUID,这样导致反序列化失败。
下面代码有几处进行少量修改
1)、显式声明的常量serialVersionUID注释掉;
2)、然后修改Person类,添加体重(weight)的属性;
3)、最后运行反序列化代码
4)、查看运行结果。
示例代码如下:
//ObjectOutputStreamDemo类代码如下
public class ObjectOutputStreamDemo{
/*
* void writeObject()
* 将特定的对象转化为字节序列
*/
@Test
public void testOOS() throws Exception{
FileOutputStream fos = new FileOutputStream("boy.obj");
//构造方法ObjectOutputSteam(OutputStream out)
ObjectOutputStream oos = new ObjectOutputStream(fos);
Person boy = new Person("Peter",12,"男",50);
oos.writeObject(boy);
System.out.println("完成序列化");
oos.close();
}
}
//ObjectInputStreamDemo类如下
public class ObjectInputStreamDemo{
/*
* Object readObject();
* 将对象序列化转换为对象
*/
@Test
public void testOIS() throws Exception{
FileInputStream fis = new FileInputStream("boy.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
//Object readObject()读取二进制文件转换成对象
Person boy = (Person)ois.readObject();
System.out.println(boy);
System.out.println("反序列化操作完毕");
ois.close();
}
}
//Java对象类代码如下
public class Person implements Serializable {
//private static final long serialVersionUID = 1L;
private String name;
private int age;
private String gender;
private int weight;
public Person() {
super();
}
public Person(String name, int age, String gender,int weight) {
super();
this.name = name;
this.age = age;
this.gender = gender;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getWeight(){
return weight;
}
public void setWeight(int weight){
this.weight = weight;
}
@Override
public String toString() {
return "name=" + name + ", age=" + age + ", gender=" + gender +", weight="+weight ;
}
}
分析:对象序列化操作,对象所属类除了要实现Serializable接口外,还要显式声明serialVersionUID,确保对象类的版本一致,修改类信息后不会由于默认的serialVersionUID不一致而报错。
思考:大家可以将上述代码拷贝到本地运行测试,serialVersionUID不变,删除Person类的属性查看运行结果。
