您的位置:首頁 > 軟件教程 > 教程 > 為什么static和transient關(guān)鍵字修飾的變量不能被序列化?

為什么static和transient關(guān)鍵字修飾的變量不能被序列化?

來源:好特整理 | 時(shí)間:2024-06-23 18:55:49 | 閱讀:85 |  標(biāo)簽: T SIE Transient 序列 S C 面試 IE   | 分享到:

一、寫在開頭 在上一篇學(xué)習(xí)序列化的文章中我們提出了這樣的一個(gè)問題: “如果在我的對象中,有些變量并不想被序列化應(yīng)該怎么辦呢?” 當(dāng)時(shí)給的回答是:不想被序列化的變量我們可以使用transient或static關(guān)鍵字修飾;transient 關(guān)鍵字的作用是阻止實(shí)例中那些用此關(guān)鍵字修飾的的變量序列化;當(dāng)對

在上一篇學(xué)習(xí)序列化的文章中我們提出了這樣的一個(gè)問題:

“如果在我的對象中,有些變量并不想被序列化應(yīng)該怎么辦呢?”

當(dāng)時(shí)給的回答是:不想被序列化的變量我們可以使用 transient static 關(guān)鍵字修飾;transient 關(guān)鍵字的作用是阻止實(shí)例中那些用此關(guān)鍵字修飾的的變量序列化;當(dāng)對象被反序列化時(shí),被 transient 修飾的變量值不會被持久化和恢復(fù);而static關(guān)鍵字修飾的變量并不屬于對象本身,所以也同樣不會被序列化!

當(dāng)時(shí)沒有解釋具體為什么static和transient 關(guān)鍵字修飾的變量就不能被序列化了,這個(gè)問題實(shí)際上在很多大廠的面試中都可能會被問及。我們今天在這篇中進(jìn)行解釋吧。

案例演示

我們先通過一個(gè)實(shí)戰(zhàn)案例,去看一看用static和transient 關(guān)鍵字修飾后的變量,序列化與反序列化后的現(xiàn)象。

public class TestService {
    public static void main(String[] args) throws IOException {
        //初始化對象信息
        Person person = new Person();
        person.setName("JavaBuild");
        person.setAge(30);
        System.out.println(person.getName()+" "+person.getAge());

        //序列化過程
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("E:\\person.txt"));) {
            objectOutputStream.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }
        person.par1 = "序列化后靜態(tài)字段";
        //反序列化過程
        try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("E:\\person.txt"));) {
            Person p = (Person) objectInputStream.readObject();
            System.out.println(p);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}
class Person implements Serializable{

    private static final long serialVersionUID = 8711922740433840551L;
    private String name;
    private int age;

    public static String par1 = "靜態(tài)字段";
    transient String par2 = "臨時(shí)字段";
    transient int high = 175;

    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;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", par1=" + par1 +
                ", high=" + high +
                ", par2='" + par2 + '\'' +
                '}';
    }
}

在Person類中,我們定義了兩個(gè)正常的屬性,姓名與年齡,同時(shí)呢,我們也分別定義了一個(gè)靜態(tài)字段和兩個(gè)臨時(shí)字段,輸出結(jié)果為:

JavaBuild 30
Person{name='JavaBuild', age=30, par1=序列化后靜態(tài)字段, high=0, par2='null'}

對于使用static關(guān)鍵字修飾的par1來說,在整個(gè)序列化過程中,它并未參與,原因是:我們在序列化與反序列化之間插入了屬性的重新賦值操作,最后輸出中打印出的是最新賦值,說明僅是調(diào)用了實(shí)例對象的屬性值,而不是反序列化的結(jié)果。

而對于transient 關(guān)鍵字修飾high和par2,在序列化時(shí)直接被忽略了。從輸出結(jié)果看就更加的明了了,int類型直接還原為默認(rèn)值0,而String類型直接為null。

什么原因呢?咱們繼續(xù)往下看。

源碼分析

在之前的文章中,我們已經(jīng)解釋過了,在序列化時(shí)Serializable只是作為一種標(biāo)識接口,告訴程序我這個(gè)對象需要序列化,那么真正的實(shí)現(xiàn)還要以來序列化流,比如寫出到文件時(shí),我們需要用到的ObjectOutputStream,它在序列化的時(shí)候會依次調(diào)用 writeObject()→writeObject0()→writeOrdinaryObject()→writeSerialData()→invokeWriteObject()→defaultWriteFields()。

然后最后一步的defaultWriteFields()方法中,會去調(diào)用ObjectStreamClass對象,里面有個(gè)方法為getDefaultSerialFields(),提供了可以被序列化的屬性值。

private static ObjectStreamField[] getDefaultSerialFields(Class cl) {
    // 獲取該類中聲明的所有字段
    Field[] clFields = cl.getDeclaredFields();
    ArrayList list = new ArrayList<>();
    int mask = Modifier.STATIC | Modifier.TRANSIENT;

    // 遍歷所有字段,將非 static 和 transient 的字段添加到 list 中
    for (int i = 0; i < clFields.length; i++) {
        Field field = clFields[i];
        int mods = field.getModifiers();
        if ((mods & mask) == 0) {
            // 根據(jù)字段名、字段類型和字段是否可序列化創(chuàng)建一個(gè) ObjectStreamField 對象
            ObjectStreamField osf = new ObjectStreamField(field.getName(), field.getType(), !Serializable.class.isAssignableFrom(cl));
            list.add(osf);
        }
    }

    int size = list.size();
    // 如果 list 為空,則返回一個(gè)空的 ObjectStreamField 數(shù)組,否則將 list 轉(zhuǎn)換為 ObjectStreamField 數(shù)組并返回
    return (size == 0) ? NO_FIELDS :
        list.toArray(new ObjectStreamField[size]);
}

這段源碼中,定義一個(gè)mask標(biāo)記變量,用于接收訪問修飾符中包含STATIC與TRANSIENT的屬性,并在后面的if判斷中,將這種mask的過濾掉,從而實(shí)現(xiàn)遍歷所有字段,將非 static 和 transient 的字段添加到 list 中。

而這段源碼就證明了,為什么在對象序列化過程中,static和transient不會被序列化!

總結(jié)

好啦,今天針對為什么static和transient關(guān)鍵字修飾的變量不能被序列化進(jìn)行了一個(gè)解釋,下次大家在面試的時(shí)候再被問道就可以這樣回答啦,不過,還有的BT面試官會問transient關(guān)鍵字修飾的變量真的不能被序列化嗎?這個(gè)問題咱們后面繼續(xù)討論哈。

小編推薦閱讀

好特網(wǎng)發(fā)布此文僅為傳遞信息,不代表好特網(wǎng)認(rèn)同期限觀點(diǎn)或證實(shí)其描述。

相關(guān)視頻攻略

更多

掃二維碼進(jìn)入好特網(wǎng)手機(jī)版本!

掃二維碼進(jìn)入好特網(wǎng)微信公眾號!

本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請發(fā)郵件[email protected]

湘ICP備2022002427號-10 湘公網(wǎng)安備:43070202000427號© 2013~2025 haote.com 好特網(wǎng)