transient關(guān)鍵字作用是什么彩掐?

1.從Serilizable說(shuō)到transient

我們知道堵幽,如果一個(gè)對(duì)象需要序列化朴下,那么需要實(shí)現(xiàn)Serilizable接口殴胧,那么這個(gè)類(lèi)的所有非靜態(tài)屬性佩迟,都會(huì)被序列化报强。

注意:上面說(shuō)的是非靜態(tài)屬性秉溉,因?yàn)殪o態(tài)屬性是屬于類(lèi)的坚嗜,而不是屬于類(lèi)對(duì)象的诗充,而序列化是針對(duì)類(lèi)對(duì)象的操作蝴蜓,所以這個(gè)根本不會(huì)序列化。下面我們可以實(shí)驗(yàn)一下:
實(shí)體類(lèi)Teacher.class:

import java.io.Serializable;

class Teacher implements Serializable {
    public int age;
    public static String SchoolName;

    public Teacher(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                '}';
    }
}

測(cè)試代碼SerialTest.java,基本思路就是初始化的時(shí)候押袍,靜態(tài)屬性SchoolName為"東方小學(xué)",序列化對(duì)象之后谊惭,將靜態(tài)屬性修改侮东,然后,反序列化,發(fā)現(xiàn)其實(shí)靜態(tài)變量還是修改之后的宽闲,說(shuō)明靜態(tài)變量并沒(méi)有被序列化娩梨。

import java.io.*;

public class SerialTest {
    public static void main(String[] args) {
        Teacher.SchoolName = "東方小學(xué)";
        serial();
        Teacher.SchoolName = "西方小學(xué)";
        deserial();
        System.out.println(Teacher.SchoolName);
    }
    // 序列化
    private static void serial(){
        try {
            Teacher teacher = new Teacher(9);
            FileOutputStream fileOutputStream = new FileOutputStream("Teacher.txt");
            ObjectOutputStream objectOutputStream= new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(teacher);
            objectOutputStream.flush();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
    // 反序列化
    private static void deserial() {
        try {
            FileInputStream fis = new FileInputStream("Teacher.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Teacher teacher = (Teacher) ois.readObject();
            ois.close();
            System.out.println(teacher.toString());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

輸出的結(jié)果吱殉,證明靜態(tài)變量沒(méi)有被序列化8迨=忍佟涕俗!

Teacher{age=9}
西方小學(xué)

2.序列化屬性對(duì)象的類(lèi)需要實(shí)現(xiàn)Serilizable接口再姑?

突然想到一個(gè)問(wèn)題元镀,如果有些屬性是對(duì)象,而不是基本類(lèi)型讨永,需不需要改屬性的類(lèi)型也實(shí)現(xiàn)Serilizable呢卿闹?

問(wèn)題的答案是:需要1仍8苎病氢拥!

下面是實(shí)驗(yàn)過(guò)程:

首先嫩海,有一個(gè)Teacher.java,實(shí)現(xiàn)了Serializable,里面有一個(gè)屬性是School類(lèi)型:

import java.io.Serializable;

class Teacher implements Serializable {
    public int age;

    public School school;
    public Teacher(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                '}';
    }
}

School類(lèi)型,不實(shí)現(xiàn)Serializable:


public class School {
    public String name;

    public School(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                '}';
    }
}

測(cè)試代碼叁怪,我們只測(cè)試序列化:

import java.io.*;

public class SerialTest {
    public static void main(String[] args) {
        serial();
    }

    private static void serial(){
        try {
            Teacher teacher = new Teacher(9);
            teacher.school = new School("東方小學(xué)");
            FileOutputStream fileOutputStream = new FileOutputStream("Teacher.txt");
            ObjectOutputStream objectOutputStream= new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(teacher);
            objectOutputStream.flush();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

}

會(huì)發(fā)現(xiàn)報(bào)錯(cuò)了涣觉,報(bào)錯(cuò)的原因是:School不能被序列化官册,也就是沒(méi)有實(shí)現(xiàn)序列化接口难捌,所以如果我們想序列化一個(gè)對(duì)象根吁,那么這個(gè)對(duì)象的屬性也必須是可序列化的击敌,或者它是transient修飾的愚争。

java.io.NotSerializableException: com.aphysia.transienttest.School
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at com.aphysia.transienttest.SerialTest.serial(SerialTest.java:18)
    at com.aphysia.transienttest.SerialTest.main(SerialTest.java:9)

當(dāng)我們將School實(shí)現(xiàn)序列化接口的時(shí)候轰枝,發(fā)現(xiàn)一切就正常了...問(wèn)題完美解決

3.不想被序列化的字段怎么辦?

但是如果有一個(gè)變量不是靜態(tài)變量步淹,但是我們也不想序列化它缭裆,因?yàn)樗赡苁且恍┟艽a等敏感的字段澈驼,或者它是不那么重要的字段筛武,我們不希望增加報(bào)文大小,所以想在序列化報(bào)文中排除該字段内边∧洌或者改字段存的是引用地址竿音,不是真正重要的數(shù)據(jù)春瞬,比如ArrayList里面的elementData

這個(gè)時(shí)候就需要使用transient 關(guān)鍵字颠印,將改字段屏蔽线罕。

當(dāng)我們用transient修飾School的時(shí)候:

import java.io.Serializable;

class Teacher implements Serializable {
    public int age;

    public transient School school;
    public Teacher(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "age=" + age +
                ", school=" + school +
                '}';
    }
}
import java.io.Serializable;

public class School implements Serializable {
    public String name;

    public School(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                '}';
    }
}

執(zhí)行下面序列化和反序列化的代碼:

import java.io.*;

public class SerialTest {
    public static void main(String[] args) {
        serial();
        deserial();
    }

    private static void serial(){
        try {
            Teacher teacher = new Teacher(9);
            teacher.school = new School("東方小學(xué)");
            FileOutputStream fileOutputStream = new FileOutputStream("Teacher.txt");
            ObjectOutputStream objectOutputStream= new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(teacher);
            objectOutputStream.flush();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    private static void deserial() {
        try {
            FileInputStream fis = new FileInputStream("Teacher.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Teacher teacher = (Teacher) ois.readObject();
            ois.close();
            System.out.println(teacher.toString());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

執(zhí)行結(jié)果如下袄琳,可以看到teacher字段反序列化出來(lái)刻蟹,其實(shí)是null,這也是transient起作用了。
但是注意召调,transient只能修飾變量蛮浑,但是不能修飾類(lèi)和方法,

4.ArrayList里面的elementData都被transient 關(guān)鍵字修飾了,為什么ArrayList還可以序列化呢剩拢?

這里提一下,既然transient修飾了ArrayList的數(shù)據(jù)節(jié)點(diǎn)办素,那么為什么序列化的時(shí)候我們還是可以看到ArrayList的數(shù)據(jù)節(jié)點(diǎn)呢?
這是因?yàn)樾蛄谢臅r(shí)候:

如果僅僅實(shí)現(xiàn)了Serializable接口,那么序列化的時(shí)候商源,肯定是調(diào)用java.io.ObjectOutputStream.defaultWriteObject()方法,將對(duì)象序列化充甚。然后如果是transient修飾了該屬性盈蛮,肯定該屬性就不能序列化。
但是,如果我們雖然實(shí)現(xiàn)了Serializable接口,也transient修飾了該屬性,該屬性確實(shí)不會(huì)在默認(rèn)的java.io.ObjectOutputStream.defaultWriteObject()方法里面被序列化了我磁,但是我們可以重寫(xiě)一個(gè)writeObject()方法,這樣一來(lái),序列化的時(shí)候調(diào)用的就是writeObject()存谎,而不是java.io.ObjectOutputStream.defaultWriteObject()

下面的源碼是ObjectInputStream.writeObject(Object obj),里面底層其實(shí)會(huì)有反射的方式調(diào)用到重寫(xiě)的對(duì)象的writeObject()方法,這里不做展開(kāi)诫钓。

    public final void writeObject(Object obj) throws IOException {
        // 如果可以被重寫(xiě),那么就會(huì)調(diào)用重寫(xiě)的方法
        if (enableOverride) {
            writeObjectOverride(obj);
            return;
        }
        try {
            writeObject0(obj, false);
        } catch (IOException ex) {
            if (depth == 0) {
                writeFatalException(ex);
            }
            throw ex;
        }
    }

ArrayList重寫(xiě)的writeOject()方法如下:

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        // 默認(rèn)的序列化對(duì)象的方法
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {
            // 序列化對(duì)象的值
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

我們可以看到绪杏,writeOject()里面其實(shí)在里面調(diào)用了默認(rèn)的方法defaultWriteObject()defaultWriteObject()底層其實(shí)是調(diào)用改了writeObject0()履因。ArrayList重寫(xiě)的writeOject()的思路主要是先序列化默認(rèn)的,然后序列化數(shù)組大小,再序列化數(shù)組elementData里面真實(shí)的元素端逼。這就達(dá)到了序列化元素真實(shí)內(nèi)容的目的顶滩。

5.除了transient,有沒(méi)有其他的方式仅醇,可以屏蔽反序列化节预?

且慢,問(wèn)出這個(gè)問(wèn)題,答案肯定是有的9亍!王浴!那就是Externalizable接口氓辣。

具體情況:Externalizable意思就是几蜻,類(lèi)里面有很多很多屬性,但是我只想要一部分絮吵,要屏蔽大部分,那么我不想在大部分的屬性前面加關(guān)鍵字transient,我只想標(biāo)識(shí)一下自己序列化的字段澄暮,這個(gè)時(shí)候就需要使用Externalizable接口。

show me the code!

首先定義一個(gè)Person.java,里面有三個(gè)屬性

  • age:年齡
  • name:名字(被transient修飾)
  • score:分?jǐn)?shù)

實(shí)現(xiàn)了Externalizable接口自娩,就必須實(shí)現(xiàn)writeExternal()readExternal()方法碎乃。

  • writeExternal:將需要序列化的屬性進(jìn)行自定義序列化
  • readExternal:將需要反序列化的屬性進(jìn)行自定義反序列化
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class Person implements Externalizable {
    public int age;
    public transient String name;
    public int score;

    // 必須實(shí)現(xiàn)無(wú)參構(gòu)造器
    public Person() {
    }

    public Person(int age, String name, int score) {
        this.age = age;
        this.name = name;
        this.score = score;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        /*
         * 指定序列化時(shí)候?qū)懭氲膶傩浴_@里不寫(xiě)入score
         */
        out.writeObject(age);
        out.writeObject(name);
        out.writeObject(score);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        /*
         * 指定序列化時(shí)候?qū)懭氲膶傩浴_@里仍然不寫(xiě)入年齡
         */
        this.age = (int)in.readObject();
        this.name = (String)in.readObject();
    }

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

上面的代碼,我們可以看出苛白,序列化的時(shí)候,將三個(gè)屬性都寫(xiě)進(jìn)去了,但是反序列化的時(shí)候禾锤,我們僅僅還原了兩個(gè),那么我們來(lái)看看測(cè)試的代碼:

import java.io.*;

public class ExternalizableTest {
    public static void main(String[] args) {
        serial();
        deserial();
    }

    private static void serial(){
        try {
            Person person = new Person(9,"Sam",98);
            FileOutputStream fileOutputStream = new FileOutputStream("person.txt");
            ObjectOutputStream objectOutputStream= new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(person);
            objectOutputStream.flush();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    private static void deserial() {
        try {
            FileInputStream fis = new FileInputStream("person.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Person person = (Person) ois.readObject();
            ois.close();
            System.out.println(person);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

測(cè)試結(jié)果如下,就可以發(fā)現(xiàn)其實(shí)前面兩個(gè)都反序列化成功了,后面那個(gè)是因?yàn)槲覀冎貙?xiě)的時(shí)候逼争,沒(méi)有自定義該屬性的反序列化移层,所以沒(méi)有是正常的啦...

Person{age=9, name='Sam', score='0'}

如果細(xì)心點(diǎn),可以發(fā)現(xiàn),有一個(gè)字段是transient修飾的,不是說(shuō)修飾了尼变,就不會(huì)被序列化么哀澈,怎么序列化出來(lái)了膨报。

沒(méi)錯(cuò),只要實(shí)現(xiàn)了Externalizable接口够吩,其實(shí)就不會(huì)被transient左右了强法,只會(huì)按照我們自定義的字段進(jìn)行序列化和反序列化,這里的transient是無(wú)效的...

關(guān)于序列化的transient暫時(shí)到這,keep going~

【作者簡(jiǎn)介】
秦懷,公眾號(hào)【秦懷雜貨店】作者,技術(shù)之路不在一時(shí),山高水長(zhǎng)识埋,縱使緩慢零渐,馳而不息诵盼。這個(gè)世界希望一切都很快风宁,更快戒财,但是我希望自己能走好每一步,寫(xiě)好每一篇文章狼纬,期待和你們一起交流。

此文章僅代表自己(本菜鳥(niǎo))學(xué)習(xí)積累記錄冈欢,或者學(xué)習(xí)筆記盈简,如有侵權(quán)柠贤,請(qǐng)聯(lián)系作者核實(shí)刪除臼勉。人無(wú)完人,文章也一樣瓢谢,文筆稚嫩,在下不才,勿噴,如果有錯(cuò)誤之處,還望指出础倍,感激不盡~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌笨奠,老刑警劉巖腺兴,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栈拖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)檀夹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事⌒孕郏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮叠必,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好臣咖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布铆农。 她就那樣靜靜地躺著夷狰,像睡著了一般瘫证。 火紅的嫁衣襯著肌膚如雪坑赡。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,488評(píng)論 1 302
  • 那天黍图,我揣著相機(jī)與錄音修械,去河邊找鬼貌亭。 笑死涂屁,一個(gè)胖子當(dāng)著我的面吹牛竖般,可吹牛的內(nèi)容都是我干的丈屹。 我是一名探鬼主播宛渐,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼借笙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谦纱!你這毒婦竟也來(lái)了梦重?” 一聲冷哼從身側(cè)響起沛膳,我...
    開(kāi)封第一講書(shū)人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柱查,沒(méi)想到半個(gè)月后雹熬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雕崩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鲤看,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嚷闭,到底是詐尸還是另有隱情吵聪,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布千扶,位于F島的核電站来涨,受9級(jí)特大地震影響社裆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜趴梢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧钢猛,春花似錦公你、人聲如沸溉躲。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蚀狰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刹勃。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工咕晋, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓念祭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容