java-Transient关键字、Volatile关键字介绍和序列化、反序列化机制、单例类序列化
- Transient关键字
Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想
用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,
transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。
注意static变量也是可以串行化的
同时,通过反序列化得到的对象是不同的对象,而且得到的对象不是通过构造器得到的,
也就是说反序列化得到的对象不执行构造器。
下面进行测试:
新建一个javabean类,代码:
import java.util.Date;
public class LoggingInfo implements java.io.Serializable
{ private static Date loggingDate = new Date(); private String uid; private transient String pwd; LoggingInfo(String user, String password) { uid = user; pwd = password; } public String toString() { String password=null; if(pwd == null) { password = "NOT SET"; } else { password = pwd; } return "logon info: \n " + "user: " + uid + "\n logging date : " + loggingDate.toString() + "\n password: " + password; }
}
- 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版本之间序列化方式稍有不同,但基本上都是兼容的。
在某些特殊情况下,可能需要自定义序列化和反序列化的行为,看下面例子:
class AbstractSerializeDemo { private int x, y; public void init(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } public void printXY() { System.out.println("x:" + x + ";y:" + y); }
} public class SerializeDemo extends AbstractSerializeDemo implements Serializable { private int z; public SerializeDemo() { super.init(10, 50); z = 100; } public void printZ() { super.printXY(); System.out.println("z:" + z); } public static void main(String[] args) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); SerializeDemo sd = new SerializeDemo(); sd.printZ(); out.writeObject(sd); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); SerializeDemo sd2 = (SerializeDemo) in.readObject(); sd2.printZ(); }
}
- 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方法。
private Object readResolve() { return INSTANCE; } private Object writeReplace(){ return INSTANCE; }
- 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两个方法的实现是实现序列化和反序列化时程序自己调用的。也就是说在程序如下:
out = new ObjectOutputStream(new FileOutputStream("test2.txt"));System.out.println("写入:"+sigleCls);out.writeObject(sigleCls);
- 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 方法对序列化内容进行处理。
最后给出一个实例代码:
单例类:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;public class SigleCls implements Serializable,Externalizable
{private static final long serialVersionUID = 1L;private static SigleCls sigleCls;public SigleCls(){}public static SigleCls getInstance(){if (sigleCls == null) {sigleCls = new SigleCls();}return sigleCls;}private String name;private transient String psw;public void setName(String name) {this.name = name;}public void setPsw(String psw) {this.psw = psw;}private Object readResolve(){System.out.println("SigleCls.readResolve()");return sigleCls;}private Object writeReplace(){System.out.println("SigleCls.writeReplace()");return sigleCls;}@Override
public String toString() {return "name="+name+",pwd="+psw;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {System.out.println("SigleCls.writeExternal()");out.writeObject(sigleCls);}private void writeObject(ObjectOutputStream out) throws IOException{System.out.println("LoggingInfo.writeObject()");out.defaultWriteObject();out.writeInt(4);
}
private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException{System.out.println("LoggingInfo.readObject()");in.defaultReadObject();System.out.println("整数="+in.readInt());
}@Override
public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {// TODO Auto-generated method stubSystem.out.println("SigleCls.readExternal()");in.readObject();
}
}
/*** 注意实现Externalizable接口的类,在发序列化时,将会执行构造函数,* 因为对于流操作而言,此对象是有明确类型的(Serializable接口是个标记接口).* 而且,如果实现了writeExternal和readExternal,* 将不会在执行readObject和writeObject,* 因为此时这两个方法已经被"擦除".*/
- 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
java-Transient关键字、Volatile关键字介绍和序列化、反序列化机制、单例类序列化相关推荐
- java transient 和Volatile关键字
Volatile修饰的成员变量在每次被线程访问时,都强迫从主内存中重读该成员变量的值.而且,当成员变量发生变化时,强迫线程将变化值回写到主内存.这样在任何时刻,两个不同的线程总是看到某个成员变量的同一 ...
- Java transient和volatile关键字
推荐大家去看原文博主的文章,条理清晰阅读方便,转载是为了方便以后个人查阅 https://www.cnblogs.com/heilyeah/p/6594122.html 关键字Volatile Vol ...
- null在java存在的意义何在,Java并发编程——volatile关键字
一.volatile是什么 volatile是Java并发编程中重要的一个关键字,被比喻为"轻量级的synchronized",与synchronized不同的是,volatile ...
- 如何理解 JAVA 中的 volatile 关键字
如何理解 JAVA 中的 volatile 关键字 最近在重新梳理多线程,同步相关的知识点.关于 volatile 关键字阅读了好多博客文章,发现质量高适合小白的不多,最终找到一篇英文的非常通俗易懂. ...
- Java并发编程—volatile关键字(保证变量的可见性、有序性机制)
原文作者:Matrix海子 原文地址:Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程 ...
- java中的Volatile关键字使用
文章目录 什么时候使用volatile Happens-Before java中的Volatile关键字使用 在本文中,我们会介绍java中的一个关键字volatile. volatile的中文意思是 ...
- 深入理解Java中的volatile关键字
在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized ...
- java 轻量级同步volatile关键字简介与可见性有序性与synchronized区别 多线程中篇(十二)...
概念 JMM规范解决了线程安全的问题,主要三个方面:原子性.可见性.有序性,借助于synchronized关键字体现,可以有效地保障线程安全(前提是你正确运用) 之前说过,这三个特性并不一定需要全部同 ...
- Java并发编程--volatile关键字解析
volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以 ...
最新文章
- python【蓝桥杯vip练习题库】ADV-185五次方数(枚举)
- scapy安装and简介
- impinj固定式阅读器数据解析中Rfid筛选器实现
- UltraEdit批量删除关键字所在的指定行
- failed to initialize nvml driver/library version mismatch ubuntu
- mysql数据库修改数据库名称_MySQL数据库之MySQL 修改数据库名称的一个新奇方法...
- 90后占六成 花小猪如何吸引年轻人又安全不减配?
- 小白打基础一定要吃透的11类 Python 内置函数
- Android Studio如何查找和替换
- 北语发布 | 汉语学习者文本多维标注数据集YACLC V1.0 -- 文本纠错方向
- 【java】程序初始化顺序
- ios 定位权限获取
- 操作系统中的故障恢复控制台意义非凡
- 【C++ set的基本操作】
- 舆情监测系统功能简介,网络舆情监测系统平台有哪些?
- libjpeg与turbo libjpeg的使用
- Java计算商品的促销日期
- 2021年全球医用气体压力调节器收入大约180.9百万美元,预计2028年达到226.3百万美元
- 内核启动分析(三)——zImage 解压缩阶段
- 量化交易:KDJ周线择时