一、序列化与反序列化
Java 序列化就是指将对象转换为字节序列的过程,而反序列化则是只将字节序列转换成目标对象的过程。
我们都知道,在进行浏览器访问的时候,我们看到的文本、图片、音频、视频等都是通过二进制序列进行传输的,那么如果我们需要将Java对象进行传输的时候,是不是也应该先将对象进行序列化?答案是肯定的,我们需要先将Java对象进行序列化,然后通过网络,IO进行传输,当到达目的地之后,再进行反序列化获取到我们想要的对象,最后完成通信。
二、如何实现序列化与反序列化
第一种方式:
①目标对象需要先实现 Seriable接口
②使用到JDK中关键类 ObjectOutputStream 和ObjectInputStream 进行序列化和反序列化
ObjectOutputStream 类中:通过使用writeObject(Object object) 方法,将对象以二进制格式进行写入。
ObjectInputStream 类中:通过使用readObject()方法,从输入流中读取二进制流,转换成对象。
public class Student implements Serializable {
private static final long serialVersionUID = 3404072173323892464L;
private String name;
private transient String id;
private String age;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id='" + id + '\'' +
", age='" + age + '\'' +
'}';
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public Student(String name, String id) {
System.out.println("args Constructor");
this.name = name;
this.id = id;
}
public Student() {
System.out.println("none-arg Constructor");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
首先:
1、Serializable 接口的作用只是用来标识我们这个类是需要进行序列化,并且Serializable 接口中并没有提供任何方法。
2、serialVersionUid 序列化版本号的作用是用来区分我们所编写的类的版本,用于判断反序列化时类的版本是否一致,如果不一致会出现版本不一致异常。
3、transient 关键字,主要用来忽略我们不希望进行序列化的变量
public static void main(String[] args){
File file = new File("D:/test.txt");
Student student = new Student("孙悟空","12");
try {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file));
outputStream.writeObject(student);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
Student s = (Student) objectInputStream.readObject();
System.out.println(s.toString());
System.out.println(s.equals(student));
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
outputstream、inputstream如何记忆
不管你从磁盘读,从网络读,或者从键盘读,读到内存,就是InputStream。
不管你写倒磁盘,写到网络,或者写到屏幕,都是OuputStream。
总之记住输入输出是相对于内存而言的就可以了
第二种方式:
Student 实现 Externalnalizable接口 而不实现Serializable 接口
Externaliable 接口是 Serializable 的子类,有着和Serializable接口同样的功能:
public class Student implements Externalizable {
private static final long serialVersionUID = 3404072173323892464L;
private String name;
private transient String id;
private String age;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id='" + id + '\'' +
", age='" + age + '\'' +
'}';
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public Student(String name, String id) {
System.out.println("args Constructor");
this.name = name;
this.id = id;
}
public Student() {
System.out.println("none-arg Constructor");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeObject(id);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
id = (String) in .readObject();
}
}
三、注意点
static代表类的成员、transient代表对象的临时数据,因此被声明为这两种类型的数据成员是不能被序列化的
Q:怎样实现部分序列化
A:①实现Externalizable接口,根据实际需求来实现readExteral、writeExteral来控制序列化和反序列化所使用的属性
②用关键字transient,标记不需要序列化的属性
四、自定义serialVersionUID作用
①提高程序运行效率,如果在类中未显式声明serialVersionUID,则在序列化时需要通过计算得到一个serialVersionUID。显式声明省去了计算过程,提高了运行效率
②提高程序不同平台上的兼容性。各个平台的编译器在计算serialVersionUID时完全有可能采用不同的计算方式,这将会导致在一个平台上序列化的对象在另一个平台上将无法实现反序列化的操作。
③增强程序各个版本的可兼容性
在默认情况下,每个类都有唯一的serialVersionUID,因此,当后期对类进行修改时(例如添加新的属性),类的serialVersionUID会改变,这将导致在修改前对象序列化文件在修改后将无法进行反序列化,通过显示声明serialVersionUID,就不会发生这个问题
五、什么时候用序列化
需要网络来发送对象或对象的状态需要持久化到数据库或文件中