不积跬步,无以至千里;不积小流,无以成江海。
Java语言基础
一、基本概念
1. 序列化与反序列化
序列化指的是对象的序列化,流化,是将对象转换为容易传输的格式的过程
反序列化将从该流重新构造对象。也是对象持久化的一种机制
作用:
序列化:在传递和保存对象时.保证对象的完整性和可传递性。以便在网络上传输或者保存在本地文件中。
反序列化:根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。
总结:核心作用就是对象状态的保存和重建。
2. json/xml的数据传递
在数据传输(也可称为网络传输)前,先通过序列化工具类将Java对象序列化为json/xml文件。
在数据传输(也可称为网络传输)后,再将json/xml文件反序列化为对应语言的对象
3. 序列化算法
将对象实例相关的类元数据输出。
递归地输出类的超类描述直到不再有超类。
类元数据输出完毕后,从最顶端的超类开始输出对象实例的实际数据值。
从上至下递归输出实例的数据。
4. 用途举例
应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中
二、Java实现序列化和反序列化的过程
1. 实现序列化的必备要求
只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列。(不是则会抛出异常)
Externalizable接口继承自 Serializable接口
2. JDK中序列化和反序列化的API
java.io.ObjectInputStream
对象输入流,readObject()方法从输入流中读取字节序列,然后将字节序列反序列化为一个对象并返回。
java.io.ObjectOutputStream
对象输出流,writeObject(Object obj)方法将将传入的obj对象进行序列化,把得到的字节序列写入到目标输出流中进行输出。
3. 对象序列化和反序列化步骤:
3.1 序列化步骤
创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
通过对象输出流的writeObject()方法写对象。
3.2 对象反序列化的步骤如下:
创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
通过对象输入流的readObject()方法读取对象。
4. 示例
定义一个Person类,实现Serializable接口
import java.io.Serializable;
public class Person implements Serializable {
/**序列化ID*/
private static final long serialVersionUID = -5809782578272943999L;
private int age;
private String name;
private String sex;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
}
序列化反序列化Person对象
public class TestObjSerializeAndDeserialize {
public static void main(String[] args) throws Exception {
SerializePerson();//序列化Person对象
Person p = DeserializePerson();//反序列Perons对象
System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
p.getName(), p.getAge(), p.getSex()));
}
private static void SerializePerson() throws FileNotFoundException, IOException {
Person person = new Person();
person.setName("gacl");
person.setAge(25);
person.setSex("男");
// ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
new File("E:/Person.txt")));
oo.writeObject(person);
System.out.println("Person对象序列化成功!");
oo.close();
}
private static Person DeserializePerson() throws Exception, IOException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("E:/Person.txt")));
Person person = (Person) ois.readObject();
System.out.println("Person对象反序列化成功!");
return person;
}
}
4.1 序列化图示
4.2 反序列化图示
三、序列化和反序列化的注意点
3.1 序列化时,只对对象的状态进行保存,而不管对象的方法;
3.2 当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
3.3 声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。
3.4 Java有很多基础类已经实现了serializable接口,比如String,Vector等。但是也有一些没有实现serializable接口的;
四、serialVersionUID的作用
serialVersionUID: 字面意思上是序列化的版本号,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量
private static final long serialVersionUID
实现Serializable接口的类如果类中没有添加serialVersionUID,那么就会出现如下的警告提示
serialVersionUID有两种生成方式:
采用这种方式生成的serialVersionUID是1L,例如:
private static final long serialVersionUID = 1L;
采用这种方式生成的serialVersionUID是根据类名,接口名,方法和属性等来生成的,例如:
private static final long serialVersionUID = 4603642343377807741L;
举例说明serialVersionUID的作用
public class TestSerialversionUID {
public static void main(String[] args) throws Exception {
SerializeCustomer();// 序列化Customer对象
Customer customer = DeserializeCustomer();// 反序列Customer对象
System.out.println(customer);
}
private static void SerializeCustomer() throws FileNotFoundException,
IOException {
Customer customer = new Customer("gacl",25);
// ObjectOutputStream 对象输出流
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
new File("E:/Customer.txt")));
oo.writeObject(customer);
System.out.println("Customer对象序列化成功!");
oo.close();
}
private static Customer DeserializeCustomer() throws Exception, IOException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("E:/Customer.txt")));
Customer customer = (Customer) ois.readObject();
System.out.println("Customer对象反序列化成功!");
return customer;
}
}
class Customer implements Serializable {
//Customer类中没有定义serialVersionUID
private String name;
private int age;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "name=" + name + ", age=" + age;
}
}
运行结果:
序列化和反序列化都成功了。
当修改一下Customer类,添加多一个sex属性,如下:
class Customer implements Serializable {
//Customer类中没有定义serialVersionUID
private String name;
private int age;
//新添加的sex属性
private String sex;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public Customer(String name, int age,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "name=" + name + ", age=" + age;
}
}
抛出如下的异常信息:
Exception in thread "main" java.io.InvalidClassException: Customer;
local class incompatible:
stream classdesc serialVersionUID = -88175599799432325,
local class serialVersionUID = -5182532647273106745
修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。
借鉴博客: https://www.cnblogs.com/xdp-gacl/p/3777987.html
https://blog.csdn.net/tree_ifconfig/article/details/82766587