java中transient關鍵字使用學習

導師讓我學習序列化和反序列化的時候才意識到自己的對java中的transient關鍵字不熟。看了很多介紹和學習筆記之後,對transient關鍵字的使用做一下總結:

如果一個對象只要實現了Serilizable接口,這個類的所有屬性和方法都會自動序列化。

1.什麼情況下需要使用transient關鍵字?

在實際開發過程中,一些類的部分屬性需要序列化,而其他屬性不需要被序列化。

比如說,如果一個用戶有一些敏感信息(如登陸密碼,銀行卡號等),爲了安全起見,不希望在網絡操作中被傳輸,這些信息對應的變量就可以加上transient關鍵字。這樣,被修飾的變量就不會實現序列化。

 實現Serilizable接口,將不需要序列化的屬性前添加關鍵字transient,序列化對象的時候,這個屬性就不會序列化到指定的目的地中。transient僅存於調用者的內存中而不會寫到磁盤裏持久化。

栗子:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
 * @description 使用transient關鍵字不序列化某個變量。注意讀取的時候,讀取數據的順序一定要和存放數據的順序保持一致
 * @author 旺仔牛奶
 * 2017年8月30日 
 */
public class ApplyTransient {

    public static void main(String[] args) {
        User user = new User();
        user.setUserName("旺仔牛奶");
        user.setPasswd("caka");
        
        System.out.println("Before Serializable: ");
        System.out.println("username: " + user.getUserName());
        System.err.println("password: " + user.getPasswd());
        //序列化操作
        try {
           ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("E:/wangzai.txt"));
            os.writeObject(user); // 將User對象寫進文件
            os.flush();//刷新流到文件
            os.close();//關閉流
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            //反序列化操作
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("E:/wangzai.txt"));
            user = (User) is.readObject(); // 從流中讀取User的數據
            is.close();//關閉流
            
            System.out.println("\nAfter Serializable: ");
            System.out.println("username: " + user.getUserName());
            System.err.println("password: " + user.getPasswd());
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
//User實體類
class User implements Serializable {
    private static final long serialVersionUID = 8294180014912103005L;  
    
    public static String userName;
    private transient String passwd;
    
    public String getUserName() {
        return userName;
    }
    
    public void setUserName(String userName) {
        this.userName = userName;
    }
    
    public String getPasswd() {
        return passwd;
    }
    
    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }
}

輸出結果爲:

Before Serializable: 
username: 旺仔牛奶
password: caka

After Serializable: 
username: 旺仔牛奶
password: null
上個例子可以看出:password字段爲null,說明反序列化時根本沒有從文件中獲取到信息。(因爲類的定義中加了transient關鍵字)

2.transient關鍵字的使用總結

  • 一旦變量被transient修飾,變量將不再是對象持久化的一部分,該變量內容在序列化後無法獲得訪問。
  • transient關鍵字只能修飾變量,而不能修飾方法和類。注意,本地變量是不能被transient關鍵字修飾的。變量如果是用戶自定義類變量,則該類需要實現Serializable接口。本地變量指出現在方法中,在方法中定義,在方法中使用,超出方法就不存在的變量)
  • 被transient關鍵字修飾的變量不再能被序列化,一個靜態變量不管是否被transient修飾,均不能被序列化。

上例中的userName屬性被static修飾了,但反序列化後還是讀出來了,就很奇怪,好像是和第三個小點衝突了,其實不是的。這個值是JVM中的。不是反序列化得出的

基於上例,再舉個例子:(在序列化之前臨時改變userName的值)

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
 * @description 使用transient關鍵字不序列化某個變量。注意讀取的時候,讀取數據的順序一定要和存放數據的順序保持一致
 * @author 旺仔牛奶
 * 2017年8月30日 
 */
public class ApplyTransient {

    public static void main(String[] args) {
        User user = new User();
        user.setUserName("旺仔牛奶");
        user.setPasswd("caka");
        
        System.out.println("Before Serializable: ");
        System.out.println("username: " + user.getUserName());
        System.err.println("password: " + user.getPasswd());
        //序列化操作
        try {
           ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("E:/wangzai.txt"));
            os.writeObject(user); // 將User對象寫進文件
            os.flush();//刷新流到文件
            os.close();//關閉流
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            // 在反序列化之前改變username的值
            User.userName = "旺仔牛奶的牛奶";
            //反序列化操作
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("E:/wangzai.txt"));
            user = (User) is.readObject(); // 從流中讀取User的數據
            is.close();//關閉流
            
            System.out.println("\nAfter Serializable: ");
            System.out.println("username: " + user.getUserName());
            System.err.println("password: " + user.getPasswd());
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
//User實體類
class User implements Serializable {
    private static final long serialVersionUID = 8294180014912103005L;  
    
    public static String userName;
    private transient String passwd;
    
    public String getUserName() {
        return userName;
    }
    
    public void setUserName(String userName) {
        this.userName = userName;
    }
    
    public String getPasswd() {
        return passwd;
    }
    
    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }
}
輸出結果爲:

Before Serializable: 
username: 旺仔牛奶
password: caka

After Serializable: 
username: 旺仔牛奶的牛奶
password: null

發現userName的值並不是序列化之前的值,而是臨時修改的值。所以可見被static修飾的變量確實沒有沒序列化,是JVM中的值對應的static變量的值

3.transient關鍵字使用的注意點

是否只要使用transient關鍵字就都不序列化?

看下面的例子:

import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
/**
 * @description 使用transient關鍵字不序列化某個變量。注意讀取的時候,讀取數據的順序一定要和存放數據的順序保持一致
 * @author 旺仔牛奶
 * 2017年8月30日 
 */
public class ApplyTransient implements Externalizable {

    private transient String content = "旺仔牛奶能被實例化嗎";
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(content);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
        content = (String) in.readObject();
    }
    public static void main(String[] args) throws Exception {
        ApplyTransient applyTransientt = new ApplyTransient();
        //序列化
        ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File("wangzai")));
        out.writeObject(applyTransientt);
        //反序列化
        ObjectInput in = new ObjectInputStream(new FileInputStream(new File("wangzai")));
        applyTransientt = (ApplyTransient) in.readObject();
        System.out.println(applyTransientt.content);
        out.close();
        in.close();
    }

}
執行結果爲:

旺仔牛奶能被實例化嗎

這是爲什麼呢,不是說類的變量被transient關鍵字修飾以後將不能序列化了嗎?

在Java中,對象的序列化可以通過實現兩種接口來實現,一種是Serializable接口,則所有的序列化將會自動進行,若實現的是Externalizable接口,則沒有任何東西可以自動序列化,需要在writeExternal方法中進行手工指定所要序列化的變量,這與是否被transient修飾無關。因此第二個例子輸出的是變量content初始化的內容。





發佈了33 篇原創文章 · 獲贊 51 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章