版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第一篇文章帶你了解Java中序列化與反序列化目錄一、序列化和反序列化概念二、序列化和反序列化的必要性三、序列化和反序列化的實現(xiàn)1.JDK類庫提供的序列化API2.實現(xiàn)序列化的要求3.實現(xiàn)Java對象序列化與反序列化的方法4.JDK類庫中序列化的步驟5.JDK類庫中反序列化的步驟四、序列化的必要條件五、序列化高級,使用情境分析1.序列化ID問題特性使用案例2.靜態(tài)變量序列化3.父類的序列化與Transient關(guān)鍵字4.對敏感字段加密5.序列化存儲規(guī)則總結(jié)
一、序列化和反序列化概念
Serialization(序列化)是一種將對象以一連串的字節(jié)描述的過程;反序列化deserialization是一種將這些字節(jié)重建成一個對象的過程。將程序中的對象,放入文件中保存就是序列化,將文件中的字節(jié)碼重新轉(zhuǎn)成對象就是反序列化。
二、序列化和反序列化的必要性
當兩個進程進行遠程通信時,可以相互發(fā)送各種類型的數(shù)據(jù),包括文本、圖片、音頻、視頻等,而這些數(shù)據(jù)都會以二進制序列的形式在網(wǎng)絡(luò)上傳送。
而java是面向?qū)ο蟮拈_發(fā)方式,一切都是java對象,想要實現(xiàn)java對象的網(wǎng)絡(luò)傳輸,就可以使用序列化和反序列化來實現(xiàn)。發(fā)送方將需要發(fā)送的Java對象序列化轉(zhuǎn)換為字節(jié)序列,然后在網(wǎng)絡(luò)上傳送;接收方接收到字符序列后,使用反序列化從字節(jié)序列中恢復(fù)出Java對象。
當我們了解了為什么需要Java序列化和反序列化后,我們很自然地會想Java序列化的好處。
一是實現(xiàn)了數(shù)據(jù)的持久化,通過序列化可以把數(shù)據(jù)永久地保存到硬盤上(通常存放在文件里);
二是利用序列化實現(xiàn)遠程通信,即在網(wǎng)絡(luò)上傳送對象的字節(jié)序列。
總結(jié),在網(wǎng)絡(luò)中數(shù)據(jù)的傳輸必須是序列化形式來進行的。其他序列化的方式可以是json傳輸,xml形式傳輸。
三、序列化和反序列化的實現(xiàn)
1.JDK類庫提供的序列化API
java.io.ObjectOutputStream:表示對象輸出流它的writeObject(Objectobj)方法可以對參數(shù)指定的obj對象進行序列化,把得到的字節(jié)序列寫到一個目標輸出流中。
java.io.ObjectInputStream:表示對象輸入流它的readObject()方法從源輸入流中讀取字節(jié)序列,再把它們反序列化成為一個對象,并將其返回。
2.實現(xiàn)序列化的要求
只有實現(xiàn)了Serializable或Externalizable接口的類的對象才能被序列化,否則拋出異常。
3.實現(xiàn)Java對象序列化與反序列化的方法
假定一個Student類,它的對象需要序列化,可以有如下三種方法:
方法一:
若Student類僅僅實現(xiàn)了Serializable接口,則可以按照以下方式進行序列化和反序列化。ObjectOutputStream采用默認的序列化方式,對Student對象的非transient的實例變量進行序列化。ObjcetInputStream采用默認的反序列化方式,對對Student對象的非transient的實例變量進行反序列化。
方法二:
若Student類僅僅實現(xiàn)了Serializable接口,并且還定義了readObject(ObjectInputStreamin)和writeObject(ObjectOutputSteamout),則采用以下方式進行序列化與反序列化。ObjectOutputStream調(diào)用Student對象的writeObject(ObjectOutputStreamout)的方法進行序列化。ObjectInputStream會調(diào)用Student對象的readObject(ObjectInputStreamin)的方法進行反序列化。
方法三:
若Student類實現(xiàn)了Externalnalizable接口,且Student類必須實現(xiàn)readExternal(ObjectInputin)和writeExternal(ObjectOutputout)方法,則按照以下方式進行序列化與反序列化。ObjectOutputStream調(diào)用Student對象的writeExternal(ObjectOutputout))的方法進行序列化。ObjectInputStream會調(diào)用Student對象的readExternal(ObjectInputin)的方法進行反序列化。
4.JDK類庫中序列化的步驟
步驟一:創(chuàng)建一個對象輸出流,它可以包裝一個其它類型的目標輸出流,如文件輸出流:
ObjectOutputStreamout=newObjectOutputStream(newfileOutputStream(“D:\\objectfile.obj”));
步驟二:通過對象輸出流的writeObject()方法寫對象:
out.writeObject(“Hello”);out.writeObject(newDate());
5.JDK類庫中反序列化的步驟
步驟一:創(chuàng)建一個對象輸入流,它可以包裝一個其它類型輸入流,如文件輸入流:
ObjectInputStreamin=newObjectInputStream(newfileInputStream(“D:\\objectfile.obj”));
步驟二:通過對象輸出流的readObject()方法讀取對象:
Stringobj1=(String)in.readObject();Dateobj2=(Date)in.readObject();
說明:為了正確讀取數(shù)據(jù),完成反序列化,必須保證向?qū)ο筝敵隽鲗憣ο蟮捻樞蚺c從對象輸入流中讀對象的順序一致。為了更好地理解Java序列化與反序列化,選擇方法一編碼實現(xiàn)。
Student類定義如下:
*實現(xiàn)了序列化接口的學(xué)生類
publicclassStudentimplementsSerializable{
privateStringname;
privatecharsex;
privateintyear;
privatedoublegpa;
publicStudent(){
publicStudent(Stringname,charsex,intyear,doublegpa){
=name;
this.sex=sex;
this.year=year;
this.gpa=gpa;
publicvoidsetName(Stringname){
=name;
publicvoidsetSex(charsex){
this.sex=sex;
publicvoidsetYear(intyear){
this.year=year;
publicvoidsetGpa(doublegpa){
this.gpa=gpa;
publicStringgetName(){
return;
publicchargetSex(){
returnthis.sex;
publicintgetYear(){
returnthis.year;
publicdoublegetGpa(){
returnthis.gpa;
把Student類的對象序列化到文件/Users/sschen/Documents/student.txt,并從該文件中反序列化,向console顯示結(jié)果。代碼如下:
publicclassUserStudent{
publicstaticvoidmain(String[]args){
Studentst=newStudent("Tom",'M',20,3.6);
Filefile=newFile("/Users/sschen/Documents/student.txt");
try{
file.createNewFile();
catch(IOExceptione){
e.printStackTrace();
try{
//Student對象序列化過程
FileOutputStreamfos=newFileOutputStream(file);
ObjectOutputStreamoos=newObjectOutputStream(fos);
oos.writeObject(st);
oos.flush();
oos.close();
fos.close();
//Student對象反序列化過程
FileInputStreamfis=newFileInputStream(file);
ObjectInputStreamois=newObjectInputStream(fis);
Studentst1=(Student)ois.readObject();
System.out.println("name="+st1.getName());
System.out.println("sex="+st1.getSex());
System.out.println("year="+st1.getYear());
System.out.println("gpa="+st1.getGpa());
ois.close();
fis.close();
catch(ClassNotFoundExceptione){
e.printStackTrace();
catch(IOExceptione){
e.printStackTrace();
而查看文件/Users/sschen/Documents/student.txt,其內(nèi)保存的內(nèi)容并不是可以容易閱讀的內(nèi)容:
aced00057372001f636f6d2e73736368656e2e53657269616c697a61626c652e53747564656e74f15dbda4a034724d020004440003677061430003736578490004796561724c00046e616d657400124c6a6176612f6c616e672f537472696e673b7870400ccccccccccccd004d00000014740003546f6d
四、序列化的必要條件
1、必須是同包,同名。
2、serialVersionUID必須一致。有時候兩個類的屬性稍微不一致的時候,可以通過將此屬性寫死值,實現(xiàn)序列化和反序列化。
五、序列化高級,使用情境分析
1.序列化ID問題
情境:兩個客戶端A和B試圖通過網(wǎng)絡(luò)傳遞對象數(shù)據(jù),A端將對象C序列化為二進制數(shù)據(jù)再傳給B,B反序列化得到C。
問題:C對象的全類路徑假設(shè)為com.inout.Test,在A和B端都有這么一個類文件,功能代碼完全一致。也都實現(xiàn)了Serializable接口,但是反序列化時總是提示不成功。
解決:虛擬機是否允許反序列化,不僅取決于類路徑和功能代碼是否一致,一個非常重要的一點是兩個類的序列化ID是否一致(就是privatestaticfinallongserialVersionUID=1L)。下面的代碼中,雖然兩個類的功能代碼完全一致,但是序列化ID不同,他們無法相互序列化和反序列化。
簡單來說,Java的序列化機制是通過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節(jié)流中的serialVersionUID與本地相應(yīng)實體(類)的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現(xiàn)序列化版本不一致的異常。
當實現(xiàn)java.io.Serializable接口的實體(類)沒有顯式地定義一個名為serialVersionUID,類型為long的變量時,Java序列化機制會根據(jù)編譯的class自動生成一個serialVersionUID作序列化版本比較用,這種情況下,只有同一次編譯生成的class才會生成相同的serialVersionUID。
如果我們不希望通過編譯來強制劃分軟件版本,即實現(xiàn)序列化接口的實體能夠兼容先前版本,未作更改的類,就需要顯式地定義一個名為serialVersionUID,類型為long的變量,不修改這個變量值的序列化實體都可以相互進行串行化和反串行化。相同功能代碼不同序列化ID的類對比,代碼如下:
publicclassSerialVersionIDAimplementsSerializable{
privatestaticfinallongserialVersionUID=1L;
privateStringname;
publicStringgetName(){
returnname;
publicvoidsetName(Stringname){
=name;
publicSerialVersionIDA(){
publicSerialVersionIDA(Stringname){
=name;
publicclassSerialVersionIDAimplementsSerializable{
privatestaticfinallongserialVersionUID=2L;
privateStringname;
publicStringgetName(){
returnname;
publicvoidsetName(Stringname){
=name;
publicSerialVersionIDA(){
publicSerialVersionIDA(Stringname){
=name;
使用serialVersionUID為1L的類進行序列化,而使用serialVersionUID為2L的類進行反序列化,會提示異常,異常內(nèi)容為:
java.io.InvalidClassException:com.sschen.Serializable.SerialVersionIDA;localclassincompatible:streamclassdescserialVersionUID=2,localclassserialVersionUID=1atjava.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)atjava.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1630)atjava.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)atjava.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)atjava.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)atjava.io.ObjectInputStream.readObject(ObjectInputStream.java:373)atcom.sschen.Serializable.SerialVersionTest.main(SerialVersionTest.java:30)atsun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethod)atsun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)atjava.lang.reflect.Method.invoke(Method.java:498)atellij.rt.execution.application.AppMain.main(AppMain.java:147)
序列化ID在Eclipse下提供了兩種生成策略,一個是固定的1L,一個是隨機生成一個不重復(fù)的long類型數(shù)據(jù)(實際上是使用JDK工具生成),在這里有一個建議,如果沒有特殊需求,就是用默認的1L就可以,這樣可以確保代碼一致時反序列化成功。那么隨機生成的序列化ID有什么作用呢,有些時候,通過改變序列化ID可以用來限制某些用戶的使用。
特性使用案例
讀者應(yīng)該聽過Faade模式,它是為應(yīng)用程序提供統(tǒng)一的訪問接口,案例程序中的Client客戶端使用了該模式,案例程序結(jié)構(gòu)圖下圖所示。
Client端通過FaadeObject才可以與業(yè)務(wù)邏輯對象進行交互。而客戶端的FaadeObject不能直接由Client生成,而是需要Server端生成,然后序列化后通過網(wǎng)絡(luò)將二進制對象數(shù)據(jù)傳給Client,Client負責反序列化得到Faade對象。該模式可以使得Client端程序的使用需要服務(wù)器端的許可,同時Client端和服務(wù)器端的FaadeObject類需要保持一致。當服務(wù)器端想要進行版本更新時,只要將服務(wù)器端的FaadeObject類的序列化ID再次生成,當Client端反序列化FaadeObject就會失敗,也就是強制Client端從服務(wù)器端獲取最新程序。
2.靜態(tài)變量序列化
publicclassSerialStaticTestimplementsSerializable{
privatestaticfinallongserialVersionUID=1L;
publicstaticintstaticVar=5;
publicstaticvoidmain(String[]args){
try{
Filefile=newFile("/Users/sschen/Documents/student.txt");
try{
file.createNewFile();
catch(IOExceptione){
e.printStackTrace();
//初始時staticVar為5
ObjectOutputStreamout=newObjectOutputStream(
newFileOutputStream(file));
out.writeObject(newSerialStaticTest());
out.close();
//序列化后修改為10
SerialStaticTest.staticVar=10;
ObjectInputStreamoin=newObjectInputStream(newFileInputStream(file));
SerialStaticTestt=(SerialStaticTest)oin.readObject();
oin.close();
//再讀取,通過t.staticVar打印新的值
System.out.println(t.staticVar);
}catch(FileNotFoundExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}catch(ClassNotFoundExceptione){
e.printStackTrace();
上面代碼中的main方法,將對象序列化保存到文件后,修改靜態(tài)變量的數(shù)值,再將序列化對象讀取出來,然后通過讀取出來的對象獲得靜態(tài)變量的數(shù)值并打印出來。依照代碼,這個System.out.println(t.staticVar)語句輸出的是10還是5呢?最后的輸出是10,對于無法理解的讀者認為,打印的staticVar是從讀取的對象里獲得的,應(yīng)該是保存時的狀態(tài)才對。之所以打印10的原因在于序列化時,并不保存靜態(tài)變量,這其實比較容易理解,序列化保存的是對象的狀態(tài),靜態(tài)變量屬于類的狀態(tài),因此序列化并不保存靜態(tài)變量。
3.父類的序列化與Transient關(guān)鍵字
情境:一個子類實現(xiàn)了Serializable接口,它的父類都沒有實現(xiàn)Serializable接口,序列化該子類對象,然后反序列化后輸出父類定義的某變量的數(shù)值,該變量數(shù)值與序列化時的數(shù)值不同。
解決:要想將父類對象也序列化,就需要讓父類也實現(xiàn)Serializable接口。如果父類不實現(xiàn)的話的,就需要有默認的無參的構(gòu)造函數(shù)。在父類沒有實現(xiàn)Serializable接口時,虛擬機是不會序列化父對象的,而一個Java對象的構(gòu)造必須先有父對象,才有子對象,反序列化也不例外。所以反序列化時,為了構(gòu)造父對象,只能調(diào)用父類的無參構(gòu)造函數(shù)作為默認的父對象。因此當我們?nèi)「笇ο蟮淖兞恐禃r,它的值是調(diào)用父類無參構(gòu)造函數(shù)后的值。如果你考慮到這種序列化的情況,在父類無參構(gòu)造函數(shù)中對變量進行初始化,否則的話,父類變量值都是默認聲明的值,如int型的默認是0,string型的默認是null。
Transient關(guān)鍵字的作用是控制變量的序列化,在變量聲明前加上該關(guān)鍵字,可以阻止該變量被序列化到文件中,在被反序列化后,transient變量的值被設(shè)為初始值,如int型的是0,對象型的是null。
特性使用案例
我們熟悉使用Transient關(guān)鍵字可以使得字段不被序列化,那么還有別的方法嗎?根據(jù)父類對象序列化的規(guī)則,我們可以將不需要被序列化的字段抽取出來放到父類中,子類實現(xiàn)Serializable接口,父類不實現(xiàn),根據(jù)父類序列化規(guī)則,父類的字段數(shù)據(jù)將不被序列化,形成類圖如圖2所示。
上圖中可以看出,attr1、attr2、attr3、attr5都不會被序列化,放在父類中的好處在于當有另外一個Child類時,attr1、attr2、attr3依然不會被序列化,不用重復(fù)抒寫transient,代碼簡潔。
4.對敏感字段加密
情境:服務(wù)器端給客戶端發(fā)送序列化對象數(shù)據(jù),對象中有一些數(shù)據(jù)是敏感的,比如密碼字符串等,希望對該密碼字段在序列化時,進行加密,而客戶端如果擁有解密的密鑰,只有在客戶端進行反序列化時,才可以對密碼進行讀取,這樣可以一定程度保證序列化對象的數(shù)據(jù)安全。
解決:在序列化過程中,虛擬機會試圖調(diào)用對象類里的writeObject和readObject方法,進行用戶自定義的序列化和反序列化,如果沒有這樣的方法,則默認調(diào)用是ObjectOutputStream的defaultWriteObject方法以及ObjectInputStream的defaultReadObject方法。用戶自定義的writeObject和readObject方法可以允許用戶控制序列化的過程,比如可以在序列化的過程中動態(tài)改變序列化的數(shù)值?;谶@個原理,可以在實際應(yīng)用中得到使用,用于敏感字段的加密工作,下面的代碼展示了這個過程。
publicclassSerialPwdTestimplementsSerializable{
privatestaticfinallongserialVersionUID=1L;
privateStringpassword="pass";
publicStringgetPassword(){
returnpassword;
publicvoidsetPassword(Stringpassword){
this.password=password;
privatevoidwriteObject(ObjectOutputStreamout){
try{
ObjectOutputStream.PutFieldputFields=out.putFields();
System.out.println("原密碼:"+password);
password="encryption";//模擬加密
putFields.put("password",password);
System.out.println("加密后的密碼"+password);
out.writeFields();
}catch(IOExceptione){
e.printStackTrace();
privatevoidreadObject(ObjectInputStreamin){
try{
ObjectInputStream.GetFieldreadFields=in.readFields();
Objectobject=readFields.get("password","");
System.out.println("要解密的字符串:"+object.toString());
password="pass";//模擬解密,需要獲得本地的密鑰
}catch(IOExceptione){
e.printStackTrace();
}catch(ClassNotFoundExceptione){
e.printStackTrace();
publicstaticvoidmain(String[]args){
Filefile=newFile("/Users/sschen/Documents/student.txt");
try{
file.createNewFile();
catch(IOExceptione){
e.printStackTrace();
try{
ObjectOutputStreamout=newObjectOutputStream(newFileOutputStream(file));
out.writeObject(newSerialPwdTest());
out.close();
ObjectInputStreamoin=newObjectInputStream(newFileInputStream(file));
SerialPwdTestt=(SerialPwdTest)oin.readObject();
System.out.println("解密后的字符串:"+t.getPassword());
oin.close();
}catch(FileNotFoundExceptione){
e.printStackTrace();
}catch(IOExceptione){
e.printStackTrace();
}catch(ClassNotFoundExceptione){
e.printStackTrace();
SerialPwdTest的writeObject方法中,對密碼進行了加密,在加密后進行序列化保存到文件中,在readObject中則在讀取到密碼后,對password進行解密,只有擁有密鑰的客戶端,才可以正確的解析出密碼,確保了數(shù)據(jù)的安全。上面代碼的執(zhí)行結(jié)果為:
原密碼:pass加密后的密碼encryption要解密的字符串:encryption解密后的字符串:pass
特性使用案例
RMI技術(shù)是完全基于Java序列化技術(shù)的,服務(wù)器端接口調(diào)用所需要的參數(shù)對象來至于客戶端,它們通過網(wǎng)絡(luò)相互傳輸。這就涉及RMI的安全傳輸?shù)膯栴}。一些敏感的字段,如用戶名密碼(用戶登錄時需要對密碼進行傳輸),我們希望對其進行加密,這時,就可以采用本節(jié)介紹的方法在客戶端對密碼進行加密,服務(wù)器端進行解密,確保數(shù)據(jù)傳輸?shù)陌踩浴?/p>
5.序列化存儲規(guī)則
情境:問題代碼如清單4所示。
清單4.存儲規(guī)則問題代碼
publicclassSerialSaveTestimplementsSerializable{
publicstaticvoidmain(String[]args){
Filefile=newFile("/Users/sschen/Documents/student.txt");
try{
file.createNewFile();
catch(IOExceptione){
e.printStackTrace();
try{
ObjectOutputStreamout=newObjectOutputStream(newFileOutputStream(file));
SerialSaveTesttest=newSerialSaveTest();
//試圖將對象兩次寫入文件
out.writeObject(test);
out.flush();
System.out.println(file.length());
out.writeObject(test);
out.close();
System.out.println(file.length());
ObjectInputStreamoin=newObjectInputStream(newFileInputStream(file));
//從文件依次讀出兩個文件
SerialSaveTestt1=(SerialSaveTest)oin.readObject();
SerialSaveTestt2=(SerialSaveTest)oin.readObject();
oin.close();
//判斷兩個引用是否指向同一個對象
System.out.println(t1==t2);
}catch(IOExceptione){
e.printStackTrace();
}catch(ClassNotFoundExceptione){
e.printStackTrace();
清單4中對同一對象兩次寫入文件,打印出寫入一次對象后的存儲大小和寫入兩次后的存儲大小,然后從文件中反序列化出兩個對象,比較這兩個對象是否為同一對象。一般的思維是,兩次寫入對象,文件大小會變?yōu)閮杀兜拇笮?,反序列化時,由于從文件讀取,生成了兩個對象,判斷相等時應(yīng)該是輸入false才對,但是最后結(jié)果輸出如下:
5964true
我們看到,第二次寫入對象時文件只增加了5字節(jié),并且兩個對象是相等的,這是為什么呢?
解答:Java序列化機制為了節(jié)省磁盤空間,具有特定的存儲規(guī)則,當寫入文件的為同一對象時,并不會再將對象的內(nèi)容進行存儲,而只是再次存儲一份引用,上面增加的5字節(jié)的存儲空間就是新增引用和一些控制信息的空間。反序列化時,恢復(fù)引用關(guān)系,使得清單3中的t1和t2指向唯一的對象,二者相等,輸出true。該存儲規(guī)則極大的節(jié)省了存儲空間。
特性案例分析
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 臨潭事業(yè)編招聘2022年考試模擬試題及答案解析42
- 銅仁2025年貴州銅仁市沿河縣消防救援大隊招錄政府專職消防員24人筆試歷年參考題庫附帶答案詳解
- 衢州2025年浙江衢州常山縣衛(wèi)健系統(tǒng)事業(yè)單位招考編外工作人員39人筆試歷年參考題庫附帶答案詳解
- 綿陽四川綿陽市公安局招聘警務(wù)輔助人員76人筆試歷年參考題庫附帶答案詳解
- 濰坊2025年山東濰坊濱海區(qū)招聘城市社區(qū)專職工作人員79人筆試歷年參考題庫附帶答案詳解
- 河北2025年河北工藝美術(shù)職業(yè)學(xué)院選聘5人筆試歷年參考題庫附帶答案詳解
- 撫州2025年江西撫州市教育體育局直屬學(xué)校選調(diào)教師47人筆試歷年參考題庫附帶答案詳解
- 廣西2025年廣西輻射環(huán)境監(jiān)督管理站招聘筆試歷年參考題庫附帶答案詳解
- 山東2025年山東省文物考古研究院招聘2人筆試歷年參考題庫附帶答案詳解
- 寧德2025年福建寧德市公安局招聘輔警39人筆試歷年參考題庫附帶答案詳解
- 藥物警戒基礎(chǔ)知識全員培訓(xùn)
- 骨密度檢測的臨床意義
- 鉆探原始班報表試行版
- 腸菌移植治療炎癥性腸病專家共識(2025)解讀
- T/CPPC 1032-2021建筑生產(chǎn)資源分供商評價規(guī)范
- 機耕合同協(xié)議書范本簡單
- 送車免責合同協(xié)議書模板
- 外科學(xué)重癥監(jiān)測治療與復(fù)蘇
- 瓊脂糖-纖維素復(fù)合分離介質(zhì)
- 早產(chǎn)兒家庭參與式護理
- 2025年招商引資項目可行性研究報告范文
評論
0/150
提交評論