java-Transient关键字、Volatile关键字介绍和序列化、反序列化机制、单例类序列化
Java的serialization提供了一種持久化對象實例的機(jī)制。當(dāng)持久化對象時,可能有一個特殊的對象數(shù)據(jù)成員,我們不想?
用serialization機(jī)制來保存它。為了在一個特定對象的一個域上關(guān)閉serialization,可以在這個域前加上關(guān)鍵字transient。?
transient是Java語言的關(guān)鍵字,用來表示一個域不是該對象串行化的一部分。當(dāng)一個對象被串行化的時候,?
transient型變量的值不包括在串行化的表示中,然而非transient型的變量是被包括進(jìn)去的。?
注意static變量也是可以串行化的?
同時,通過反序列化得到的對象是不同的對象,而且得到的對象不是通過構(gòu)造器得到的,?
也就是說反序列化得到的對象不執(zhí)行構(gòu)造器。
下面進(jìn)行測試:?
新建一個javabean類,代碼:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
測試類,調(diào)用上面的javabean類,進(jìn)行序列化和反序列化,代碼如下:
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream;public class Test{public static void main(String[] args){LoggingInfo loggingInfo = new LoggingInfo("longyin", "123");System.out.println("寫入:"+loggingInfo);ObjectOutputStream objectOutput = null;ObjectInputStream objectInput = null;try {objectOutput = new ObjectOutputStream(new FileOutputStream("test.txt"));objectInput = new ObjectInputStream(new FileInputStream("test.txt"));objectOutput.writeObject(loggingInfo);LoggingInfo info = (LoggingInfo) objectInput.readObject();System.out.println("讀取:"+info);System.out.println("是否相等:"+(info==loggingInfo));} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}finally{if (objectInput != null) {try {objectInput.close();} catch (IOException e) {e.printStackTrace();}}if (objectOutput != null) {try {objectOutput.close();} catch (IOException e) {e.printStackTrace();}}}} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
執(zhí)行結(jié)果:?
通過執(zhí)行結(jié)果,可以對照上面的分析,說明上面的分析是正確的。
- 下面說說Volatile關(guān)鍵字
Java 語言提供了一種稍弱的同步機(jī)制,即 volatile 變量.用來確保將變量的更新操作通知到其他線程,保證了新值能立即同步到主內(nèi)存,以及每次使用前立即從主內(nèi)存刷新. 當(dāng)把變量聲明為volatile類型后,編譯器與運行時都會注意到這個變量是共享的.?
volatile 變量對所有線程是立即可見的,對 volatile 變量所有的寫操作都能立即反應(yīng)到其他線程之中,換句話說:volatile 變量在各個線程中是一致的,所以基于 volatile 變量的運算是線程安全的.?
對于以上的說法,我沒有想到如何用實例進(jìn)行驗證。?
下面只是個人的理解:?
1。如果在類中使用volatile修飾一個變量,并且是static的類型,那么該變量屬于類,是類變量,那么即使多個線程訪問該變量訪問的也是同一個,哪個線程改變它的話,其他線程在訪問它的時候就是最新的值。不存在不同步的問題。
2。如果在類中使用volatile修飾的變量沒有使用static修飾,那就屬于成員變量,那么多個線程在訪問的時候,訪問同一個對象下的該成員變量也不存在不同步的問題。對于同一個對象,該成員變量就一個!線程無論何時訪問都是最新的。
所以能用到volatile關(guān)鍵字解決多線程的不同步問題相當(dāng)少了。
- 序列化和反序列化
正常情況下,一個類實現(xiàn)java序列化很簡單,只需要implements Serializable接口即可,之后該類在跨jvm的傳輸過程中會遵照默認(rèn)java序列化規(guī)則序列化和反序列化;不同jvm版本之間序列化方式稍有不同,但基本上都是兼容的。?
在某些特殊情況下,可能需要自定義序列化和反序列化的行為,看下面例子:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
這段程序表示了一個可序列化的類繼承自一個非序列化的有狀態(tài)超類,期望的結(jié)果是,子類序列化以后傳輸并反序列化回來,原先的值域包括超類的值域都保持不變。
但是輸出是:?
x:10;y:50?
z:100?
x:0;y:0?
z:100?
結(jié)果和期望不符,子類的值域保留下來了,但是超類的值域丟失了,這對jvm來說是正常的,因為超類不可序列化;
為了解決這個問題,只能自定義序列化行為,具體做法是在SerializeDemo里加入以下代碼:
private void writeObject(ObjectOutputStream os) throws IOException { os.defaultWriteObject();//java對象序列化默認(rèn)操作 os.writeInt(getX()); os.writeInt(getY()); } private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { is.defaultReadObject();//java對象反序列化默認(rèn)操作 int x=is.readInt(); int y=is.readInt(); super.init(x,y); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
writeObject和readObject方法為JVM會在序列化和反序列化java對象時會分別調(diào)用的兩個方法,修飾符都是private,沒錯。
我們在序列化的默認(rèn)動作之后將超類里的兩個值域x和y也寫入object流;與之對應(yīng)在反序列化的默認(rèn)操作之后讀入x和y兩個值,然后調(diào)用超類的初始化方法。
再次執(zhí)行程序之后的輸出為:
x:10;y:50?
z:100?
x:10;y:50?
z:100?
另外還有兩個自定義序列化方法writeReplace和readResolve,分別用來在序列化之前替換序列化對象 和 在反序列化之后的對返回對象的處理。一般可以用來避免singleTon對象跨jvm序列化和反序列化時產(chǎn)生多個對象實例,事實上singleTon的對象一旦可序列化,它就不能保證singleTon了。JVM的Enum實現(xiàn)里就是重寫了readResolve方法,由JVM保證Enum的值都是singleTon的,所以建議多使用Enum代替使用writeReplace和readResolve方法。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
注:writeReplace調(diào)用在writeObject前執(zhí)行;readResolve調(diào)用在readObject之后執(zhí)行。?
(以上序列化反序列化機(jī)制部分摘自:http://developer.51cto.com/art/201104/257839.htm)
上面的INSTANCE是單例類的實例。通過上面的代碼可以是單例類在序列化和反序列化后得到同一個對象!!?
還有需要注意的是,上面的兩個方法簽名就是那樣的方法簽名,記住就可以了。如果非要問為什么?那應(yīng)該從源碼的角度看看對象的序列化和反序列化的過程。
- 使用java.io.Externalizable進(jìn)行序列化和反序列化
序列化和反序列化還有一種方法就是實現(xiàn)上面的接口,實現(xiàn)上面的接口需要實現(xiàn)兩個方法:
@Override public void writeExternal(ObjectOutput out) throws IOException {// TODO Auto-generated method stub } @Override public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {// TODO Auto-generated method stub }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
上面的兩個方式是實現(xiàn)Externalizable接口必須實現(xiàn)的方法。通過這兩個方法的名字我們也該知道,它所實現(xiàn)的功能和
private void writeObject(ObjectOutputStream os) throws IOException { //...... } private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { //...... }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
和這兩個方法實現(xiàn)功能一樣,都是自定義序列化和反序列化。?
不同的是:?
1、writeObject、readObject兩個方法的實現(xiàn)并不是強(qiáng)制的,實現(xiàn)一個或者兩個方法都實現(xiàn)都是可以的。而方法writeExternal、readExternal是實現(xiàn)接口是必須實現(xiàn)的方法。?
2、writeObject、readObject兩個方法的實現(xiàn)是實現(xiàn)序列化和反序列化時程序自己調(diào)用的。也就是說在程序如下:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
上面的程序使用ObjectOutputStream的write方法序列化對象sigleCls的時候,會自動調(diào)用上面的writeObject、readObject方法,如果sigleCls類實現(xiàn)了這兩個方法的話。不用顯式調(diào)用。
而writeExternal、readExternal也不用顯式調(diào)用,這一點同上面的一樣的。不同的是,實現(xiàn)這兩個方法進(jìn)行序列化的時候,必須在實現(xiàn)類中有公共的無參數(shù)的構(gòu)造器!!!否則拋出異常!!?
3、如果實現(xiàn)了Externalizable接口,同時實現(xiàn)private Object readResolve(){} 、private Object writeReplace(){ } 方法,也是生效的。?
注:writeReplace調(diào)用在writeExternal前執(zhí)行;readResolve調(diào)用在readExternal之后執(zhí)行。?
4、在此writeExternal 和readExternal 的作用與writeObject和readObject 一樣,當(dāng)我們同時實現(xiàn)了兩個interface的時候,JVM只運行Externalizable 接口里面的writeExternal 和readExternal 方法對序列化內(nèi)容進(jìn)行處理。
最后給出一個實例代碼:?
單例類:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
測試類:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream;public class Test2 {public static void main(String[] args) {ObjectOutputStream out = null;ObjectInputStream in = null;SigleCls sigleCls = SigleCls.getInstance();sigleCls.setName("longyin");sigleCls.setPsw("23456");try {out = new ObjectOutputStream(new FileOutputStream("test2.txt"));System.out.println("寫入:"+sigleCls);out.writeObject(sigleCls);out.flush();in = new ObjectInputStream(new FileInputStream("test2.txt"));SigleCls sig = (SigleCls) in.readObject();System.out.println("讀取:"+sig);System.out.println("相等與否:"+(sig==sigleCls));} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
結(jié)果:?
?
大家可以通過結(jié)果,驗證上面的理論部分是否正常。應(yīng)該說結(jié)果證明了上面的理論部分!!!
https://www.ibm.com/developerworks/cn/java/j-lo-serial/?
這篇文章是一個博士所寫!非常好!值得好好看看!!
from:?http://blog.csdn.net/u010156024/article/details/48345257
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的java-Transient关键字、Volatile关键字介绍和序列化、反序列化机制、单例类序列化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊并发(一)——深入分析Volatil
- 下一篇: 如何提高创作型任务的效率?