前言
通常在写实体类时都默认继承Serializable接口,但是有什么用?今天就来深入了解下java序列化机制
序列化、反序列化的概念
序列化就是将java对象转化成二进制保存到磁盘中去,反序列化就是从磁盘中读取文件流然后转成java对象
常用的方式及好处
- 网络通讯传输java对象数据->利用序列化实现远程通信,即在网络上传送对象的字节序列
- 永久保存java对象->实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上
Java序列化机制
- Serializable接口
- 创建StudentEntity 并实现Serializable 接口
public class StudentEntity implements Serializable {
private static final long serialVersionUID = 1L;
private String studentName;
//get set toString
serialVersionUID有两种显示的生成方式:
一是默认的1L,比如:private static final long serialVersionUID = 1L;
二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如: private static final long serialVersionUID = xxxxL;
serialVersionUID的作用->目的是序列化对象版本控制,有关各版本反序列化时是否兼容
- 在Test类中去实现序列化和反序列化。
public class Test {
public static void main(String[] args) throws Exception, IOException {
SerializeStudent();
DeSerializeStudent();
}
//序列化方法
private static void SerializeStudent() throws FileNotFoundException, IOException {
StudentEntity studentEntity = new StudentEntity();
studentEntity.setStudentName("Kaleldo");
//序列化对象到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C://Test/Student"));
oos.writeObject(studentEntity);
oos.close();
System.out.println("序列化对象成功");
}
//反序列化方法
private static void DeSerializeStudent() throws FileNotFoundException, IOException{
File file = new File("C://Test/Student");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
StudentEntity studentEntity = null;
try {
studentEntity = (StudentEntity)ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("反序列化对象成功"+studentEntity.toString());
}
}
- 结果:在C://Test 目录会发现一个Student文件 ,同时控制台输出以下内容
Connected to the target VM, address: '127.0.0.1:51227', transport: 'socket'
序列化对象成功
Disconnected from the target VM, address: '127.0.0.1:51227', transport: 'socket'
反序列化对象成功StudentEntity{studentName='Kaleldo'}
- Externalizable接口
- 实现Externalizable接口需注意3点
(1)Externalizable继承自Serializable接口
(2)需重写writeExternal()与readExternal()方法
(3)实现Externalizable接口的类必须要提供一个public的无参的构造器。
public class Student1Entity implements Externalizable {
private String studentName;
public Student1Entity() {
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(studentName);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
studentName = (String) in.readObject();
}
//get set toString
}
- 测试
public class Test {
public static void main(String[] args) throws Exception, IOException {
SerializeStudent();
DeSerializeStudent();
}
//序列化方法
private static void SerializeStudent() throws FileNotFoundException, IOException {
Student1Entity studentEntity = new Student1Entity();
studentEntity.setStudentName("Kaleldo");
//序列化对象到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C://Test/Student1"));
oos.writeObject(studentEntity);
oos.close();
System.out.println("序列化对象成功");
}
//反序列化方法
private static void DeSerializeStudent() throws FileNotFoundException, IOException{
File file = new File("C://Test/Student1");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Student1Entity studentEntity = null;
try {
studentEntity = (Student1Entity)ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("反序列化对象成功"+studentEntity.toString());
}
}
-输出
Connected to the target VM, address: '127.0.0.1:51328', transport: 'socket'
序列化对象成功
Disconnected from the target VM, address: '127.0.0.1:51328', transport: 'socket'
反序列化对象成功Student1Entity{studentName='Kaleldo'}
transient关键字及静态变量的序列化
- 通过实现Externalizable接口可以自定义序列化的属性,同样,关键字transient也可以达到相同的效果
但是两点区别
- transient修饰的变量,即使使用private修饰也会被序列化
- 如果让private属性不被序列化,则使用Externalizable
- 静态变量不会被序列化
public class StudentEntity implements Serializable {
private static final long serialVersionUID = 1L;
private String studentName;
private static int studentNumber;
}
- 测试
public class Test {
public static void main(String[] args) throws Exception, IOException {
SerializeStudent();
StudentEntity.setStudentNumber(12);
DeSerializeStudent();
}
//序列化方法
private static void SerializeStudent() throws FileNotFoundException, IOException {
StudentEntity.setStudentNumber(13);
StudentEntity studentEntity = new StudentEntity();
studentEntity.setStudentName("Kaleldo");
//序列化对象到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C://Test/Student1"));
oos.writeObject(studentEntity);
oos.close();
System.out.println("序列化对象成功");
}
//反序列化方法
private static void DeSerializeStudent() throws FileNotFoundException, IOException{
File file = new File("C://Test/Student");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
StudentEntity studentEntity = null;
try {
studentEntity = (StudentEntity)ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("反序列化对象成功"+studentEntity.toString());
}
}
- 结果:即使在序列化之前修改了值为13,但是还是输出12,反序列输出的静态变量值变化了:说明静态变量没有被序列化了。
序列化对象成功
Disconnected from the target VM, address: '127.0.0.1:52125', transport: 'socket'
反序列化对象成功StudentEntity{studentName='Kaleldo'studentNumber='12'}
后话
1.实现序列化有两种方式,一实现Serializable接口,二是实现Externalizable接口,Externalizable可以自定义序列化规则
2.static、transient修饰的字段不会被序列化
3.serialVersionUID相当于版本号,如果每次序列化的serialVersionUID不同,将会报序列化出错