国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

從java的序列化和反序列化說起

whlong / 1650人閱讀

摘要:從的序列化和反序列化說起序列化是將對象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^程,而相反的過程就稱為反序列化。當(dāng)使用接口來進(jìn)行序列化與反序列化的時(shí)候需要開發(fā)人員重寫與方法。

從java的序列化和反序列化說起

序列化 (Serialization)是將對象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^程,而相反的過程就稱為反序列化。

在java中允許我們創(chuàng)建可復(fù)用的對象,但是這些對象僅僅存在jvm的堆內(nèi)存中,有可能被垃圾回收器回收掉而消失,也可能隨著jvm的停止而消失,但是有的時(shí)候我們希望這些對象被持久化下來,能夠在需要的時(shí)候重新讀取出來。比如我們需要在網(wǎng)絡(luò)中傳輸對象,首先就需要把對象序列化二進(jìn)制,然后在網(wǎng)絡(luò)中傳輸,接收端收到這些二進(jìn)制數(shù)據(jù)后進(jìn)行反序列化還原成對象,完成對象的網(wǎng)絡(luò)傳輸,java的序列化和反序列化功能就可以幫助我們現(xiàn)實(shí)此功能。

那么java要怎么樣才能實(shí)現(xiàn)序列化和反序列化呢?

Serializable接口

在java中要實(shí)現(xiàn)序列化和和反序列化只需要實(shí)現(xiàn)Serializable接口,任何視圖將沒有實(shí)現(xiàn)此接口的對象進(jìn)行序列化和反序列化操作都會(huì)拋出NotSerializableException,下面是實(shí)現(xiàn):

public  byte[] serializer(T obj) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = null;
    try {
        oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
    } catch (IOException e) {
        logger.error("java序列化發(fā)生異常:{}",e);
        throw new RuntimeException(e);
    }finally{
        try {
            if(oos != null)oos.close();
        } catch (IOException e) {
            logger.error("java序列化發(fā)生異常:{}",e);
        }
    }
    return baos.toByteArray();
}

public  T deserializer(byte[] data, Class clazz) {
    ByteArrayInputStream bais = new ByteArrayInputStream(data);
    ObjectInputStream ois = null;
    try {
        ois = new ObjectInputStream(bais);
        return (T)ois.readObject();
    } catch (Exception e) {
        logger.error("java反序列化發(fā)生異常:{}",e);
        throw new RuntimeException(e);
    }finally{
        try {
            ois.close();
        } catch (IOException e) {
            logger.error("java反序列化發(fā)生異常:{}",e);
            throw new RuntimeException(e);
        }
    }
}

transient關(guān)鍵字

正常情況下,在序列化過程中,對象里面的屬性都會(huì)被序列化,但是有的時(shí)候,我們想過濾掉某個(gè)屬性不要被序列化,該怎么辦呢,很簡單java給我們提供了一個(gè)關(guān)鍵字來實(shí)現(xiàn):transient,只要被transient關(guān)鍵字修飾了,就會(huì)被過濾掉

readObject和writeObject方法

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

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

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

細(xì)心的你肯定也發(fā)現(xiàn)了,我們在序列化的類里面定于了這兩個(gè)方法,但是并沒有顯式的調(diào)用這兩個(gè)方法,那到底是誰調(diào)用的,又是何時(shí)被調(diào)用的呢?

深入ByteArrayOutputStream類源碼會(huì)發(fā)現(xiàn)其調(diào)用棧:

ObjectOutputStream.writeObject(Object obj)----------->writeObject0(Object obj, boolean unshared)----------->writeOrdinaryObject(Object obj,ObjectStreamClass desc,boolean unshared)----------->writeSerialData(Object obj, ObjectStreamClass desc)

在writeSerialData方法里面會(huì)先獲取序列化類里面是否有writeObject(ObjectOutputStream out),有就會(huì)反射的調(diào)用,沒有就執(zhí)行默認(rèn)的序列化方法defaultWriteFields(obj, slotDesc)。

ByteArrayInputStream也是同樣的原理。

如果您讀過在ArrayList的源碼,你可能會(huì)發(fā)現(xiàn)在ArrayList中的字段elementData被關(guān)鍵字transient修飾了,而elementData字段是ArrayList存儲(chǔ)元素的,難道ArrayList存儲(chǔ)的元素序列化會(huì)被忽略嗎?但是你會(huì)發(fā)現(xiàn)并沒有被忽略,而是能正常的序列化和反序列化,這是為什么呢?答案就是,ArrayList寫有上面提到的readObject和writeObject兩個(gè)方法,ArrayList實(shí)際上是動(dòng)態(tài)數(shù)組,每次在放滿以后自動(dòng)增長設(shè)定的長度值,如果數(shù)組自動(dòng)增長長度設(shè)為50,而實(shí)際只放了1個(gè)元素,那就會(huì)序列化49個(gè)null元素。為了保證在序列化的時(shí)候不會(huì)將這么多null同時(shí)進(jìn)行序列化,ArrayList把元素?cái)?shù)組設(shè)置為transient,自定義序列化過程,這樣可以優(yōu)化存儲(chǔ)。

Externalizable接口

除了Serializable 之外,java中還提供了另一個(gè)序列化接口Externalizable,繼承了Serializable,該接口中定義了兩個(gè)抽象方法:writeExternal()與readExternal()。當(dāng)使用Externalizable接口來進(jìn)行序列化與反序列化的時(shí)候需要開發(fā)人員重寫writeExternal()與readExternal()方法。

序列化ID

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

序列化 ID 在 Eclipse 下提供了兩種生成策略,一個(gè)是固定的 1L,一個(gè)是隨機(jī)生成一個(gè)不重復(fù)的 long 類型數(shù)據(jù)(實(shí)際上是使用 JDK 工具生成),在這里有一個(gè)建議,如果沒有特殊需求,就是用默認(rèn)的 1L 就可以,這樣可以確保代碼一致時(shí)反序列化成功。那么隨機(jī)生成的序列化 ID 有什么作用呢,有些時(shí)候,通過改變序列化 ID 可以用來限制某些用戶的使用。

Protobuf

我們知道java自帶的序列化效率是非常低的,因?yàn)樗蛄谢傻淖止?jié)數(shù)非常多(包含了很多類的信息),不太適合用于存儲(chǔ)和在網(wǎng)絡(luò)上傳輸,下面來介紹下google給我們提供一個(gè)序列化效率相當(dāng)高的框架protobuff,比起java原生的序列化出來的字節(jié)數(shù)小十幾倍。那么它是如何做到的呢?

以int類型為例,int在java的占用4個(gè)字節(jié),如果我們不做特殊處理,int類型的值轉(zhuǎn)化成二進(jìn)制也需要占用4個(gè)字節(jié)的空間,但是protobuff卻不是這樣做的,請看下面代碼:

while (true) {
    if ((value & ~0x7F) == 0) {
        UnsafeUtil.putByte(buffer, position++, (byte) value);
        break;
    } else {
        UnsafeUtil.putByte(buffer, position++, (byte) ((value & 0x7F) | 0x80));
        value >>>= 7;
    }
}

value & ~0x7F 是什么意思呢?0x7F取反跟value相與,那么value的低7位全部被置0了,如果此時(shí)相與的值等于0,說明value的值不會(huì)大于0x7F=127,就可以用一個(gè)字節(jié)來表示,大大節(jié)省了字節(jié)數(shù),看個(gè)列子:

value=0x00000067,轉(zhuǎn)換成二進(jìn)制:

0000 0000  0000 0000  0000 0000  0110 0111

& 1111 1111 1111 1111 1111 1111 1000 0000

= 0000 0000 0000 0000 0000 0000 0000 0000

此時(shí)value & ~0x7F=0,當(dāng)把value強(qiáng)制轉(zhuǎn)換成byte類型時(shí),int會(huì)被截?cái)啵皇O碌臀蛔止?jié),于是當(dāng)value值小于128時(shí),序列化后的字節(jié)就變成:0110 0111,一個(gè)字節(jié)就可以表示了。

問題來了,如果value的值大于0x7F呢,接著看(value & 0x7F) | 0x80這句代碼,假設(shè)value=2240,

 0000 0000  0000 0000  0000 1000  1100 0000     0x000008C0

& 0000 0000 0000 0000 0000 0000 0111 1111 0x0000007F

= 0000 0000 0000 0000 0000 0000 1100 0000 0x000000C0

| 0000 0000 0000 0000 0000 0000 1000 0000 0x00000080

= 0000 0000 0000 0000 0000 0000 1100 0000 0x000000C0

這個(gè)過程意思就是獲取value的最低位字節(jié),把這個(gè)字節(jié)的最高位置為1,表示后面還有可讀字節(jié)。

對0x000000C0強(qiáng)轉(zhuǎn)byte類型就變成:1100 0000,然后向右移7位:

0000 0000 0000 0000 0000 0000 0001 0001

重復(fù)上面的步驟,得到0001 0001,循環(huán)結(jié)束,最后得到:

1100 0000 0001 0001

2個(gè)字節(jié)就可以表示2240了,但是此時(shí)你會(huì)發(fā)現(xiàn)我們每次向右移動(dòng)的是7位,移4次才能表示28位,但是int要占用32位,如果value的值比較大,假如等于2147483647,那么這是就需要5個(gè)字節(jié)來表示,綜上所述protobuff表示一個(gè)int類型的值就不會(huì)固定4個(gè)字節(jié),而是用1-5個(gè)字節(jié)動(dòng)態(tài)來表示;那么你可能又會(huì)有疑問了,5個(gè)字節(jié)來表示一個(gè)int,字節(jié)數(shù)不是變多了么?其實(shí)從概率角度來看,我們業(yè)務(wù)上不能可能每一個(gè)int值都是一個(gè)非常大的值,所以還是可以為我們節(jié)省非常大的字節(jié)空間。同理long,double,float也是同樣的原理。下面就以proptobuff3來介紹下protobuff的使用

一、整備

從protobuff官網(wǎng)下載protoc.exe可執(zhí)行文件

二、編寫proto文件,具體的語法參見官網(wǎng)文檔

syntax = "proto3";
option java_package = "com.yanghui.serialize.protobuf3";
option java_outer_classname = "PersonModule";
message Person {
    int32 age = 1;
    int64 time = 2;
    string name = 3;
    map properties = 4;
}

三、編譯成java類

e:/study/protobuf/bin/protoc.exe -I=D:/workspace/serialize/src/main/java/com/yanghui/serialize/protobuf3 --java_out=D:/workspace/serialize/src/main/java person.proto

-I:表示proto文件所在目錄

--java_out:表示輸出java的類

執(zhí)行以上命令就會(huì)在指定的目錄生成一個(gè)java類PersonModule.java,接下來就可以使用了

@Test
public void testProtobuffSerialize() throws InvalidProtocolBufferException {
    Builder builder = PersonModule.Person.newBuilder();
    builder.setAge(21);
    builder.setTime(100L);
    builder.setName("yanghui");
    builder.putProperties("key1", "value1");
    com.yanghui.serialize.protobuf3.PersonModule.Person person = builder.build();

    byte[] personBytes = person.toByteArray();
    System.out.println(Arrays.toString(personBytes));
    System.out.println(personBytes.length);

    com.yanghui.serialize.protobuf3.PersonModule.Person p = 
        com.yanghui.serialize.protobuf3.PersonModule.Person.parseFrom(personBytes);
    System.out.println(p.toString());
}


文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76591.html

相關(guān)文章

  • java列化和反列化

    摘要:引語平時(shí)我們在運(yùn)行程序的時(shí)候創(chuàng)建的對象都在內(nèi)存中當(dāng)程序停止或者中斷了對象也就不復(fù)存在了如果我們能將對象保存起來在需要使用它的時(shí)候在拿出來使用就好了并且對象的信息要和我們保存時(shí)的信息一致序列化就可以解決了這樣的問題序列化當(dāng)然不止一種方式如下序 引語: ????平時(shí)我們在運(yùn)行程序的時(shí)候,創(chuàng)建的對象都在內(nèi)存中,當(dāng)程序停止或者中斷了,對象也就不復(fù)存在了.如果我們能將對象保存起來,在需要使用它的...

    snowell 評論0 收藏0
  • java對象列化和反列化

    摘要:序列化對象和平臺無關(guān),序列化得到的字節(jié)流可以在任何平臺反序列化。從文件中或網(wǎng)絡(luò)上獲得序列化的字節(jié)流后,根據(jù)字節(jié)流中所保存的對象狀態(tài)及描述信息,通過反序列化重建對象。因此意味著不要序列化靜態(tài)變量不屬于對象狀態(tài)的一部分,因此它不參與序列化。 一.序列化和反序列化(1)序列化:將內(nèi)存中的對象轉(zhuǎn)化為字節(jié)序列,用于持久化到磁盤中或者通過網(wǎng)絡(luò)傳輸。對象序列化的最主要的用處就是傳遞和保存對象,保證對...

    chadLi 評論0 收藏0
  • Java 列化和反列化

    摘要:把字節(jié)序列恢復(fù)為對象的過程稱為對象的反序列化。代表對象輸入流,它的方法從一個(gè)源輸入流中讀取字節(jié)序列,再把它們反序列化為一個(gè)對象,并將其返回。接口繼承自接口,實(shí)現(xiàn)接口的類完全由自身來控制序列化的行為,而僅實(shí)現(xiàn)接口的類可以采用默認(rèn)的序列化方式。 把對象轉(zhuǎn)換為字節(jié)序列的過程稱為對象的序列化。把字節(jié)序列恢復(fù)為對象的過程稱為對象的反序列化。    對象的序列化主要有兩種用途:   1) 把...

    jcc 評論0 收藏0
  • springboot學(xué)習(xí)(三)——使用HttpMessageConverter進(jìn)行http列化和反

    摘要:序列化反序列化主要體現(xiàn)在程序這個(gè)過程中,包括網(wǎng)絡(luò)和磁盤。如果是開發(fā)應(yīng)用,一般這兩個(gè)注解對應(yīng)的就是序列化和反序列化的操作。協(xié)議的處理過程,字節(jié)流內(nèi)部對象,就涉及這兩種序列化。進(jìn)行第二步操作,也就是序列化和反序列化的核心是。 以下內(nèi)容,如有問題,煩請指出,謝謝! 對象的序列化/反序列化大家應(yīng)該都比較熟悉:序列化就是將object轉(zhuǎn)化為可以傳輸?shù)亩M(jìn)制,反序列化就是將二進(jìn)制轉(zhuǎn)化為程序內(nèi)部的...

    stackfing 評論0 收藏0
  • Java IO (三) 讀取其他進(jìn)程數(shù)據(jù),RandomAccessFile,列化和反列化

    摘要:虛擬機(jī)讀取其他進(jìn)程的數(shù)據(jù)對象的方法可以運(yùn)行平臺上的其他程序該方法產(chǎn)生一個(gè)對象對象代表由該程序啟動(dòng)啟動(dòng)的子進(jìn)程類提供如下三個(gè)方法用于和其子進(jìn)程通信獲取子進(jìn)程的錯(cuò)誤流獲取子進(jìn)程的輸入流獲取子進(jìn)程的輸出流這里的輸入流輸出流容易混淆從程序的角度思考 Java虛擬機(jī)讀取其他進(jìn)程的數(shù)據(jù) Runtime對象的exec方法可以運(yùn)行平臺上的其他程序,該方法產(chǎn)生一個(gè)Process對象,Process對象...

    zhangfaliang 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<