一、Serializable序列化的简要说明
1. 持久化的简单介绍:
“持久化”意味着对象的“生存时间”并不取决于程序是否正在执行——它存在或“生存”于程序的每一次调用之间。通过序列化一个对象,将其写入磁盘,以后在程序再次调用时重新恢复那个对象,就能圆满实现一种“持久”效果。
2. 语言里增加了对象序列化的概念后,可提供对两种主要特性的支持:
- 远程方法调用(RMI)使本来存在于其他机器的对象可以表现出好象就在本地机器上的行为。将消息发给远程对象时,需要通过对象序列化来传输参数和返回值。
- 使用一个Java Bean 时,它的状态信息通常在设计期间配置好。程序启动以后,这种状态信息必须保存下来,以便程序启动以后恢复;具体工作由对象序列化完成。
3. Serializable的一些说明:
- 对象的序列化处理非常简单,只需对象实现了Serializable 接口即可(该接口仅是一个标记,没有方法)
- 序列化的对象包括基本数据类型,所有集合类以及其他许多东西,还有Class 对象
- 对象序列化不仅保存了对象的“全景图”,而且能追踪对象内包含的所有句柄并保存那些对象;接着又能对每个对象内包含的句柄进行追踪
- 使用transient关键字修饰的的变量,在序列化对象的过程中,该属性不会被序列化。
4. 序列化的步骤:
- 首先要创建某些OutputStream对象:OutputStream outputStream = new FileOutputStream("output.txt")
- 将其封装到ObjectOutputStream对象内:ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
- 此后只需调用writeObject()即可完成对象的序列化,并将其发送给OutputStream:objectOutputStream.writeObject(Object);
- 最后不要忘记关闭资源:objectOutputStream.close(), outputStream .close();
5. 反序列化的步骤:
- 首先要创建某些OutputStream对象:InputStream inputStream= new FileInputStream("output.txt")
- 将其封装到ObjectInputStream对象内:ObjectInputStream objectInputStream= new ObjectInputStream(inputStream);
- 此后只需调用readObject()即可完成对象的反序列化:objectInputStream.readObject();
- 最后不要忘记关闭资源:objectInputStream.close(),inputStream.close();
二、Serializable序列化的代码实例
项目结构如下:
1. 首先我们建立一个Man类,实现了Serializable接口,用于Person类的测试:
package com.huhx.model;
import java.io.Serializable;
public class Man implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private String password;
public Man(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
2. 我们再建立一个Person类,用于序列化:
package com.huhx.model;
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private Man man;
private String username;
private transient int age;
public Person() {
System.out.println("person constru");
}
public Person(Man man, String username, int age) {
this.man = man;
this.username = username;
this.age = age;
}
public Man getMan() {
return man;
}
public void setMan(Man man) {
this.man = man;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3. 编写一个包含main方法的测试类:MainTest,它的writeSerializableObject用于序列化对象:
// Serializable:把对象序列化
public static void writeSerializableObject() {
try {
Man man = new Man("huhx", "123456");
Person person = new Person(man, "刘力", 21);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("output.txt"));
objectOutputStream.writeObject("string");
objectOutputStream.writeObject(person);
objectOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
4. 测试类MainTest,它的readSerializableObject用于反序列化对象:
// Serializable:反序列化对象
public static void readSerializableObject() {
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("output.txt"));
String string = (String) objectInputStream.readObject();
Person person = (Person) objectInputStream.readObject();
objectInputStream.close();
System.out.println(string + ", age: " + person.getAge() + ", man username: " + person.getMan().getUsername());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
5. 在Main方法添加以上两个方法的运行,结果如下:
- 在Person类中包含Man的引用,当Person被序列化的时候,从结果可以知道Man也被序列化了
- writeObject方法可以传入String,是因为String首先是一个类,其次它也是实现了Serializable接口的
- Person类中的age字段是transient,从打印结果可以看到,序列化Person person = new Person(man, "刘力", 21)对象时,age没有进行序列化。如果transient修饰的Object类型的,那么打印的结果将会是null
三、Externalizable序列化的代码实例
首先我们看一下Externalizable的定义:继承了Serializable接口
public interface Externalizable extends java.io.Serializable
1. 同样的我们先创建一个实现了Externalizable的User类:重写里面两个方法readExternal和writeExternal
package com.huhx.model;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class User implements Externalizable {
private String user;
public String getUser() {
return user;
}
public int getAge() {
return age;
}
private int age;
public User() {
System.out.println("user constructor.");
}
public User(String user, int age) {
System.out.println("user constructor two.");
this.user = user;
this.age = age;
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("read external.");
user = (String) in.readObject();
age = in.readInt();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("write external.");
out.writeObject(user);
out.writeInt(age);
}
}
2. 在MainTest中加入方法writeExternalizableObject,用于序列化对象User
// Externalizable的序列化对象
public static void writeExternalizableObject() {
User user = new User("huhx", 22);
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("Externalizable.txt"));
objectOutputStream.writeObject(user);
objectOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
3. 在MainTest中加入方法writeExternalizableObject,用于反序列化对象User
// Externalizable的反序列化对象
public static void readExternalizableObject() {
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("Externalizable.txt"));
User user = (User) objectInputStream.readObject();
objectInputStream.close();
System.out.println("name: " + user.getUser() + ", age: " + user.getAge());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
4. 在Main方法添加以上两个方法的运行,结果如下:
- 首先User user = new User("huhx", 22);执行了User的含参构造函数
- 当执行到writeObject(user);方法时,由于User实现了Externalizable接口,所以它的writeExternal会执行,
- 在User中的readExternal方法中调用了ObjectInput的readObject方法,在这个方法中通过反射机制创建User的实例,调用了User的无参构造函数。
- 然后在readObject方法执行的时候,同样会先执行User类的readExternal方法。这个会在后续源代码分析时讲到
四、总结
今天的Java知识分享就到这里!点关注,不迷路,关注程序员曾曾,每天分享不同的Java基础知识,如果想要知道更多的Java基础知识我这边整理了一个我自己的GitHub仓库:Java小白修炼手册,大家如果有需要可以自行查看