java序列化想必大家都已经或多或少听说过了,那么听起来如此专业的词汇究竟是做了什么事情呢,在实际中又如何通过代码来实现?在这一篇文章中,我就将我所学到的东西全部记录下来,与大家共享,希望对大家有帮助。
1. Java序列化是什么
Java序列化其实就是,将JVM内存中的Java对象转换为字节序列形式的过程,同理反序列化就是将字节序列还原为Java对象的过程
当我们清楚了什么是Java序列化和反序列化后,就会自然而然的明白其应用的场景。因为JVM内存中对象的生命周期可能达不到我们的要求,随着进程的宕机,内存中数据也会随之丢失,普通的数据我们都是通过数据库或者磁盘文件的形式进行持久化保存。但是对象无法通过这些方式保存,所以序列化的功能就填补了这一空白。我们可以将对象经过序列化转换为字节序列的形式,这些字节序列我们可以根据需要进行持久化保存到磁盘文件中,或者存入数据库,当然还有最重要的用处就是通过远程通信发送给其他Java进程。
2. transient关键字
当某个字段被声明为transient后,默认序列化机制就会忽略该字段。同时,static修饰的静态变量也不会被序列化,在接下来的例子中都会详细展示。
3.如何实现Java序列化
只有实现了Serializable或者Externalizable接口的对象才可以进行序列化,使用 java.io.ObjectOutputStream的writeObject()方法进行序列化,使用java.io.ObjectInputStream的readObject()方法进行反序列化。我们通过简单的代码示例来看下这几个实现方法的区别。
首先定义一个对象Person类。
1)仅实现Serializable接口
所有的序列化会自动进行,ObjectOutputStream/ObjcetInputStream采用默认的序列化方式/反序列化方式,对对象的非transient的实例变量进行序列化/反序列化。
示例
首先定义一个对象Person类,其中age使用transient关键字修饰,country是静态变量:
import java.io.Serializable;
public class Person implements Serializable{
private String name;
//使用transient关键字修饰的变量会在序列化时自动忽略
private transient Integer age;
private String sex;
//静态变量不会被序列化
private static String country;
public Person(String name, Integer age, String sex, String country) {
this.name = name;
this.age = age;
this.sex = sex;
this.country = country;
}
public void print() {
System.out.println("name:" + name + ",age:" + age + ",sex:" + sex +",country:" + country);
}
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 getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public static String getCountry() {
return country;
}
public static void setCountry(String country) {
Person.country = country;
}
}
序列化与反序列化实现代码:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Serialize {
public static void main(String[] args) throws Exception{
//创建对象
Person person = new Person("Roy",23,"Male","China");
System.out.println("序列化前对象数据:");
person.print();
//创建文件并将序列化后的字节保存到文件中
File file = new File("D://person.out");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
out.writeObject(person);
out.close();
//此时将对象中静态变量country变更为USA
person.setCountry("USA");
//读取文件中字节内容并进行反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Person origPerson = (Person) in.readObject(); // 强制转换到Person类型
in.close();
System.out.println("反序列化后得到新对象数据:");
origPerson.print();
}
}
执行结果如下:
序列化前对象数据:
name:Roy,age:23,sex:Male,country:China
反序列化后得到新对象数据:
name:Roy,age:null,sex:Male,country:USA
由此可以看出transient修饰的变量不会进行序列化,静态变量因为存放内存区域不同,也不会被序列化。
2)既实现Serializable接口,又实现了writeObject(ObjectOutputSteam out)和readObject(ObjectInputStream in)方法的对象
则采用以下方式进行序列化与反序列化,
ObjectOutputStream/ObjcetInputStream会使用自定义writeObject()/readObject()方法,对对象实例变量进行序列化/反序列化。
示例
首先定义一个对象Person类,其中age使用transient关键字修饰,country是静态变量,其中实现了writeObject()/readObject()方法:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Person implements Serializable{
private String name;
//使用transient关键字修饰的变量会在序列化时自动忽略
private transient Integer age;
private String sex;
//静态变量不会被序列化
private static String country;
public Person(String name, Integer age, String sex, String country) {
this.name = name;
this.age = age;
this.sex = sex;
this.country = country;
}
public void print() {
System.out.println("name:" + name + ",age:" + age + ",sex:" + sex +",country:" + country);
}
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 getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public static String getCountry() {
return country;
}
public static void setCountry(String country) {
Person.country = country;
}
//实现了writeObject方法,只将age属性序列化
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeInt(age);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
age = in.readInt();
}
}
序列化与反序列化实现代码同上
执行结果如下:
序列化前对象数据:
name:Roy,age:23,sex:Male,country:China
反序列化后得到新对象数据:
name:null,age:23,sex:null,country:USA
此时因为走的是我们自己实现的writeObject()/readObject()方法,所以而我们的方法只将age属性进行了序列化与反序列化,即便它是transient关键字修饰的属性也没有关系,同时静态变量依然不受影响。
3)仅实现Externalnalizable接口
对象内必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,ObjectOutputStream/ObjcetInputStream会使用自定义writeExternal()/readExternal()方法,对对象实例变量进行序列化/反序列化。
示例
首先定义一个对象Person_e类实现Externalnalizable接口,其中age使用transient关键字修饰,country是静态变量,其中必须实现writeExternal()/readExternal()方法:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
public class Person_e implements Externalizable{
private String name;
private transient Integer age;
private String sex;
private static String country;
public Person_e() {
}
public Person_e(String name, Integer age, String sex, String country) {
this.name = name;
this.age = age;
this.sex = sex;
this.country = country;
}
public void print() {
System.out.println("name:" + name + ",age:" + age + ",sex:" + sex +",country:" + country);
}
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 getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public static String getCountry() {
return country;
}
public static void setCountry(String country) {
Person_e.country = country;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeBytes(name);;
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readLine();
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Externalize {
public static void main(String[] args) throws Exception{
//创建对象
Person_e person = new Person_e("Roy",23,"Male","China");
System.out.println("序列化前对象数据:");
person.print();
//创建文件并将序列化后的字节保存到文件中
File file = new File("D://person.out");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
out.writeObject(person);
out.close();
//此时将对象中静态变量country变更为USA
person.setCountry("USA");
//读取文件中字节内容并进行反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Person_e origPerson = (Person_e) in.readObject(); // 强制转换到Person_e类型
in.close();
System.out.println("反序列化后得到新对象数据:");
origPerson.print();
}
}
执行结果如下:
序列化前对象数据:
name:Roy,age:23,sex:Male,country:China
反序列化后得到新对象数据:
name:Roy,age:null,sex:null,country:USA
此时因为走的是我们自己实现的writeExternal()/readExternal()方法,所以而我们的方法只将name属性进行了序列化与反序列化,同时静态变量依然不受影响。
4.总结
在这里我们展示了Java对象序列化与反序列化的使用方法,使用文件进行持久化的保存,但是在实际情况中,最常用的方式是将学序列化后的字节与其他Java进程通讯,实现Java对象的传递,具体的使用请大家自己研究吧,谢谢~