日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

每日一博 - Java序列化一二事儿

發布時間:2025/3/21 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 每日一博 - Java序列化一二事儿 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • what
  • Why
  • 作用
  • 常用API
    • java.io.Serializable
    • java.io.Externalizable
    • java.io.ObjectOutputStream
    • java.io.ObjectInputStream
  • Code
    • 實現Serializable接口
    • ObjectOutputStream#writeObject 實現序列化
    • ObjectInputStream#readObject方法實現反序列化
  • 序列化底層實現分析
    • writeObject(Object)
  • FAQ


what

把Java對象轉換為字節序列的過程,-----------> 序列化
把字節序列恢復為Java對象的過程,-----------> 反序列化


Why

我們知道,Java對象是運行在JVM的堆內存中的,如果JVM停止后,對象也就不復存在了。

如果想在JVM停止后,把這些對象保存到磁盤或者通過網絡傳輸到另一遠程機器,怎么辦呢?------------------------------------就要把這些對象轉化為字節數組,這個過程就是序列化 .


作用

序列化使得對象可以脫離程序運行而獨立存在,它主要有兩種用途:

  • 序列化機制可以讓對象地保存到磁盤上,減輕內存壓力的同時,也起了持久化的作用;

比如 Web服務器中的Session對象,當有 10+萬用戶并發訪問的,就有可能出現10萬個Session對象,內存可能消化不良,于是Web容器就會把一些seesion先序列化到硬盤中,等要用了,再把保存在硬盤中的對象還原到內存中 【僅舉例,實際工作中并不會這么干】

  • 序列化機制讓Java對象在網絡中傳輸變得更加容易

在使用Dubbo遠程調用服務框架時,需要把傳輸的Java對象實現Serializable接口,即讓Java對象序列化,因為這樣才能讓對象在網絡上傳輸。


常用API

java.io.Serializable

Serializable接口是一個標記接口,沒有方法或字段。一旦實現了此接口,就標志該類的對象就是可序列化的。

public interface Serializable { }


java.io.Externalizable

Externalizable繼承了Serializable接口,還定義了兩個抽象方法:writeExternal()和readExternal(),如果開發人員使用Externalizable來實現序列化和反序列化,需要重寫writeExternal()和readExternal()方法

public interface Externalizable extends java.io.Serializable {void writeExternal(ObjectOutput out) throws IOException;void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }


java.io.ObjectOutputStream

表示對象輸出流,它的writeObject(Object obj)方法可以對指定obj對象參數進行序列化,再把得到的字節序列寫到一個目標輸出流中。

java.io.ObjectInputStream

表示對象輸入流,它的readObject()方法,從輸入流中讀取到字節序列,反序列化成為一個對象,最后將其返回。


Code

實現Serializable接口

import java.io.Serializable;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/9/12 19:07* @mark: show me the code , change the world*/ public class Artisan implements Serializable {private Integer age;private String name;public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;} }


ObjectOutputStream#writeObject 實現序列化

把Artisan對象 (必須實現Serializable 接口)設置值后,寫入一個文件,即序列化

import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/9/12 19:08* @mark: show me the code , change the world*/ public class Test {public static void main(String[] args) throws IOException {ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\artisan.out"));Artisan artisan = new Artisan();artisan.setAge(18);artisan.setName("artisan");objectOutputStream.writeObject(artisan);objectOutputStream.flush();objectOutputStream.close();} }

ObjectInputStream#readObject方法實現反序列化

再把test.out文件讀取出來,反序列化為Student對象

import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/9/12 19:09* @mark: show me the code , change the world*/ public class Test2 {public static void main(String[] args) throws IOException, ClassNotFoundException {ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\artisan.out"));Artisan art = (Artisan) objectInputStream.readObject();System.out.println("name="+art.getName());} }


序列化底層實現分析

Serializable接口,只是一個空的接口,沒有方法或字段,為什么這么神奇,實現了它就可以讓對象序列化了?

為了驗證Serializable的作用,寫個ArtisanNoSerial對象,去掉實現Serializable接口,看序列化過程怎樣吧~

堆棧信息看一下

ObjectOutputStream 在序列化的時候,會判斷被序列化的Object是哪一種類型,String array enum 還是 Serializable,如果都不是的話,拋出 NotSerializableException異常。所以 Serializable真的只是一個標志,一個序列化標志 。


writeObject(Object)

序列化的方法就是writeObject, debug下


writeObject直接調用的就是writeObject0()方法

public final void writeObject(Object obj) throws IOException {......writeObject0(obj, false);...... } private void writeObject0(Object obj, boolean unshared)throws IOException{......//String類型if (obj instanceof String) {writeString((String) obj, unshared);//數組類型} else if (cl.isArray()) {writeArray(obj, desc, unshared);//枚舉類型} else if (obj instanceof Enum) {writeEnum((Enum<?>) obj, desc, unshared);//Serializable實現序列化接口} else if (obj instanceof Serializable) {writeOrdinaryObject(obj, desc, unshared);} else{//其他情況會拋異常~if (extendedDebugInfo) {throw new NotSerializableException(cl.getName() + "\n" + debugInfoStack.toString());} else {throw new NotSerializableException(cl.getName());}}......

writeObject0 主要實現是對象的不同類型,調用不同的方法寫入序列化數據,這里面如果對象實現了Serializable接口,就調用writeOrdinaryObject()方法~

private void writeOrdinaryObject(Object obj,ObjectStreamClass desc,boolean unshared)throws IOException{......//調用ObjectStreamClass的寫入方法writeClassDesc(desc, false);// 判斷是否實現了Externalizable接口if (desc.isExternalizable() && !desc.isProxy()) {writeExternalData((Externalizable) obj);} else {//寫入序列化數據writeSerialData(obj, desc);}.....}

writeSerialData()實現的就是寫入被序列化對象的字段數據

private void writeSerialData(Object obj, ObjectStreamClass desc)throws IOException{for (int i = 0; i < slots.length; i++) {if (slotDesc.hasWriteObjectMethod()) {//如果被序列化的對象自定義實現了writeObject()方法,則執行這個代碼塊slotDesc.invokeWriteObject(obj, this);} else {// 調用默認的方法寫入實例數據defaultWriteFields(obj, slotDesc);}}}

defaultWriteFields()方法,獲取類的基本數據類型數據,直接寫入底層字節容器;獲取類的obj類型數據,循環遞歸調用writeObject0()方法,寫入數據~

private void defaultWriteFields(Object obj, ObjectStreamClass desc)throws IOException{ // 獲取類的基本數據類型數據,保存到primVals字節數組desc.getPrimFieldValues(obj, primVals);//primVals的基本類型數據寫到底層字節容器bout.write(primVals, 0, primDataSize, false);// 獲取對應類的所有字段對象ObjectStreamField[] fields = desc.getFields(false);Object[] objVals = new Object[desc.getNumObjFields()];int numPrimFields = fields.length - objVals.length;// 獲取類的obj類型數據,保存到objVals字節數組desc.getObjFieldValues(obj, objVals);//對所有Object類型的字段,循環for (int i = 0; i < objVals.length; i++) {......//遞歸調用writeObject0()方法,寫入對應的數據writeObject0(objVals[i],fields[numPrimFields + i].isUnshared());......}}

FAQ

  • static靜態變量和transient 修飾的字段是不會被序列化的

  • serialVersionUID問題

JAVA序列化的機制是通過判斷類的serialVersionUID來驗證版本是否一致的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID和本地相應實體類的serialVersionUID進行比較,如果相同,反序列化成功,如果不相同,就拋出InvalidClassException異常

如果確實需要修改某個類類,又想反序列化成功,怎么辦呢?可以手動指定serialVersionUID的值,一般可以設置為1L或者,或者讓我們的編輯器IDE生成

  • 如果某個序列化類的成員變量是對象類型,則該對象類型的類必須實現序列化

  • 子類實現了序列化,父類沒有實現序列化,父類中的字段丟失問題

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的每日一博 - Java序列化一二事儿的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。