一、序列化與反序列化
序列化:指堆記憶體中的java物件數(shù)據(jù),透過某種方式把對儲存到磁碟檔案中,或傳遞給其他網(wǎng)路節(jié)點(網(wǎng)路傳輸)。這個過程稱為序列化,通常是指將資料結(jié)構(gòu)或物件轉(zhuǎn)換成二進位的過程。
即將對象轉(zhuǎn)化為二進制,用于保存,或者網(wǎng)絡(luò)傳輸。
反序列化:把磁碟檔案中的物件資料或是把網(wǎng)路節(jié)點上的物件數(shù)據(jù),恢復(fù)成Java物件模型的過程。也就是將在序列化過程中所產(chǎn)生的二進位串轉(zhuǎn)換成資料結(jié)構(gòu)或物件的過程
與序列化相反,將二進制轉(zhuǎn)化成對象。
二、序列化的作用
① 想把內(nèi)存中的物件儲存到一個檔案或資料庫中時候;
##② 想用套接字在網(wǎng)路上傳送物件的時候;③ 想透過RMI傳輸物件的時候
一些應(yīng)用場景,涉及到將對象轉(zhuǎn)化成二進制,序列化保證了能夠成功讀取到保存的對象。
三、java的序列化實作
要實現(xiàn)物件的序列化,最直接的操作就是實作Serializable介面使用IO流中的對象流可以實現(xiàn)序列化操作,將物件儲存到文件,再讀取出來。 先建立一個對象,並實作Serializable介面:import java.io.Serializable; public class User implements Serializable{ private static final long serialVersionUID = 1L; private String name; private int age; 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; } @Override public String toString() { return "User [name=" + name + ", age=" + age + "]"; } }用物件流寫一個保存物件與讀取物件的工具類別:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeUtil { // 保存對象,序列化 public static void saveObject(Object object) throws Exception { ObjectOutputStream out = null; FileOutputStream fout = null; try { fout = new FileOutputStream("D:/1.txt"); out = new ObjectOutputStream(fout); out.writeObject(object); } finally { fout.close(); out.close(); } } // 讀取對象,反序列化 public static Object readObject() throws Exception { ObjectInputStream in = null; FileInputStream fin = null; try { fin = new FileInputStream("D:/1.txt"); in = new ObjectInputStream(fin); Object object = in.readObject(); return object; } finally { fin.close(); in.close(); } } }測試:
public class Main { public static void main(String[] args) { User user = new User(); user.setName("旭旭寶寶"); user.setAge(33); // 保存 try { SerializeUtil.saveObject(user); } catch (Exception e) { System.out.println("保存時異常:" + e.getMessage()); } // 讀取 User userObject; try { userObject = (User) SerializeUtil.readObject(); System.out.println(userObject); } catch (Exception e) { System.out.println("讀取時異常:" + e.getMessage()); } } }測試結(jié)果:
public class User { private String name; private int age; 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; } @Override public String toString() { return "User [name=" + name + ", age=" + age + "]"; } }再測試Main方法:
四、序列化ID的作用
可以看到,我們在進行序列化時,加了一個serialVersionUID字段,這就是序列化IDprivate static final long serialVersionUID = 1L;這個序列化ID起著關(guān)鍵的作用,它決定著是否能夠成功反序列化! java的序列化機制是透過判斷執(zhí)行階段類別的serialVersionUID來驗證版本一致性的,在進行反序列化時,JVM會把傳進來的位元組流中的serialVersionUID與本地實體類別中的serialVersionUID進行比較,如果相同則認為是一致的,便可以進行反序列化,否則就會報序列化版本不一致的異常。
即序列化ID是為了保證成功進行反序列化
五、預(yù)設(shè)的序列化ID
當(dāng)我們一個實體類別中沒有明確的定義一個名為「serialVersionUID」、類型為long的變數(shù)時,Java序列化機制會根據(jù)編譯時的class自動產(chǎn)生一個serialVersionUID作為序列化版本比較,這種情況下,只有同一次編譯產(chǎn)生的class才會產(chǎn)生相同的serialVersionUID。譬如,當(dāng)我們寫一個類別時,隨著時間的推移,我們因為需求改動,需要在本地類別中添加其他的字段,這個時候再反序列化時便會出現(xiàn)serialVersionUID不一致,導(dǎo)致反序列化失敗。那麼如何解決呢?便是在本地類別中加入一個「serialVersionUID」變量,值保持不變,便可進行序列化和反序列化。如果沒有顯示指定serialVersionUID,會自動生成一個。 只有同一次編譯生成的class才會生成相同的serialVersionUID。 但是如果出現(xiàn)需求變動,Bean類發(fā)生改變,則會導(dǎo)致反序列化失敗。為了不出現(xiàn)這類的問題,所以我們最好還是顯式的指定一個 serialVersionUID。
六、序列化的其他問題
1、靜態(tài)變數(shù)不會被序列化( static,transient)2、當(dāng)一個父類別實作序列化,子類別自動實作序列化,不需要明確實作Serializable介面。 3、當(dāng)一個對象的實例變數(shù)引用其他對象,序列化該對象時也把引用對象進行序列化。子類序列化時: 如果父類沒有實現(xiàn)Serializable接口,沒有提供默認構(gòu)造函數(shù),那么子類的序列化會出錯; 如果父類沒有實現(xiàn)Serializable接口,提供了默認的構(gòu)造函數(shù),那么子類可以序列化,父類的成員變量不會被序列化。如果父類 實現(xiàn)了Serializable接口,則父類和子類都可以序列化。
七、使用效率更高的序列化框架—Protostuff
<dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.5.9</version> </dependency>
<dependency> <groupId>io.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.5.9</version> </dependency>修改Main程式碼
import com.dyuproject.protostuff.LinkedBuffer; import com.dyuproject.protostuff.ProtobufIOUtil; import com.dyuproject.protostuff.ProtostuffIOUtil; import com.dyuproject.protostuff.Schema; import com.dyuproject.protostuff.runtime.RuntimeSchema; public class Main { public static void main(String[] args) { User user = new User(); user.setName("旭旭寶寶"); user.setAge(33); Schema<User> schema = RuntimeSchema.getSchema(User.class); // 保存對象,序列化,轉(zhuǎn)化二進制數(shù)據(jù) LinkedBuffer buffer = LinkedBuffer.allocate(512); final byte[] protostuff; try { protostuff = ProtobufIOUtil.toByteArray(user, schema, buffer); } finally { buffer.clear(); } // 讀取對象,反序列化 User userObject = schema.newMessage(); ProtostuffIOUtil.mergeFrom(protostuff, userObject, schema); System.out.println(userObject); } }User類,並未實作Serializable介面
public class User { private String name; private int age; 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; } @Override public String toString() { return "User [name=" + name + ", age=" + age + "]"; } }測試結(jié)果:
#
若要要整合Redis使用,也可以寫成一個工具類:
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.dyuproject.protostuff.LinkedBuffer; import com.dyuproject.protostuff.ProtobufIOUtil; import com.dyuproject.protostuff.Schema; import com.dyuproject.protostuff.runtime.RuntimeSchema; public class SerializeUtil { private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>(); @SuppressWarnings("unchecked") public static <T> byte[] serializer(T obj) { Class<T> clazz = (Class<T>) obj.getClass(); Schema<T> schema = getSchema(clazz); return ProtobufIOUtil.toByteArray(obj, schema, LinkedBuffer.allocate(256)); } public static <T> T deSerializer(byte[] bytes, Class<T> clazz) { T message; try { message = clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } Schema<T> schema = getSchema(clazz); ProtobufIOUtil.mergeFrom(bytes, message, schema); return message; } @SuppressWarnings("unchecked") public static <T> Schema<T> getSchema(Class<T> clazz) { Schema<T> schema = (Schema<T>) cachedSchema.get(clazz); if (schema == null) { schema = RuntimeSchema.createFrom(clazz); if (schema != null) { cachedSchema.put(clazz, schema); } } return schema; } }
這樣即使我們的User類就不用再實現(xiàn)Serialiable接口了,同樣可以進行序列化,效率也更高。
php中文網(wǎng),大量的免費Java入門教程,歡迎在線學(xué)習(xí)!
以上是java如何序列化的詳細內(nèi)容。更多資訊請關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費脫衣圖片

Undresser.AI Undress
人工智慧驅(qū)動的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

yield關(guān)鍵字用於創(chuàng)建生成器,按需產(chǎn)生值,節(jié)省內(nèi)存。 1.替代return生成有限序列,如斐波那契數(shù)列;2.實現(xiàn)無限序列,如自然數(shù)列;3.處理大數(shù)據(jù)或文件讀取,逐行處理避免內(nèi)存溢出;4.注意生成器只能遍歷一次,可用next()或for循環(huán)調(diào)用。

VariableVariables是PHP中一種將變量值作為另一個變量名使用的特性,它通過$$var的形式實現(xiàn)動態(tài)訪問變量、處理表單輸入和構(gòu)建靈活配置結(jié)構(gòu)等功能。例如$name="age";echo$$name相當(dāng)於輸出$age的值;常見使用場景包括:1.動態(tài)訪問變量,如${$type.'_info'}可根據(jù)條件選擇不同變量;2.處理表單輸入時自動賦值,但需注意安全隱患;3.構(gòu)建靈活的配置結(jié)構(gòu),通過字符串名稱獲取對應(yīng)值;使用時需注意代碼維護性、命名衝突和調(diào)試難度等問題,建議僅

PHP基礎(chǔ)語法包括:1.使用包裹代碼;2.用echo或print輸出內(nèi)容,其中echo支持多參數(shù);3.變量無需聲明類型,以$開頭,常見類型有字符串、整數(shù)、浮點數(shù)、布爾值、數(shù)組和對象。掌握這些要點有助於快速入門PHP開發(fā)。

PHP有8種變量類型,常用包括Integer、Float、String、Boolean、Array、Object、NULL和Resource。要查看變量類型,可使用gettype()或is_type()系列函數(shù)。 PHP會自動轉(zhuǎn)換類型,但建議關(guān)鍵邏輯用===嚴格比較。手動轉(zhuǎn)換可用(int)、(string)等語法,但注意可能丟失信息。

PHP文件是一種服務(wù)器端腳本語言文件,用於動態(tài)網(wǎng)頁開發(fā),能處理表單數(shù)據(jù)、連接數(shù)據(jù)庫、生成動態(tài)內(nèi)容、控制訪問權(quán)限。它以.php結(jié)尾,代碼在服務(wù)器上執(zhí)行後返回結(jié)果給瀏覽器。要運行PHP文件需安裝本地服務(wù)器環(huán)境如XAMPP,把文件放至服務(wù)器目錄並通過瀏覽器訪問。 PHP通常與HTML混合使用,建議學(xué)習(xí)前先掌握HTML、CSS、JavaScript及基本編程概念,多練習(xí)可快速上手。

PHP變量使用常見錯誤包括未定義變量、引用賦值不當(dāng)、類型比較不嚴謹和全局變量混亂。 1.忽略變量未定義會引發(fā)Notice錯誤,應(yīng)使用isset()或empty()檢查;2.引用賦值修改變量會影響其他變量,應(yīng)在循環(huán)後unset()清理;3.使用==會導(dǎo)致類型自動轉(zhuǎn)換,應(yīng)優(yōu)先使用===進行全等判斷;4.全局變量易造成混亂,建議避免或封裝成類屬性以提高代碼清晰度。

PHP變量以$開頭,命名需遵循規(guī)則,如不能以數(shù)字開頭、區(qū)分大小寫;變量作用域分為局部、全局和超全局;使用global可訪問全局變量,但建議用參數(shù)傳遞;可變變量和引用賦值需謹慎使用。變量是存儲數(shù)據(jù)的基礎(chǔ),正確掌握其規(guī)則和機制對開發(fā)至關(guān)重要。

開發(fā)Go網(wǎng)絡(luò)掃描器需把握四個核心點:1.選擇合適的庫如net、gopacket;2.理解ICMP、TCP、SYN、UDP等底層協(xié)議;3.利用goroutine和channel設(shè)計並發(fā)機制並控制數(shù)量;4.確保掃描合規(guī)性避免濫用。網(wǎng)絡(luò)掃描基本方式包括ICMP探測主機存活、TCP/SYN/UDP端口檢測等,Go的net庫可實現(xiàn)基礎(chǔ)掃描,gopacket支持原始數(shù)據(jù)包操作。通過限制goroutine數(shù)量配合WaitGroup和緩衝channel可提升效率。注意事項包括合法授權(quán)、速率控制、避免公網(wǎng)大規(guī)模掃
