更多課程 選擇中心
        Java培訓

        400-111-8989

        Java培訓 > Java教程  > 正文

        Java序列化的幾種方式以及序列化的作用

        • 發布:Java培訓
        • 來源:Java教程
        • 時間:2017-09-22 15:02

        java培訓機構分享的本文著重講解一下Java序列化的相關內容。

        如果對Java序列化感興趣的同學可以研究一下。

        一.Java序列化的作用

        有的時候我們想要把一個Java對象變成字節流的形式傳出去,有的時候我們想要從一個字節流中恢復一個Java對象。例如,有的時候我們想要

        把一個Java對象寫入到硬盤或者傳輸到網路上面的其它計算機,這時我們就需要自己去通過java把相應的對象寫成轉換成字節流。對于這種通用

        的操作,我們為什么不使用統一的格式呢?沒錯,這里就出現了java的序列化的概念。在Java的OutputStream類下面的子類ObjectOutput-

        Stream類就有對應的WriteObject(Object object) 其中要求對應的object實現了java的序列化的接口。

        為了更好的理解java序列化的應用,我舉兩個自己在開發項目中遇到的例子:

        1)在使用tomcat開發JavaEE相關項目的時候,我們關閉tomcat后,相應的session中的對象就存儲在了硬盤上,如果我們想要在tomcat重啟的

        時候能夠從tomcat上面讀取對應session中的內容,那么保存在session中的內容就必須實現相關的序列化操作。

        2)如果我們使用的java對象要在分布式中使用或者在rmi遠程調用的網絡中使用的話,那么相關的對象必須實現java序列化接口。

        親愛的小伙伴,大概你已經了解了java序列化相關的作用,接下來們來看看如何實現java的序列化吧。~

        二.實現java對象的序列化和反序列化。

        Java對象的序列化有兩種方式。

        a.是相應的對象實現了序列化接口Serializable,這個使用的比較多,對于序列化接口Serializable接口是一個空的接口,它的主要作用就是

        標識這個對象時可序列化的,jre對象在傳輸對象的時候會進行相關的封裝。這里就不做過多的介紹了。

        下面是一個實現序列化接口的Java序列化的例子:非常簡單

        Java序列化的幾種方式以及序列化的作用

        b.實現序列化的第二種方式為實現接口Externalizable,Externlizable的部分源代碼如下:

        Java序列化的幾種方式以及序列化的作用

        沒錯,Externlizable接口繼承了java的序列化接口,并增加了兩個方法:

        Java序列化的幾種方式以及序列化的作用

        首先,我們在序列化對象的時候,由于這個類實現了Externalizable 接口,在writeExternal()方法里定義了哪些屬性可以序列化,

        哪些不可以序列化,所以,對象在經過這里就把規定能被序列化的序列化保存文件,不能序列化的不處理,然后在反序列的時候自動調

        用readExternal()方法,根據序列順序挨個讀取進行反序列,并自動封裝成對象返回,然后在測試類接收,就完成了反序列。

        所以說Exterinable的是Serializable的一個擴展。

        為了更好的理解相關內容,請看下面的例子:

        package com.xiaohao.test;import java.io.Externalizable;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInput;import java.io.ObjectInputStream;import java.io.ObjectOutput;import java.io.ObjectOutputStream;import java.text.SimpleDateFormat;import java.util.Date;/**

        * 測試實體類

        * @author 小浩

        * @創建日期 2015-3-12

        */class Person implements Externalizable{ private static final long serialVersionUID = 1L;
        String userName;

        String password;

        String age; public Person(String userName, String password, String age) { super(); this.userName = userName; this.password = password; this.age = age;

        } public Person() { super();

        } public String getAge() { return age;

        } public void setAge(String age) { this.age = age;

        } public String getUserName() { return userName;

        } public void setUserName(String userName) { this.userName = userName;

        } public String getPassword() { return password;

        } public void setPassword(String password) { this.password = password;

        } /**

        * 序列化操作的擴展類

        */

        @Override

        public void writeExternal(ObjectOutput out) throws IOException { //增加一個新的對象

        Date date=new Date();

        out.writeObject(userName);

        out.writeObject(password);

        out.writeObject(date);

        } /**

        * 反序列化的擴展類

        */

        @Override

        public void readExternal(ObjectInput in) throws IOException,

        ClassNotFoundException { //注意這里的接受順序是有限制的哦,否則的話會出錯的

        // 例如上面先write的是A對象的話,那么下面先接受的也一定是A對象...

        userName=(String) in.readObject();

        password=(String) in.readObject();

        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");

        Date date=(Date)in.readObject();

        System.out.println("反序列化后的日期為:"+sdf.format(date));

        } @Override

        public String toString() { //注意這里的年齡是不會被序列化的,所以在反序列化的時候是讀取不到數據的

        return "用戶名:"+userName+"密 碼:"+password+"年齡:"+age;

        }

        }/**

        * 序列化和反序列化的相關操作類

        * @author 小浩

        * @創建日期 2015-3-12 Java學習交流QQ群:589809992 我們一起學Java!

        */class Operate{ /**

        * 序列化方法

        * @throws IOException

        * @throws FileNotFoundException

        */

        public void serializable(Person person) throws FileNotFoundException, IOException{

        ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("a.txt"));

        outputStream.writeObject(person);

        } /**

        * 反序列化的方法

        * @throws IOException

        * @throws FileNotFoundException

        * @throws ClassNotFoundException

        */

        public Person deSerializable() throws FileNotFoundException, IOException, ClassNotFoundException{

        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("a.txt")); return (Person) ois.readObject();

        }

        }/**

        * 測試實體主類

        * @author 小浩

        * @創建日期 2015-3-12

        */public class Test{

        public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {

        Operate operate=new Operate();

        Person person=new Person("小浩","123456","20");

        System.out.println("為序列化之前的相關數據如下:\n"+person.toString());

        operate.serializable(person);

        Person newPerson=operate.deSerializable();

        System.out.println("-------------------------------------------------------");

        System.out.println("序列化之后的相關數據如下:\n"+newPerson.toString());

        }

        }

        首先,我們在序列化UserInfo對象的時候,由于這個類實現了Externalizable 接口,在writeExternal()方法里定義了哪些屬性可

        以序列化,哪些不可以序列化,所以,對象在經過這里就把規定能被序列化的序列化保存文件,不能序列化的不處理,然后在反序列

        的時候自動調用readExternal()方法,根據序列順序挨個讀取進行反序列,并自動封裝成對象返回,然后在測試類接收,就完成了反

        序列。

        對于實現Java的序列化接口需要注意一下幾點:

        1.java中的序列化時transient變量(這個關鍵字的作用就是告知JAVA我不可以被序列化)和靜態變量不會被序列化(下面是一個測試的例子)

        Java序列化的幾種方式以及序列化的作用

        Java序列化的幾種方式以及序列化的作用

        2.也是最應該注意的,如果你先序列化對象A后序列化B,那么在反序列化的時候一定記著JAVA規定先讀到的對象是先被序列化的對象,不要先接收對象B,那樣會報錯.尤其在使用上面的Externalizable的時候一定要注意讀取的先后順序。

        3.實現序列化接口的對象并不強制聲明唯一的serialVersionUID,是否聲明serialVersionUID對于對象序列化的向上向下的兼容性有很大的影響。我們來做個測試:

        思路一

        把User中的serialVersionUID去掉,序列化保存。反序列化的時候,增加或減少個字段,看是否成功。

        Java代碼

        public class User implements Serializable{private String name; private int age;private long phone;private List friends;

        ...
        }

        保存到文件中:

        Java代碼

        ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream os = new ObjectOutputStream(bos);os.writeObject(src);os.flush();os.close();byte[] b = bos.toByteArray();bos.close();FileOutputStream fos = new FileOutputStream(dataFile);fos.write(b);fos.close();

        增加或者減少字段后,從文件中讀出來,反序列化:

        Java代碼

        ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream os = new ObjectOutputStream(bos);os.writeObject(src);os.flush();os.close();byte[] b = bos.toByteArray();bos.close();FileOutputStream fos = new FileOutputStream(dataFile);fos.write(b);fos.close();

        結果:拋出異常信息

        Java代碼

        Exception in thread "main" java.io.InvalidClassException: serialize.obj.UserVo; local class incompatible: stream classdesc serialVersionUID = 3305402508581390189, local class serialVersionUID = 7174371419787432394 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560)at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582)

        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)

        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)

        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)

        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)

        at serialize.obj.ObjectSerialize.read(ObjectSerialize.java:74)

        at serialize.obj.ObjectSerialize.main(ObjectSerialize.java:27)

        思路二

        eclipse指定生成一個serialVersionUID,序列化保存,修改字段后反序列化

        略去代碼

        結果:反序列化成功

        結論

        如果沒有明確指定serialVersionUID,序列化的時候會根據字段和特定的算法生成一個serialVersionUID,當屬性有變化時這個id發生了變化,所以反序列化的時候

        就會失敗。拋出“本地classd的唯一id和流中class的唯一id不匹配”。

        jdk文檔關于serialVersionUID的描述:

        寫道

        如果可序列化類未顯式聲明 serialVersionUID,則序列化運行時將基于該類的各個方面計算該類的默認 serialVersionUID 值,如“Java(TM) 對象序列化規范”中所述。不過,強烈建議 所有可序列化類都顯式聲明 serialVersionUID 值,原因是計算默認的 serialVersionUID 對類的詳細信息具有較高的敏感性,根據編譯器實現的不同可能千差萬別,這樣在反序列化過程中可能會導致意外的 InvalidClassException。因此,為保證 serialVersionUID 值跨不同 java 編譯器實現的一致性,序列化類必須聲明一個明確的 serialVersionUID 值。還強烈建議使用 private 修飾符顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應用于直接聲明類 – serialVersionUID 字段作為繼承成員沒有用處。數組類不能聲明一個明確的 serialVersionUID,因此它們總是具有默認的計算值,但是數組類沒有匹配 serialVersionUID 值的要求。

        三.實現序列化的其它方式 (這是一個擴展內容,感興趣的可以擴展一下)

        1)是把對象包裝成JSON字符串傳輸。

        這里采用JSON格式同時使用采用Google的gson-2.2.2.jar 進行轉義

        2)采用谷歌的ProtoBuf

        隨著Google工具protoBuf的開源,protobuf也是個不錯的選擇。對JSON,Object Serialize(Java的序列化和反序列化),ProtoBuf 做個對比。

        定義一個通用的待傳輸的對象UserVo:

        /**

        * Java學習交流QQ群:589809992 我們一起學Java!

        */public class Userprivate static final long serialVersionUID = -5726374138698742258L;

        { private String name; private int age; private long phone; private List friends;

        ...set和get方法

        }

        初始化User的實例src:

        Java代碼

        User user1 = new UserVo();user1 .setName("user1 ");

        user1 .setAge(30);

        user1 .setPhone(13789126278L);

        UserVo f1 = new UserVo();

        f1.setName("tmac");

        f1.setAge(32);

        f1.setPhone(123L);

        User user2 = new User();

        user2 .setName("user2 ");

        user2 .setAge(29);

        user2 .setPhone(123L);
        List friends = new ArrayList();

        friends.add(user1 );

        friends.add(user2 );

        user1 .setFriends(friends);

        1.首先使用JOSN來實現序列化。

        Java代碼

        Gson gson = new Gson();
        String json = gson.toJson(src);

        得到的字符串:

        Js代碼

        {"name":"user1 ","age":30,"phone":123,"friends":[{"name":"user1 ","age":32,"phone":123},{"name":"user2 ","age":29,"phone":123}]}

        字節數為153

        Json的優點:明文結構一目了然,可以跨語言,屬性的增加減少對解析端影響較小。缺點:字節數過多,依賴于不同的第三方類庫。

        Object Serialize(Java的序列化和反序列化)

        UserVo實現Serializalbe接口,提供唯一的版本號:

        序列化方法:

        Java代碼

        ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream os = new ObjectOutputStream(bos);os.writeObject(src);os.flush();os.close();byte[] b = bos.toByteArray();bos.close();

        字節數是238

        反序列化:

        Java代碼

        ObjectInputStream ois = new ObjectInputStream(fis);vo = (UserVo) ois.readObject();ois.close();fis.close();

        Object Serializalbe 優點:java原生支持,不需要提供第三方的類庫,使用比較簡單。

        缺點:無法跨語言,字節數占用比較大,某些情況下對于對象屬性的變化比較敏感。

        對象在進行序列化和反序列化的時候,必須實現Serializable接口,但并不強制聲明唯一的serialVersionUID

        是否聲明serialVersionUID對于對象序列化的向上向下的兼容性有很大的影響。

        Google ProtoBuf

        protocol buffers 是google內部得一種傳輸協議,目前項目已經開源(http://code.google.com/p/protobuf/)。

        它定義了一種緊湊得可擴展得二進制協議格式,適合網絡傳輸,并且針對多個語言有不同得版本可供選擇。

        以protobuf-2.5.0rc1為例,準備工作:

        下載源碼,解壓,編譯,安裝

        Shell代碼

        tar zxvf protobuf-2.5.0rc1.tar.gz ./configure ./make ./make install

        測試:

        Shell代碼

        MacBook-Air:~ ming$ protoc --version libprotoc 2.5.0

        安裝成功!

        進入源碼得java目錄,用mvn工具編譯生成所需得jar包,protobuf-java-2.5.0rc1.jar

        1、編寫.proto文件,命名UserVo.proto

        Text代碼

        package serialize;option java_package = "serialize";option java_outer_classname="UserVoProtos";

        message User{

        optional string name = 1;

        optional int32 age = 2;

        optional int64 phone = 3;

        repeated serialize.UserVo friends = 4;

        }

        2、在命令行利用protoc 工具生成builder類

        Shell代碼

        protoc -IPATH=.proto文件所在得目錄 –java_out=java文件的輸出路徑 .proto的名稱

        得到UserProtos類

        3、編寫序列化代碼

        Java代碼

        UserVoProtos.User.Builder builder = UserVoProtos.User.newBuilder();builder.setName("Yaoming"); builder.setAge(30);builder.setPhone(13789878978L);UserVoProtos.UserVo.Builder builder1 = UserVoProtos.UserVo.newBuilder();builder1.setName("tmac"); builder1.setAge(32); builder1.setPhone(138999898989L);UserVoProtos.UserVo.Builder builder2 = UserVoProtos.UserVo.newBuilder();builder2.setName("liuwei"); builder2.setAge(29); builder2.setPhone(138999899989L);builder.addFriends(builder1);builder.addFriends(builder2);UserVoProtos.UserVo vo = builder.build(); byte[] v = vo.toByteArray();

        字節數53

        反序列化

        Java代碼

        UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);System.out.println(uvo.getFriends(0).getName());

        結果:tmac,反序列化成功

        google protobuf 優點:字節數很小,適合網絡傳輸節省io,跨語言 。

        缺點:需要依賴于工具生成代碼。

        工作機制

        proto文件是對數據的一個描述,包括字段名稱,類型,字節中的位置。protoc工具讀取proto文件生成對應builder代碼的類庫。protoc xxxxx –java_out=xxxxxx 生成java類庫。builder類根據自己的算法把數據序列化成字節流,或者把字節流根據反射的原理反序列化成對象。

        proto文件中的字段類型和java中的對應關系:

        Java序列化的幾種方式以及序列化的作用

        字段屬性的描述:

        寫道

        required: a well-formed message must have exactly one of this field. optional: a well-formed message can have zero or one of this field (but not more than one). repeated: this field can be repeated any number of times (including zero) in a well-formed message. The order of the repeated values will be preserved.

        protobuf 在序列化和反序列化的時候,是依賴于.proto文件生成的builder類完成,字段的變化如果不表現在.proto文件中就不會影響反序列化,比較適合字段變化的情況。

        做個測試:把UserVo序列化到文件中:

        Java代碼

        UserProtos.User vo = builder.build();byte[] v = vo.toByteArray();FileOutputStream fos = new FileOutputStream(dataFile);fos.write(vo.toByteArray());fos.close();

        為User增加字段,對應的.proto文件:

        Text代碼

        package serialize;option java_package = "serialize";option java_outer_classname="UserVoProtos";

        message User{

        optional string name = 1;

        optional int32 age = 2;

        optional int64 phone = 3;

        repeated serialize.UserVo friends = 4;

        optional string address = 5; }

        從文件中反序列化回來:

        Java代碼

        FileInputStream fis = new FileInputStream(dataFile);byte[] dstb = new byte[fis.available()];for(int i=0;i

        fis.close(); UserProtos.User uvo = UserProtos.User.parseFrom(dstb);

        System.out.println(uvo.getFriends(0).getName());

        成功得到結果。

        三種方式對比傳輸同樣的數據,google protobuf只有53個字節是最少的。結論:

        Java序列化的幾種方式以及序列化的作用
        免責聲明:本文由小編轉載自網絡,旨在分享提供閱讀,版權歸原作者所有,如有侵權請聯系我們進行刪除

        預約申請免費試聽課

        填寫下面表單即可預約申請免費試聽!怕錢不夠?可就業掙錢后再付學費! 怕學不會?助教全程陪讀,隨時解惑!擔心就業?一地學習,可全國推薦就業!

        上一篇:寫詩一般的 Java 日志
        下一篇:Java 程序員進入 BAT 所需要掌握的基本技能
        12大要點讓你的Java開發所向披靡~

        12大要點讓你的Java開發所向披靡~

        學習Java最好的12本免費在線電子書

        學習Java最好的12本免費在線電子書

        Java常用日志框架介紹

        Java常用日志框架介紹

        一篇文章了解RPC框架原理

        一篇文章了解RPC框架原理

        • 掃碼領取資料

          回復關鍵字:視頻資料

          免費領取 達內課程視頻學習資料

        • 視頻學習QQ群

          添加QQ群:1143617948

          免費領取達內課程視頻學習資料

        Copyright ? 2021 Tedu.cn All Rights Reserved 京ICP備08000853號-56 京公網安備 11010802029508號 達內時代科技集團有限公司 版權所有

        選擇城市和中心
        貴州省

        福建省

        • 達內廈門軟件園中心
        廣西省

        海南省

        国产高清情侣视频2019年,限制电影福利在线观看,23伊人大香蕉 百度 好搜 搜狗
        <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>