Java序列化與反序列化
1、什麼是序列化?為什麼要序列化?
Java 序列化就是指將對象轉換為字節序列的過程,而反序列化則是隻將字節序列轉換成目標對象的過程。
我們都知道,在進行瀏覽器訪問的時候,我們看到的文本、圖片、音頻、視頻等都是通過二進製序列進行傳輸的,那麼如果我們需要將Java對象進行傳輸的時候,是不是也應該先將對象進行序列化?答案是肯定的,我們需要先將Java對象進行序列化,然後通過網絡,IO進行傳輸,當到達目的地之後,再進行反序列化獲取到我們想要的對象,最後完成通信。
2、如何實現序列化
2.1、使用到JDK中關鍵類 ObjectOutputStream 和ObjectInputStream
ObjectOutputStream 類中:通過使用writeObject(Object object) 方法,將對象以二進製格式進行寫入。
ObjectInputStream 類中:通過使用readObject()方法,從輸入流中讀取二進製流,轉換成對象。
2.2、目標對象需要先實現 Seriable接口
我們創建一個Student類:
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;
}
}
代碼中Student類實現了Serializable 接口,並且生成了一個版本號:
private static final long serialVersionUID = 3404072173323892464L;
首先:
1、Serializable 接口的作用隻是用來標識我們這個類是需要進行序列化,並且Serializable 接口中並沒有提供任何方法。
2、serialVersionUid 序列化版本號的作用是用來區分我們所編寫的類的版本,用於判斷反序列化時類的版本是否一直,如果不一致會出現版本不一致異常。
3、transient 關鍵字,主要用來忽略我們不希望進行序列化的變量
2.3、將對象進行序列或和反序列化
如果你想學習Java可以來這個群,首先是一二六,中間是五三四,最後是五一九,裏麵有大量的學習資料可以下載。
2.3.1 第一種寫入方式:
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();
}
}
創建對象Student ,然後通過ObjectOutputStream類中的writeObject()方法,將對象輸出到文件中。
然後通過ObjectinputStream 類中的readObject()方法反序列化,獲取對象。
2.3.2 第二種寫入方式:
在Student 類中實現writeObject()和readObject()方法:
private void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
objectOutputStream.defaultWriteObject();
objectOutputStream.writeUTF(id);
}
private void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
objectInputStream.defaultReadObject();
id = objectInputStream.readUTF();
}
通過這中方式進行序列話,我們可以自定義想要進行序列化的變量,將輸入流和輸出流傳入對線實例中,然後進行序列化以及反序列化。
2.3.3 第三種寫入方式:
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();
}
}
通過和前麵的第二種寫入方法對比,我們可以發現他們的實現原理都是十分的類似,不過實現Externalnalizable接口 並不支持第一種序列化方法,它隻能夠通過實現接口中的writeExternal()和readExternal()方法實現對象的序列化。
3、麵試中關於序列化的問題:
1、什麼是序列化,如何實現序列化
java中對象的序列化就是將對象轉換成二進製序列,反序列化則是將二進製序列轉換成對象
Java 實現序列化有多種方式
1、首先需要使用到工具類ObjectInputStream 和ObjectOutputStream 兩個IO類
2、實現Serializable 接口:
有兩種具體序列化方法:
直接通過ObjectOutputStream 和 ObjectInputStream 類中的 writeObject()和readObject()方法
通過在序列化對象中實現writeObject()和readObject()方法,傳入ObjectOutputStream和ObjectInputStream對象,完成序列化
3、實現Externalizable 接口:
隻能夠通過實現接口中的writeExternal()和readExternal()方法實現對象的序列化
2、transient 關鍵字?如何將transient修飾符修飾的變量序列化?
transient 的作用是用來屏蔽我們不希望進行序列化的變量,是對象在進行序列化和反序列話的過程中忽略該變量。我們可以通過上述序列化方法中的 實現writeObject 和readObject 方法,在方法中調用輸出流或輸入流的writeUTF()和readUTF()方法。或者通過實現Externalizable 接口,實現writeExternal()和readExternal()方法,然後再自定義序列話對象。
3、如何保證序列化和反序列化後的對象一致?(如有異議望指正)
對於這個問題我在查閱了一些資料之後,發現並不能保證序列化和反序列化之後的對象是一致的,因為我們在反序列化的過程中,是先創建一個對象,然後再通過對對象進行賦值來完成對象的反序列化,這樣問題就來了,在創建了一個新的對象之後,對象引用和原本的對象並不是指向同一個目標。因此我們隻能保證他們的數據和版本一致,並不能保證對象一致。
最後更新:2017-04-18 10:00:33