Java基礎22--物件序列化和反序列化
Java基礎22–物件的輸出與輸入
物件序列化和反序列化
序列:排隊、
把物件轉為位元組序列,序列化的過程
ObjectOutputStream:用於輸出物件,把物件轉成位元組資料輸出,物件的輸出過程稱為序列化。
ObjectOutputStream比OutputStream多了很多方法,其中一個是 writeObject(obj)
只能將支援 java.io.Serializable 介面的物件寫入流中。每個 serializable 物件的類都被編碼,編碼內容包括類名和類簽名、物件的欄位值和陣列值,以及從初始物件中引用的其他所有物件的閉包。
writeObject 方法用於將物件寫入流中。所有物件(包括 String 和陣列)都可以通過 writeObject 寫入。可將多個物件或基元寫入流中。必須使用與寫入物件時相同的型別和順序從相應 ObjectInputstream 中讀回物件。
ObjectInputstream:用於輸入物件,把位元組序列轉為物件讀取,物件的讀取過程稱為反序列化。
ObjectInputstream比InputStream多了很多方法,其中一個是 Object readObject()
Serializable介面中沒抽象方法,得實現該方法才能ObjectOutputStream輸出
User類
public class User implements Serializable{
private String name;
private String password;
private int age;
}//物件需要實現序列化
@Test
public void test01()throws IOException{
User u = new User("chailinyan","123456",28);
FileOutputStream fos = new FileOutputStream("obj.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//資料:程式-->oos --> fos -->obj.dat
oos.writeObject(u);
/*物件是沒辦法直接變為位元組的,沒有getBytes()方法
* 如果沒有實現Serializable介面的型別,在序列化時,報錯誤NotSerializableException,不能序列化。
* 如果要解決問題,User類需要實現java.io.Serializable介面
*/
oos.close();
fos.close();
}
讀取物件
@Test
public void test02()throws IOException, ClassNotFoundException{
FileInputStream fis = new FileInputStream("obj.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
//把位元組流中的資料,轉為一個物件,讀取過程中會建立物件,new物件時需要找物件的型別,若user.java檔案不存在會報錯找不到類
System.out.println(obj);
ois.close();
fis.close();
}
序列化版本ID:serialVersionUID
將物件寫到.dat檔案中去了,然後User類發生改變,如下:
public class User implements Serializable{
private String name;
private String password;
private int age;
private char gender;
private String address;
}
這時再執行之前讀物件得程式碼
@Test
public void test02()throws IOException, ClassNotFoundException{
FileInputStream fis = new FileInputStream("obj.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
/*
* 當物件已經輸出到檔案中後,修改了類,再次讀取這個檔案時,報InvalidClassException。
* 報這個錯的原因是:流中關於類的serialVersionUID與本地類的serialVersionUID對不上,就會報InvalidClassException錯誤,
* 如何解決?
* (1)修改本地的serialVersionUID為流中的serialVersionUID
* (2)或者,在當初實現Serializable介面時,就固定一個serialVersionUID,這樣每次編譯就不會自動生成一個新的serialVersionUID
*/
Object obj = ois.readObject();
//把位元組流中的資料,轉為一個物件,讀取過程中會建立物件,new物件時需要找物件的型別
System.out.println(obj);
ois.close();
fis.close();
}
報錯:無效的類
流裡面得序列化版本和本地的類得序列化版本對不上,因為改變類後本地序列化版本更新了
** 如何解決?**
(1)修改本地的serialVersionUID為流中的serialVersionUID
(2)或者,在當初實現Serializable介面時,就固定一個serialVersionUID,這樣每次編譯就不會自動生成一個新的serialVersionUID
法一:
修改本地的serialVersionUID為流中的serialVersionUID
再次執行能返回值,gender沒有值是因為之前沒寫
法二:
public class User implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private String password;
private int age;
在當初實現Serializable介面時,就固定一個serialVersionUID(值隨便取,一般加L,因為數字比較大,為long,user實現序列化時類名下有黃色警告線就是因為一般建議固定一個serialVersionUID),這樣每次編譯就不會自動生成一個新的serialVersionUID,一開始就固定一個serialVersionUID,無論怎麼修改都能找到user
回顧:講自定義異常時:
自定義異常時的要求:
(1)繼承Throwable或它的子類
一般是Exception和RuntimeException比較多
(2)建議保留兩個構造器,一個無參,一個可以message賦值的構造器
(3)建議增加serialVersionUID
public class TestException {
}
class MyException extends Exception{
//繼承異常時也會有黃色線提示,建議固定序列化版本ID
//為什麼這個也會這樣警告:因為Exception繼承了Throwable,而Throwable實現了序列化,所以自定義類也要加序列化版本ID,這樣省的自定義異常類更改會導致一些問題,比如像一些異常會記錄到日誌檔案裡,下次可能要從日誌檔案中恢復一些異常資訊。類更改後可能會導致日誌檔案更改不回來,所以在定義異常時,也加入序列化版本ID
private static final long serialVersionUID = 1L;
public MyException() {
super();
}
public MyException(String message) {
super(message);
}
}
不序列的欄位:transient和static
需求:
不是物件中的所有屬性都需要序列化的。
如果某個屬性不需要序列化,可以在屬性的前面加一個關鍵字:transient
如果某個屬性是static,那麼也不會序列化。因為靜態的變數不屬於某個物件,而是整個類的,所以不需要隨著物件的序列化而序列化。
transient
示例:
若是隻想把當時的價格和名稱序列化,但當時的銷量不需要序列化,因為如果反序列化回來以後,銷量應該是目前最新的銷量,跟檔案裡的銷量沒關係,不希望銷量序列化
public class Goods implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private double price;
private transient int sale;//transient表示sale屬性不需要序列化
}
@Test
public void test01()throws IOException{
//現在要序列化這個產品的資訊,但是希望銷量100不序列化
Goods g = new Goods("裙子",88.8,100);
FileOutputStream fos = new FileOutputStream("goods.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(g);
oos.close();
fos.close();
}
讀取物件
@Test
public void test02()throws IOException, ClassNotFoundException{
FileInputStream fis = new FileInputStream("goods.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
System.out.println(obj);
ois.close();
fis.close();
}
讀回來了,銷量是0,因為銷量屬性沒有序列化,所以之前的銷量100是沒有寫進去的,銷量沒有序列化讀出來就預設值0
static
加一個靜態屬性品牌
public class Goods implements Serializable{
private static final long serialVersionUID = 1L;
private static String brand = "尚矽谷";
private String name;
private double price;
private transient int sale;//transient表示sale屬性不需要序列化
物件寫進去再讀出來結果(反序列化結果):
這時序列化時再改品牌這個屬性:
Goods.setBrand("atguigu");
@Test
public void test01()throws IOException{
Goods.setBrand("atguigu");
//現在要序列化這個產品的資訊,但是希望銷量100不序列化
Goods g = new Goods("裙子",88.8,100);
FileOutputStream fos = new FileOutputStream("goods.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(g);
oos.close();
fos.close();
}
屬性的值沒改變,還是和之前的一樣
物件的引用資料型別屬性都要實現Serializable
在序列化Husband物件,要求Wife序列化。
這裡發現String型別也實現序列化介面了。
結論:
序列化一個物件時,要求它的屬性要麼是基本資料型別,如果是引用資料型別,這個引用資料型別也必須實現Serializable介面。
序列化一個陣列,要求元素型別實現Serializable介面。
示例:
public class Husband implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private Wife wife;
public Husband(String name, Wife wife) {
super();
this.name = name;
this.wife = wife;
}
public class Wife {
private String name;
private Husband husband;
public Wife(String name, Husband husband) {
super();
this.name = name;
this.husband = husband;
}
@Test
public void test01()throws Exception {
Husband h = new Husband();
Wife wife = new Wife();
h.setName("崔志恆");
wife.setName("石榴");
h.setWife(wife);
wife.setHusband(h);
FileOutputStream fos = new FileOutputStream("marry.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(h);
/*
* java.io.NotSerializableException: com.atguigu.test10.Wife
* 雖然Husband實現了Serializable,但是因為在序列化Husband過程中,包含wife物件,所以要求Wife類也要實現Serializable介面
*/
oos.close();
fos.close();
}
報錯:java.io.NotSerializableException: com.atguigu.test10.Wife
**雖然Husband實現了Serializable,但是因為在序列化Husband過程中,包含wife物件,所以要求Wife類也要實現Serializable介面**
test2
@Test
public void test02(){
Husband h = new Husband();
Wife wife = new Wife();
h.setName("崔志恆");
wife.setName("石榴");
h.setWife(wife);
wife.setHusband(h);
System.out.println(h);
}
列印husband物件是報錯:StackOverflowError,棧溢位
列印husband物件物件呼叫了toString方法,包含了wife物件,wife物件會呼叫wife的toString方法,wife的toString方法裡又有huaband物件,形成了一個死迴圈
所以toString’方法裡不能直接寫物件,改為husband.getName()和wife.getName()
String也是引用資料型別,它也實現了序列化
相關文章
- [Java基礎]序列化和反序列化Java
- Java基礎——序列化Java
- Java安全基礎之Java序列化與反序列化Java
- java反序列化基礎Java
- java基礎知識-序列化/反序列化-gson基礎知識Java
- Java物件的序列化和反序列化實踐Java物件
- java物件序列化Java物件
- 【Java基礎】序列化與反序列化深入分析Java
- 夯實Java基礎系列22:一文讀懂Java序列化和反序列化Java
- Java基礎知識系列—序列化Java
- 理解Java物件序列化Java物件
- java 物件序列化要序列化那些內容Java物件
- Tinking in Java ---Java的NIO和物件序列化Java物件
- 物件序列化(序列化)物件
- Python中物件序列化和反序列化Python物件
- .NET物件的XML序列化和反序列化物件XML
- 物件(List<T>)的序列化和反序列化物件
- Java的序列化和反序列化Java
- Java物件流與序列化Java物件
- Java:對一個物件序列化和反序列化的簡單實現Java物件
- Java物件的序列化與反序列化-Json篇Java物件JSON
- java培訓教程分享:Java中怎樣將資料物件序列化和反序列化?Java物件
- 一文帶你全面瞭解java物件的序列化和反序列化Java物件
- Java物件表示方式1:序列化、反序列化和transient關鍵字的作用Java物件
- 談談 JAVA 的物件序列化Java物件
- 深入理解Java物件序列化Java物件
- Java 中的 transient 關鍵字和物件序列化Java物件
- JSON 物件序列化、反序列化JSON物件
- 從java的序列化和反序列化說起Java
- Java序列化和hessian序列化的區別Java
- Java序列化、反序列化、反序列化漏洞Java
- JavaScript 物件序列化JavaScript物件
- Java&Android 基礎知識梳理(2) 序列化JavaAndroid
- 《Java工程師成神之路-基礎篇》Java基礎知識——序列化(已完結)Java工程師
- 為什麼Java需要物件的序列化Java物件
- django-rest-framework 基礎二 序列化器和路由DjangoRESTFramework路由
- Python基礎之IO流和序列化講解Python
- Java的序列化與反序列化Java