内置序列化技术
本文是我們名為“ 高級(jí)Java ”的學(xué)院課程的一部分。
本課程旨在幫助您最有效地使用Java。 它討論了高級(jí)主題,包括對(duì)象創(chuàng)建,并發(fā),序列化,反射等。 它將指導(dǎo)您完成Java掌握的過(guò)程! 在這里查看 !
目錄
1.簡(jiǎn)介 2.可序列化的接口 3.可外部化的界面 4.有關(guān)可序列化接口的更多信息 5.可序列化和遠(yuǎn)程方法調(diào)用(RMI) 6. JAXB 7. JSON-P 8.序列化成本 9.超越Java標(biāo)準(zhǔn)庫(kù)和規(guī)范 10.下一步是什么 11.下載源代碼1.簡(jiǎn)介
本教程的這一部分將專門用于序列化 :將Java對(duì)象轉(zhuǎn)換為一種格式的過(guò)程,該格式可用于在同一(或其他)環(huán)境中進(jìn)行存儲(chǔ)和以后的重構(gòu)( http://en.wikipedia。 org / wiki / Serialization )。 序列化不僅允許將Java對(duì)象保存到持久性存儲(chǔ)中或從持久性存儲(chǔ)中加載Java對(duì)象,而且還是現(xiàn)代分布式系統(tǒng)通信中非常重要的組件。
序列化并不容易,但是有效的序列化則更加困難。 除了Java標(biāo)準(zhǔn)庫(kù)之外,還有許多可用的序列化技術(shù)和框架:其中一些使用緊湊的二進(jìn)制表示形式,而另一些則將可讀性放在首位。 盡管我們將在此過(guò)程中提及許多替代方案,但我們的注意力將集中在Java標(biāo)準(zhǔn)庫(kù)(和最新規(guī)范)中的替代方案: Serializable , Externalizable ,用于XML綁定的Java體系結(jié)構(gòu)( JAXB , JSR-222 )和用于Java的Java API。 JSON處理( JSON-P , JSR-353 )。
2.可序列化的接口
可以說(shuō),Java中將類標(biāo)記為可用于序列化的最簡(jiǎn)單方法是實(shí)現(xiàn)java.io.Serializable接口。 例如:
public class SerializableExample implements Serializable { }序列化運(yùn)行時(shí)與每個(gè)可序列化的類關(guān)聯(lián)一個(gè)特殊的版本號(hào),稱為序列號(hào)UID ,該序列號(hào)在反序列化 (與序列化相反的過(guò)程)中使用,以確保為序列化對(duì)象加載的類兼容。 如果兼容性受到損害,則將InvalidClassException 。
可序列化的類可以通過(guò)聲明名稱為serialVersionUID的字段為static , final且類型為long的字段來(lái)顯式引入其自己的串行版本UID 。 例如:
public class SerializableExample implements Serializable {private static final long serialVersionUID = 8894f47504319602864L; }但是,如果可序列化的類未明確聲明serialVersionUID字段,則序列化運(yùn)行時(shí)將為該類生成一個(gè)默認(rèn)的serialVersionUID字段。 值得一提的是,所有實(shí)現(xiàn)Serializable類都強(qiáng)烈建議顯式聲明serialVersionUID字段,因?yàn)槟J(rèn)的serialVersionUID生成嚴(yán)重依賴于內(nèi)部類的詳細(xì)信息,并且可能會(huì)因Java編譯器實(shí)現(xiàn)及其版本而有所不同。 這樣,為了保證行為的一致性,可序列化的類必須始終聲明一個(gè)顯式的serialVersionUID字段。
一旦該類可序列化(實(shí)現(xiàn)Serializable并聲明serialVersionUID ),就可以使用例如ObjectOutputStream / ObjectInputStream進(jìn)行存儲(chǔ)和檢索:
final Path storage = new File( "object.ser" ).toPath();try( final ObjectOutputStream out = new ObjectOutputStream( Files.newOutputStream( storage ) ) ) {out.writeObject( new SerializableExample() ); }存儲(chǔ)后,可以通過(guò)類似的方式進(jìn)行檢索,例如:
try( final ObjectInputStream in = new ObjectInputStream( Files.newInputStream( storage ) ) ) {final SerializableExample instance = ( SerializableExample )in.readObject();// Some implementation here }如我們所見(jiàn), Serializable接口沒(méi)有對(duì)應(yīng)該序列化什么以及如何進(jìn)行序列化提供很多控制( transient關(guān)鍵字將字段標(biāo)記為不可序列化除外)。 而且,它限制了更改內(nèi)部類表示形式的靈活性,因?yàn)樗赡軙?huì)破壞序列化/反序列化過(guò)程。 這就是為什么引入了另一個(gè)接口Externalizable原因。
3.可外部化的界面
與Serializable接口相反, Externalizable將類應(yīng)如何序列化和反序列化的職責(zé)委托給該類。 它只有兩種方法,這是Java標(biāo)準(zhǔn)庫(kù)中的聲明:
public interface Externalizable extends java.io.Serializable {void writeExternal(ObjectOutput out) throws IOException;void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }反過(guò)來(lái),每個(gè)實(shí)現(xiàn)Externalizable接口的類都應(yīng)提供這兩種方法的實(shí)現(xiàn)。 讓我們看一個(gè)例子:
public class ExternalizableExample implements Externalizable {private String str;private int number;private SerializableExample obj;@Overridepublic void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {setStr(in.readUTF());setNumber(in.readInt());setObj(( SerializableExample )in.readObject());}@Overridepublic void writeExternal(final ObjectOutput out) throws IOException {out.writeUTF(getStr());out.writeInt(getNumber());out.writeObject(getObj());} }與實(shí)現(xiàn)Serializable的類相似,可以使用例如ObjectOutputStream / ObjectInputStream存儲(chǔ)和檢索實(shí)現(xiàn)Externalizable的類:
final Path storage = new File( "extobject.ser" ).toPath();final ExternalizableExample instance = new ExternalizableExample(); instance.setStr( "Sample String" ); instance.setNumber( 10 ); instance.setObj( new SerializableExample() );try( final ObjectOutputStream out = new ObjectOutputStream( Files.newOutputStream( storage ) ) ) {out.writeObject( instance ); }try( final ObjectInputStream in = new ObjectInputStream( Files.newInputStream( storage ) ) ) {final ExternalizableExample obj = ( ExternalizableExample )in.readObject();// Some implementation here }當(dāng)使用Serializable接口的簡(jiǎn)單方法無(wú)法正常工作時(shí),使用Externalizable接口可以進(jìn)行細(xì)粒度的序列化/反序列化自定義。
4.有關(guān)可序列化接口的更多信息
在上一節(jié)中,我們提到了Serializable接口并沒(méi)有對(duì)應(yīng)該序列化什么以及如何序列化提供很多控制。 實(shí)際上,它并不是完全正確的(至少在使用ObjectOutputStream / ObjectInputStream時(shí))。 任何可序列化的類都可以實(shí)現(xiàn)一些特殊方法,以控制默認(rèn)的序列化和反序列化。
private void writeObject(ObjectOutputStream out) throws IOException;此方法負(fù)責(zé)為其特定類編寫對(duì)象的狀態(tài),以便相應(yīng)的readObject方法可以將其恢復(fù)(可以通過(guò)調(diào)用out.defaultWriteObject調(diào)用保存對(duì)象字段的默認(rèn)機(jī)制)。
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException;此方法負(fù)責(zé)從流中讀取并還原對(duì)象的狀態(tài)(可通過(guò)調(diào)用in.defaultReadObject調(diào)用還原對(duì)象字段的默認(rèn)機(jī)制)。
private void readObjectNoData() throws ObjectStreamException;在序列化流未將給定類列為要反序列化的對(duì)象的超類的情況下,此方法負(fù)責(zé)初始化對(duì)象的狀態(tài)。
Object writeReplace() throws ObjectStreamException;當(dāng)可序列化的類需要指定將對(duì)象寫入流時(shí)要使用的替代對(duì)象時(shí),使用此方法。
Object readResolve() throws ObjectStreamException;最后,當(dāng)從流中讀取可序列化的類的實(shí)例時(shí),可序列化的類需要指定替換時(shí),使用此方法。
一旦知道內(nèi)在的實(shí)現(xiàn)細(xì)節(jié)和要使用的特殊方法,默認(rèn)的序列化機(jī)制(使用Serializable接口)在Java中就會(huì)變得非常麻煩。 您正在編寫用于支持序列化的更多代碼,更有可能展示出更多的錯(cuò)誤和漏洞。
但是,有一種方法可以通過(guò)使用名為Serialization Proxy的非常簡(jiǎn)單的模式來(lái)降低這些風(fēng)險(xiǎn),該模式基于利用writeReplace和readResolve方法。 這種模式的基本思想是引入專用的伴隨類進(jìn)行序列化(通常作為private static內(nèi)部類),以補(bǔ)充需要序列化的類。 讓我們看一下這個(gè)例子:
public class SerializationProxyExample implements Serializable {private static final long serialVersionUID = 6163321482548364831L;private String str;private int number; public SerializationProxyExample( final String str, final int number) {this.setStr(str);this.setNumber(number);}private void readObject(ObjectInputStream stream) throws InvalidObjectException {throw new InvalidObjectException( "Serialization Proxy is expected" );}private Object writeReplace() {return new SerializationProxy( this );}// Setters and getters here }對(duì)此類的實(shí)例進(jìn)行序列化時(shí),類SerializationProxyExample實(shí)現(xiàn)將提供替換對(duì)象( SerializationProxy類的實(shí)例)。 這意味著SerializationProxyExample類的實(shí)例將永遠(yuǎn)不會(huì)直接序列化(和反序列化)。 它還說(shuō)明了為什么以某種方式進(jìn)行反序列化嘗試時(shí), readObject方法會(huì)引發(fā)異常。 現(xiàn)在,讓我們看一下伴隨的SerializationProxy類:
private static class SerializationProxy implements Serializable {private static final long serialVersionUID = 8368440585226546959L;private String str;private int number;public SerializationProxy( final SerializationProxyExample instance ) {this.str = instance.getStr();this.number = instance.getNumber();}private Object readResolve() {return new SerializationProxyExample(str, number); // Uses public constructor} }在我們的略微簡(jiǎn)化的情況下, SerializationProxy類只是復(fù)制了所有的領(lǐng)域SerializationProxyExample (但可能比被很多復(fù)雜)。 因此,當(dāng)要反序列化此類的實(shí)例時(shí), readResolve調(diào)用readResolve方法,并且SerializationProxy提供替換,這次的形式為SerializationProxyExample實(shí)例。 因此, SerializationProxy類可作為一個(gè)序列化代理SerializationProxyExample類。
5.可序列化和遠(yuǎn)程方法調(diào)用(RMI)
相當(dāng)長(zhǎng)一段時(shí)間以來(lái),Java遠(yuǎn)程方法調(diào)用( RMI )是可用于在Java平臺(tái)上構(gòu)建分布式應(yīng)用程序的唯一機(jī)制。 RMI提供了所有繁重的工作,并且可以從同一主機(jī)或不同物理(或虛擬)主機(jī)上的其他JVM透明地調(diào)用遠(yuǎn)程Java對(duì)象的方法。 RMI的基礎(chǔ)是對(duì)象序列化,該對(duì)象序列化用于編組(序列化)和解組(反序列化)方法參數(shù)。
如今, RMI仍在許多Java應(yīng)用程序中使用,但由于它的復(fù)雜性和通信限制(大多數(shù)防火墻都阻止RMI端口),因此越來(lái)越少選擇RMI 。 要獲取有關(guān)RMI的更多詳細(xì)信息,請(qǐng)參考官方文檔 。
6. JAXB
用于XML綁定的Java體系結(jié)構(gòu),或者只是JAXB ,可能是Java開(kāi)發(fā)人員可以使用的最古老的替代序列化機(jī)制。 在下面,它使用XML作為序列化格式,提供了廣泛的自定義選項(xiàng),并包含許多注釋,這些注釋使JAXB非常吸引人并且易于使用(注釋在本教程的第5部分中介紹了如何以及何時(shí)使用Enums和注釋 )。
讓我們看一個(gè)用JAXB注釋注釋的普通舊Java類(POJO)的簡(jiǎn)化示例:
import java.math.BigDecimal; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement;@XmlAccessorType( XmlAccessType.FIELD ) @XmlRootElement( name = "example" ) public class JaxbExample {@XmlElement(required = true) private String str;@XmlElement(required = true) private BigDecimal number;// Setters and getters here }要使用JAXB基礎(chǔ)結(jié)構(gòu)將該類的實(shí)例序列化為XML格式,唯一需要的是編組器(或序列化器)的實(shí)例,例如:
final JAXBContext context = JAXBContext.newInstance( JaxbExample.class ); final Marshaller marshaller = context.createMarshaller();final JaxbExample example = new JaxbExample(); example.setStr( "Some string" ); example.setNumber( new BigDecimal( 12.33d, MathContext.DECIMAL64 ) );try( final StringWriter writer = new StringWriter() ) {marshaller.marshal( example, writer ); }這是上面示例中JaxbExample類實(shí)例的XML表示形式:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <example><str>Some string</str><number>12.33000000000000</number> </example>按照相同的原則,可以使用解組器(或反序列化器)的實(shí)例將類的實(shí)例從XML表示反序列化回Java對(duì)象,例如:
final JAXBContext context = JAXBContext.newInstance( JaxbExample.class );final String xml = "" +"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\" standalone=\\"yes\\"?>" +"<example>" +" <str>Some string</str>" +" <number>12.33000000000000</number>" +"</example>";final Unmarshaller unmarshaller = context.createUnmarshaller(); try( final StringReader reader = new StringReader( xml ) ) {final JaxbExample example = ( JaxbExample )unmarshaller.unmarshal( reader );// Some implementaion here }正如我們所看到的, JAXB易于使用,并且XML格式在當(dāng)今仍然很受歡迎。 但是,XML的基本陷阱之一是冗長(zhǎng):很多時(shí)候,必要的XML結(jié)構(gòu)元素大大超過(guò)了有效的數(shù)據(jù)有效負(fù)載。
7. JSON-P
自2013年以來(lái),借助新引入的JSON處理Java API( JSON-P ),Java開(kāi)發(fā)人員可以將JSON用作序列化格式。
截至目前,JSON-P是不是Java標(biāo)準(zhǔn)庫(kù)的一部分,雖然有很多討論,包括原生JSON支持到在即將推出的Java 9釋放(語(yǔ)言http://openjdk.java.net/jeps/198 )。 盡管如此,它還是可以作為Java JSON處理參考實(shí)現(xiàn) ( https://jsonp.java.net/ )的一部分獲得的。
與JAXB相比 ,無(wú)需為使該類適合于JSON序列化而添加任何類,例如:
public class JsonExample {private String str;private BigDecimal number;// Setters and getters here }序列化不像JAXB那樣透明,并且需要為要序列化為JSON的每個(gè)類編寫一些代碼,例如:
final JsonExample example = new JsonExample(); example.setStr( "Some string" ); example.setNumber( new BigDecimal( 12.33d, MathContext.DECIMAL64 ) );try( final StringWriter writer = new StringWriter() ) {Json.createWriter(writer).write( Json.createObjectBuilder().add("str", example.getStr() ).add("number", example.getNumber() ).build()); }這是上面示例中JsonExample類實(shí)例的JSON表示形式:
{"str":"Some string","number":12.33000000000000 }反序列化過(guò)程也是如此:
final String json = "{\\"str\\":\\"Some string\\",\\"number\\":12.33000000000000}"; try( final StringReader reader = new StringReader( json ) ) {final JsonObject obj = Json.createReader( reader ).readObject();final JsonExample example = new JsonExample();example.setStr( obj.getString( "str" ) );example.setNumber( obj.getJsonNumber( "number" ).bigDecimalValue() ); }可以說(shuō),目前Java中的JSON支持非常基本。 盡管如此,擁有一個(gè)很棒的東西,Java社區(qū)正在通過(guò)引入用于JSON綁定的Java API (JSON-B, JSR-367 )來(lái)努力豐富JSON支持。 使用此API,與JSON之間的Java對(duì)象序列化和反序列化應(yīng)該像JAXB一樣透明。
8.序列化成本
重要的是要理解,盡管序列化/反序列化在Java中看起來(lái)很簡(jiǎn)單,但它不是免費(fèi)的,并且取決于數(shù)據(jù)模型和數(shù)據(jù)訪問(wèn)模式可能會(huì)消耗大量的網(wǎng)絡(luò)帶寬,內(nèi)存和CPU資源。 不僅如此,盡管如此,Java對(duì)可序列化類提供了某種版本支持(使用序列化UID,如我們?cè)凇翱尚蛄谢涌?”一節(jié)中所見(jiàn)),它確實(shí)使開(kāi)發(fā)過(guò)程變得更加困難,因?yàn)殚_(kāi)發(fā)人員需要自己弄清楚如何管理數(shù)據(jù)模型的演變。
另外要說(shuō)明的是,Java序列化在JVM領(lǐng)域之外無(wú)法正常工作。 對(duì)于使用多種編程語(yǔ)言和運(yùn)行時(shí)構(gòu)建的現(xiàn)代分布式應(yīng)用程序,這是一個(gè)重要的限制。
這就解釋了為什么許多替代的序列化框架和解決方案應(yīng)運(yùn)而生,并成為Java生態(tài)系統(tǒng)中非常流行的選擇。
9.超越Java標(biāo)準(zhǔn)庫(kù)和規(guī)范
在本節(jié)中,我們將從Fast-serialization項(xiàng)目( http://ruedigermoeller.github.io/fast-serialization/ )開(kāi)始,探討無(wú)痛且有效的Java序列化的替代解決方案:快速替換Java序列化。 快速序列化的用法與Java標(biāo)準(zhǔn)庫(kù)提供的用法沒(méi)有太大區(qū)別,但聲稱它更快,更有效。
另一組框架對(duì)此問(wèn)題有不同的看法。 它們基于結(jié)構(gòu)化數(shù)據(jù)定義(或協(xié)議),并將數(shù)據(jù)序列化為緊湊的二進(jìn)制表示形式(甚至可以從定義中生成相應(yīng)的數(shù)據(jù)模型)。 除此之外,這些框架遠(yuǎn)遠(yuǎn)超出了Java平臺(tái),可以用于跨語(yǔ)言/跨平臺(tái)序列化。 該領(lǐng)域中最知名的Java庫(kù)是Google協(xié)議緩沖區(qū) ( https://developers.google.com/protocol-buffers/),Apache Avro ( http://avro.apache.org/ )和Apache Thrift ( https:/ /thrift.apache.org/ )。
10.下一步是什么
在本部分的教程中,我們討論了Java語(yǔ)言及其運(yùn)行時(shí)提供的內(nèi)置序列化技術(shù)。 我們已經(jīng)看到了當(dāng)今的串行化的重要性,當(dāng)時(shí)幾乎所有正在構(gòu)建的單個(gè)應(yīng)用程序都是大型分布式系統(tǒng)的一部分,并且需要與其其余部分(或與其他外部系統(tǒng))進(jìn)行通信。 在本教程的下一部分中,我們將討論Java中的反射和動(dòng)態(tài)語(yǔ)言支持。
11.下載源代碼
您可以在此處下載本課程的源代碼: advanced-java-part-10
翻譯自: https://www.javacodegeeks.com/2015/09/built-in-serialization-techniques.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
- 上一篇: limbo安卓镜像(limbo安卓)
- 下一篇: app mvc框架_Google App