java-Transient关键字、Volatile关键字介绍和序列化、反序列化机制、单例类序列化
Java的serialization提供了一種持久化對象實例的機制。當持久化對象時,可能有一個特殊的對象數據成員,我們不想?
用serialization機制來保存它。為了在一個特定對象的一個域上關閉serialization,可以在這個域前加上關鍵字transient。?
transient是Java語言的關鍵字,用來表示一個域不是該對象串行化的一部分。當一個對象被串行化的時候,?
transient型變量的值不包括在串行化的表示中,然而非transient型的變量是被包括進去的。?
注意static變量也是可以串行化的?
同時,通過反序列化得到的對象是不同的對象,而且得到的對象不是通過構造器得到的,?
也就是說反序列化得到的對象不執行構造器。
下面進行測試:?
新建一個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
測試類,調用上面的javabean類,進行序列化和反序列化,代碼如下:
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
執行結果:?
通過執行結果,可以對照上面的分析,說明上面的分析是正確的。
- 下面說說Volatile關鍵字
Java 語言提供了一種稍弱的同步機制,即 volatile 變量.用來確保將變量的更新操作通知到其他線程,保證了新值能立即同步到主內存,以及每次使用前立即從主內存刷新. 當把變量聲明為volatile類型后,編譯器與運行時都會注意到這個變量是共享的.?
volatile 變量對所有線程是立即可見的,對 volatile 變量所有的寫操作都能立即反應到其他線程之中,換句話說:volatile 變量在各個線程中是一致的,所以基于 volatile 變量的運算是線程安全的.?
對于以上的說法,我沒有想到如何用實例進行驗證。?
下面只是個人的理解:?
1。如果在類中使用volatile修飾一個變量,并且是static的類型,那么該變量屬于類,是類變量,那么即使多個線程訪問該變量訪問的也是同一個,哪個線程改變它的話,其他線程在訪問它的時候就是最新的值。不存在不同步的問題。
2。如果在類中使用volatile修飾的變量沒有使用static修飾,那就屬于成員變量,那么多個線程在訪問的時候,訪問同一個對象下的該成員變量也不存在不同步的問題。對于同一個對象,該成員變量就一個!線程無論何時訪問都是最新的。
所以能用到volatile關鍵字解決多線程的不同步問題相當少了。
- 序列化和反序列化
正常情況下,一個類實現java序列化很簡單,只需要implements Serializable接口即可,之后該類在跨jvm的傳輸過程中會遵照默認java序列化規則序列化和反序列化;不同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
這段程序表示了一個可序列化的類繼承自一個非序列化的有狀態超類,期望的結果是,子類序列化以后傳輸并反序列化回來,原先的值域包括超類的值域都保持不變。
但是輸出是:?
x:10;y:50?
z:100?
x:0;y:0?
z:100?
結果和期望不符,子類的值域保留下來了,但是超類的值域丟失了,這對jvm來說是正常的,因為超類不可序列化;
為了解決這個問題,只能自定義序列化行為,具體做法是在SerializeDemo里加入以下代碼:
private void writeObject(ObjectOutputStream os) throws IOException { os.defaultWriteObject();//java對象序列化默認操作 os.writeInt(getX()); os.writeInt(getY()); } private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { is.defaultReadObject();//java對象反序列化默認操作 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對象時會分別調用的兩個方法,修飾符都是private,沒錯。
我們在序列化的默認動作之后將超類里的兩個值域x和y也寫入object流;與之對應在反序列化的默認操作之后讀入x和y兩個值,然后調用超類的初始化方法。
再次執行程序之后的輸出為:
x:10;y:50?
z:100?
x:10;y:50?
z:100?
另外還有兩個自定義序列化方法writeReplace和readResolve,分別用來在序列化之前替換序列化對象 和 在反序列化之后的對返回對象的處理。一般可以用來避免singleTon對象跨jvm序列化和反序列化時產生多個對象實例,事實上singleTon的對象一旦可序列化,它就不能保證singleTon了。JVM的Enum實現里就是重寫了readResolve方法,由JVM保證Enum的值都是singleTon的,所以建議多使用Enum代替使用writeReplace和readResolve方法。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
注:writeReplace調用在writeObject前執行;readResolve調用在readObject之后執行。?
(以上序列化反序列化機制部分摘自:http://developer.51cto.com/art/201104/257839.htm)
上面的INSTANCE是單例類的實例。通過上面的代碼可以是單例類在序列化和反序列化后得到同一個對象!!?
還有需要注意的是,上面的兩個方法簽名就是那樣的方法簽名,記住就可以了。如果非要問為什么?那應該從源碼的角度看看對象的序列化和反序列化的過程。
- 使用java.io.Externalizable進行序列化和反序列化
序列化和反序列化還有一種方法就是實現上面的接口,實現上面的接口需要實現兩個方法:
@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
上面的兩個方式是實現Externalizable接口必須實現的方法。通過這兩個方法的名字我們也該知道,它所實現的功能和
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
和這兩個方法實現功能一樣,都是自定義序列化和反序列化。?
不同的是:?
1、writeObject、readObject兩個方法的實現并不是強制的,實現一個或者兩個方法都實現都是可以的。而方法writeExternal、readExternal是實現接口是必須實現的方法。?
2、writeObject、readObject兩個方法的實現是實現序列化和反序列化時程序自己調用的。也就是說在程序如下:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
上面的程序使用ObjectOutputStream的write方法序列化對象sigleCls的時候,會自動調用上面的writeObject、readObject方法,如果sigleCls類實現了這兩個方法的話。不用顯式調用。
而writeExternal、readExternal也不用顯式調用,這一點同上面的一樣的。不同的是,實現這兩個方法進行序列化的時候,必須在實現類中有公共的無參數的構造器!!!否則拋出異常!!?
3、如果實現了Externalizable接口,同時實現private Object readResolve(){} 、private Object writeReplace(){ } 方法,也是生效的。?
注:writeReplace調用在writeExternal前執行;readResolve調用在readExternal之后執行。?
4、在此writeExternal 和readExternal 的作用與writeObject和readObject 一樣,當我們同時實現了兩個interface的時候,JVM只運行Externalizable 接口里面的writeExternal 和readExternal 方法對序列化內容進行處理。
最后給出一個實例代碼:?
單例類:
- 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
結果:?
?
大家可以通過結果,驗證上面的理論部分是否正常。應該說結果證明了上面的理論部分!!!
https://www.ibm.com/developerworks/cn/java/j-lo-serial/?
這篇文章是一個博士所寫!非常好!值得好好看看!!
from:?http://blog.csdn.net/u010156024/article/details/48345257
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的java-Transient关键字、Volatile关键字介绍和序列化、反序列化机制、单例类序列化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊并发(一)——深入分析Volatil
- 下一篇: Java transient关键字使用小