亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java基礎(chǔ)之序列化與反序列化詳解

 更新時(shí)間:2021年04月19日 15:07:57   作者:程序員孫大圣  
這篇文章主要介紹了Java基礎(chǔ)之序列化與反序列化詳解,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很好的幫助,需要的朋友可以參考下

1、什么是序列化與反序列化?

序列化 (Serialization)是將對象的狀態(tài)信息轉(zhuǎn)換為可以存儲或傳輸?shù)男问降倪^程。一般將一個對象存儲至一個儲存媒介,例如檔案或是記億體緩沖等。在網(wǎng)絡(luò)傳輸過程中,可以是字節(jié)或是XML等格式。而字節(jié)的或XML編碼格式可以還原完全相等的對象。這個相反的過程又稱為反序列化。

2、Java如何實(shí)現(xiàn)序列化和反序列化?

Java對象的序列化和反序列化

在Java中,我們可以通過多種方式來創(chuàng)建對象,并且只要對象沒有被回收我們都可以復(fù)用該對象。但是,我們創(chuàng)建出來的這些Java對象都是存在于JVM的堆內(nèi)存中的。只有JVM處于運(yùn)行狀態(tài)的時(shí)候,這些對象才可能存在。一旦JVM停止運(yùn)行,這些對象的狀態(tài)也就隨之而丟失了。

但是在真實(shí)的應(yīng)用場景中,我們需要將這些對象持久化下來,并且能夠在需要的時(shí)候把對象重新讀取出來。Java的對象序列化可以幫助我們實(shí)現(xiàn)該功能。

對象序列化機(jī)制(object serialization)是Java語言內(nèi)建的一種對象持久化方式,通過對象序列化,可以把對象的狀態(tài)保存為字節(jié)數(shù)組,并且可以在有需要的時(shí)候?qū)⑦@個字節(jié)數(shù)組通過反序列化的方式再轉(zhuǎn)換成對象。對象序列化可以很容易的在JVM中的活動對象和字節(jié)數(shù)組(流)之間進(jìn)行轉(zhuǎn)換。

在Java中,對象的序列化與反序列化被廣泛應(yīng)用到RMI(遠(yuǎn)程方法調(diào)用)及網(wǎng)絡(luò)傳輸中。

在Java中,只要一個類實(shí)現(xiàn)了java.io.Serializable接口,那么它就可以被序列化。這里先來一段代碼:

code 1 創(chuàng)建一個User類,用于序列化及反序列化

public class User implements Serializable{
    private String name;
    private int age;
    private Date birthday;
    private transient String gender;
    private static final long serialVersionUID = -6849794470754667710L;
 
    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;
    }
 
    public Date getBirthday() {
        return birthday;
    }
 
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
 
    public String getGender() {
        return gender;
    }
 
    public void setGender(String gender) {
        this.gender = gender;
    }
 
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", birthday=" + birthday +
                '}';
    }
}

code 2 對User進(jìn)行序列化及反序列化的Demo 

public class SerializableDemo {
 
    public static void main(String[] args) {
        //Initializes The Object
        User user = new User();
        user.setName("hollis");
        user.setGender("male");
        user.setAge(23);
        user.setBirthday(new Date());
        System.out.println(user);
 
        //Write Obj to File
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
            oos.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(oos);
        }
 
        //Read Obj from File
        File file = new File("tempFile");
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(file));
            User newUser = (User) ois.readObject();
            System.out.println(newUser);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(ois);
            try {
                FileUtils.forceDelete(file);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
 
    }
}
//output 
//User{name='hollis', age=23, gender=male, birthday=Tue Feb 02 17:37:38 CST 2016}
//User{name='hollis', age=23, gender=null, birthday=Tue Feb 02 17:37:38 CST 2016}

1、在Java中,只要一個類實(shí)現(xiàn)了java.io.Serializable接口,那么它就可以被序列化。

2、通過ObjectOutputStreamObjectInputStream對對象進(jìn)行序列化及反序列化

3、虛擬機(jī)是否允許反序列化,不僅取決于類路徑和功能代碼是否一致,一個非常重要的一點(diǎn)是兩個類的序列化 ID 是否一致(就是 private static final long serialVersionUID

4、序列化并不保存靜態(tài)變量。

5、要想將父類對象也序列化,就需要讓父類也實(shí)現(xiàn)Serializable 接口。

6、Transient 關(guān)鍵字的作用是控制變量的序列化,在變量聲明前加上該關(guān)鍵字,可以阻止該變量被序列化到文件中,在被反序列化后,transient 變量的值被設(shè)為初始值,如 int 型的是 0,對象型的是 null。

7、服務(wù)器端給客戶端發(fā)送序列化對象數(shù)據(jù),對象中有一些數(shù)據(jù)是敏感的,比如密碼字符串等,希望對該密碼字段在序列化時(shí),進(jìn)行加密,而客戶端如果擁有解密的密鑰,只有在客戶端進(jìn)行反序列化時(shí),才可以對密碼進(jìn)行讀取,這樣可以一定程度保證序列化對象的數(shù)據(jù)安全。

3、如何自定義序列化和反序列化呢?

code 3

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;
    transient Object[] elementData; // non-private to simplify nested class access
    private int size;
}

帶著這個問題,我們來看java.util.ArrayList的源碼從上面的代碼中可以知道ArrayList實(shí)現(xiàn)了java.io.Serializable接口,那么我們就可以對它進(jìn)行序列化及反序列化。因?yàn)閑lementData是transient的,所以我們認(rèn)為這個成員變量不會被序列化而保留下來。我們寫一個Demo,驗(yàn)證一下我們的想法:

code4

public static void main(String[] args) throws IOException, ClassNotFoundException {
        List<String> stringList = new ArrayList<String>();
        stringList.add("hello");
        stringList.add("world");
        stringList.add("hollis");
        stringList.add("chuang");
        System.out.println("init StringList" + stringList);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("stringlist"));
        objectOutputStream.writeObject(stringList);
 
        IOUtils.close(objectOutputStream);
        File file = new File("stringlist");
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
        List<String> newStringList = (List<String>)objectInputStream.readObject();
        IOUtils.close(objectInputStream);
        if(file.exists()){
            file.delete();
        }
        System.out.println("new StringList" + newStringList);
    }
//init StringList[hello, world, hollis, chuang]
//new StringList[hello, world, hollis, chuang]

了解ArrayList的人都知道,ArrayList底層是通過數(shù)組實(shí)現(xiàn)的。那么數(shù)組elementData其實(shí)就是用來保存列表中的元素的。通過該屬性的聲明方式我們知道,他是無法通過序列化持久化下來的。那么為什么code 4的結(jié)果卻通過序列化和反序列化把List中的元素保留下來了呢? 

4、writeObject和readObject方法

在ArrayList中定義了來個方法: writeObjectreadObject。

這里先給出結(jié)論:

在序列化過程中,如果被序列化的類中定義了writeObject 和 readObject 方法,虛擬機(jī)會試圖調(diào)用對象類里的 writeObject 和 readObject 方法,進(jìn)行用戶自定義的序列化和反序列化。

如果沒有這樣的方法,則默認(rèn)調(diào)用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。

用戶自定義的 writeObject 和 readObject 方法可以允許用戶控制序列化的過程,比如可以在序列化的過程中動態(tài)改變序列化的數(shù)值。

那么為什么ArrayList要用這種方式來實(shí)現(xiàn)序列化呢? 

ArrayList實(shí)際上是動態(tài)數(shù)組,每次在放滿以后自動增長設(shè)定的長度值,如果數(shù)組自動增長長度設(shè)為100,而實(shí)際只放了一個元素,那就會序列化99個null元素。為了保證在序列化的時(shí)候不會將這么多null同時(shí)進(jìn)行序列化,ArrayList把元素?cái)?shù)組設(shè)置為transient。

前面說過,為了防止一個包含大量空對象的數(shù)組被序列化,為了優(yōu)化存儲,所以,ArrayList使用transient來聲明elementData。 但是,作為一個集合,在序列化過程中還必須保證其中的元素可以被持久化下來,所以,通過重寫writeObject 和 readObject方法的方式把其中的元素保留下來。

writeObject方法把elementData數(shù)組中的元素遍歷的保存到輸出流(ObjectOutputStream)中。

readObject方法從輸入流(ObjectInputStream)中讀出對象并保存賦值到elementData數(shù)組中

至此,我們先試著來回答剛剛提出的問題:

如何自定義的序列化和反序列化策略

答:可以通過在被序列化的類中增加writeObject 和 readObject方法。那么問題又來了:

雖然ArrayList中寫了writeObject 和 readObject 方法,但是這兩個方法并沒有顯示的被調(diào)用啊。

那么如果一個類中包含writeObject 和 readObject 方法,那么這兩個方法是怎么被調(diào)用的呢?

在使用ObjectOutputStream的writeObject方法和ObjectInputStream的readObject方法時(shí),會通過反射的方式調(diào)用。

5、serializable接口

類通過實(shí)現(xiàn) java.io.Serializable 接口以啟用其序列化功能。未實(shí)現(xiàn)此接口的類將無法使其任何狀態(tài)序列化或反序列化??尚蛄谢惖乃凶宇愋捅旧矶际强尚蛄谢摹?strong>序列化接口沒有方法或字段,僅用于標(biāo)識可序列化的語義。

該接口并沒有字段和方法,為什么只有實(shí)現(xiàn)了該接口的對象才能被序列化呢?

在進(jìn)行序列化操作時(shí),會判斷要被序列化的類是否是Enum、Array和Serializable類型,如果不是則直接拋出NotSerializableException。

序列化ID:

private static final long serialVersionUID = 1L;

序列化 ID在 IDE 下提供了兩種生成策略,一個設(shè)為固定的 1L,另一個是隨機(jī)生成一個不重復(fù)的 long 類型數(shù)據(jù)(實(shí)際上是使用 JDK 工具生成)。一般如果沒有特殊需求,用默認(rèn)的 1L 就可以,這樣可以確保反序列化成功。因?yàn)椴煌男蛄谢痠d之間不能進(jìn)行序列化和反序列化。

到此這篇關(guān)于Java基礎(chǔ)之序列化與反序列化詳解的文章就介紹到這了,更多相關(guān)Java序列化與反序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mac配置 maven以及環(huán)境變量設(shè)置方式

    Mac配置 maven以及環(huán)境變量設(shè)置方式

    這篇文章主要介紹了Mac配置 maven以及環(huán)境變量設(shè)置方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • SpringBoot 設(shè)置傳入?yún)?shù)非必要的操作

    SpringBoot 設(shè)置傳入?yún)?shù)非必要的操作

    這篇文章主要介紹了SpringBoot 設(shè)置傳入?yún)?shù)非必要的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • 簡單了解synchronized和lock的區(qū)別

    簡單了解synchronized和lock的區(qū)別

    這篇文章主要介紹了簡單了解synchronized和lock的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • java微信企業(yè)號開發(fā)之開發(fā)模式的開啟

    java微信企業(yè)號開發(fā)之開發(fā)模式的開啟

    這篇文章主要為大家詳細(xì)介紹了java微信企業(yè)號開發(fā)之開發(fā)模式的開啟方法,感興趣的小伙伴們可以參考一下
    2016-06-06
  • Spring?Boot?應(yīng)用的熱部署配置方法

    Spring?Boot?應(yīng)用的熱部署配置方法

    熱部署,簡單來說,就是代碼修改后不需重啟項(xiàng)目就可自動加載出新的內(nèi)容,這篇文章主要介紹了Spring?Boot?應(yīng)用的熱部署配置?,需要的朋友可以參考下
    2022-11-11
  • Java中如何對字符串進(jìn)行utf-8編碼

    Java中如何對字符串進(jìn)行utf-8編碼

    這篇文章主要介紹了Java中如何對字符串進(jìn)行utf-8編碼問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Springboot之@Controller注解不生效問題及解決

    Springboot之@Controller注解不生效問題及解決

    這篇文章主要介紹了Springboot之@Controller注解不生效問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • SpringBoot線程池ThreadPoolTaskExecutor異步處理百萬級數(shù)據(jù)

    SpringBoot線程池ThreadPoolTaskExecutor異步處理百萬級數(shù)據(jù)

    本文主要介紹了SpringBoot線程池ThreadPoolTaskExecutor異步處理百萬級數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-03-03
  • 解決spring懶加載以及@PostConstruct結(jié)合的坑

    解決spring懶加載以及@PostConstruct結(jié)合的坑

    這篇文章主要介紹了解決spring懶加載以及@PostConstruct結(jié)合的坑,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • JSONObject按put順序排放與輸出方式

    JSONObject按put順序排放與輸出方式

    這篇文章主要介紹了JSONObject按put順序排放與輸出方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08

最新評論