transient的作用及序列化
1.transient 介绍
2. transient 使用总结
- 序列化
是指将Java对象保存为二进制字节码的过程。
- 反序列化
将二进制字节码重新转成Java对象的过程。
(1)为什么序列化
- 我们知道,一般Java对象的生命周期比Java虚拟机短,而实际的开发中,我们需要
在Jvm停止后能够继续持有对象,这个时候就需要用到序列化技术将对象持久到磁盘或数据库。
- 在多个项目进行RPC调用的,需要在网络上传输JavaBean对象。我们知道数据只能以二进制的
形式才能在网络上进行传输。所以也需要用到序列化技术。
(2)序列化的底层原理
1. 程序入口:
writeObject(obj)
Student stu1 = new Student(1001, "jack", "play");
Student stu2 = new Student(1002, "tom", "sleep");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:\\stu.dat"));
oos.writeObject(stu1);
oos.writeObject(stu2);
oos.close();
- 序列化
- 在调用writeObject()方法之前,会先调用ObjectOutputStream的构造函数,生成
一个ObjectOutputStream对象。
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
// bout是底层的数据字节容器
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
// 写入序列化文件头
writeStreamHeader();
// 设置文件缓存刷新配置
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
writeStreamHeader的方法内容如下:
protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC);
bout.writeShort(STREAM_MAGIC);
}
3. 调用writeObject()方法进行具体的序列化写入
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
- writeObject0的具体内容
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
boolean oldMode = bout.setBlockDataMode(false);
depth++;
try {
// handle previously written and non-replaceable objects
int h;
if ((obj = subs.lookup(obj)) == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
} // check for replacement object
Object orig = obj;
// 需要序列的对象的Class对象
Class<?> cl = obj.getClass();
ObjectStreamClass desc;
for (;;) {
// 提示:跳过检查string和数组
// REMIND: skip this check for strings/arrays?
Class<?> repCl;
// 创建描述c1的ObjectStreamClass对象
desc = ObjectStreamClass.lookup(cl, true);
if (!desc.hasWriteReplaceMethod() ||
(obj = desc.invokeWriteReplace(obj)) == null ||
(repCl = obj.getClass()) == cl)
{
break;
}
cl = repCl;
}
if (enableReplace) {
Object rep = replaceObject(obj);
if (rep != obj && rep != null) {
cl = rep.getClass();
desc = ObjectStreamClass.lookup(cl, true);
}
obj = rep;
} // if object replaced, run through original checks a second time
if (obj != orig) {
subs.assign(orig, obj);
if (obj == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
}
} // remaining cases
// 根据实际要写入的类型,进行不同的写入操作
// 由此可以看出String、Array、Enum是直接写入操作的
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
// 实现序列化接口的都会执行下面的方法
// 从这里也可以看出Serializable是一个标记接口,其本身并没有什么意义
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}
从上面可以看出主要做了两件事
- 创建了ObjectStreamClass对象
- 根据实际要写入的类型,进行不同的写入操作
writeOrdinaryObject()
为什么说序列化并不安全
因为序列化的对象数据转换为二进制,并且完全可逆。但是在RMI调用时
所有private字段的数据都以明文二进制的形式出现在网络的套接字上,这显然是不安全的
解决方案:
- 1、 序列化Hook化(移位和复位)
- 2、 序列数据加密和签名
- 3、 利用transient的特性解决
- 4、 打包和解包代理
4. transient 在序列化底层的应用
static和transient修饰的字段不能被序列化。
private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
Field[] clFields = cl.getDeclaredFields();
ArrayList<ObjectStreamField> list = new ArrayList<>();
int mask = Modifier.STATIC | Modifier.TRANSIENT; for (int i = 0; i < clFields.length; i++) {
if ((clFields[i].getModifiers() & mask) == 0) {
list.add(new ObjectStreamField(clFields[i], false, true));
}
}
int size = list.size();
return (size == 0) ? NO_FIELDS :
list.toArray(new ObjectStreamField[size]);
}
transient的作用及序列化的更多相关文章
- Transient的作用
1:transient的作用及其使用方法 当一个对象实现类Serilizable接口,那么这个类就可以被序列化,java的这种序列化的模式为开发者提供了很多的便利. 然而在实际开发中,我们常常遇到这样 ...
- 简述serializable和transient关键字作用
transient的作用及使用方法,官方解释为: Variables may be marked transient to indicate that they are not part of the ...
- java transient关键字作用,使用场景。
java transient关键字作用,使用场景. 2016年08月31日 15:31:10 阅读数:4280 transient的作用及使用方法,官方解释为: Variables may be ma ...
- java transient关键字作用,使用场景
transient的作用及使用方法,官方解释为: Variables may be marked transient to indicate that they are not part of the ...
- Java基础-Java中transient有什么用-序列化有那几种方式
此文转载于知乎的一篇文章,看着写的非常全面,分享给大家. 先解释下什么是序列化 我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要Java序列化技术. Java序列化 ...
- 【Java】java注解@Transient的作用, 配合JPA中时间段的查询
java注解@Transient的作用 @Transient标注的属性,不会被ORM框架映射到数据库中. 用于数据库表字段和java实体属性不一致的时候,标注在属性上使用. 例如时间段的查询 查询 R ...
- 关于transient和static的序列化和反序列化
做java开发有段时间了,最近没那么忙了,抽了点时间看了下java的源码 . 在读源码的时候看到了一个 transient 修饰的变量 ,字面意思是瞬变的.在以前的开发过程中也没用到过这个修饰语,查了 ...
- 使用transient关键字解决ehcache序列化错误
使用Ehcache时发现个不起眼的小问题 在一个Model中有以下代码: public class MyModel implements Serializable { private static f ...
- 序列化,反序列化和transient关键字
一.序列化和反序列化的概念 序列化:指把java对象转换为字节序列的过程. 反序列化:指把字节序列恢复为java对象的过程. 对象的序列化主要有两种用途: 1) 把对象的字节序列保存到硬盘上,通常存放 ...
随机推荐
- python 拼接字
在编译脚本的时候,由于脚本的框架是统一写好的,于是乎用上了拼接字的功能, 本脚本实现的是波特率设置的自动化,利用的是正则表达式,TASK函数是统一写好的,此处只做调用 from Args import ...
- Windows操作Redis及Redis命令
Windows操作Redis及Redis命令 一.Windows下操作Redis 设置密码 打开redis服务 Windows 下的redis命令行 二.redis常用命令大全 key String ...
- python输出乘法口诀
for i in range(1,10): for j in range(1,i+1): print (" ".join(["%d*%d=%d" %(j,i,i ...
- Effective Java读书笔记--类和接口
1.使类和成员的可访问性最小化不指定访问级别,就是包私有.protected = 包私有 + 子类一般private不会被访问到,如果实现了Serializable,可能会泄露.反射.final集合或 ...
- 统计单词出现次数的mapreduce
1.新建Java项目 2.导包E:\工具\大数据\大数据提升资料\01-软件资料\06-Hadoop\安装包\Java1.8环境下编译\hadoop-2.7.3\hadoop-2.7.3\share\ ...
- 2019牛客暑期多校训练营(第五场)H-subsequence 2 (拓扑排序+思维)
>传送门< 题意: 给你几组样例,给你两个字符a,b,一个长度len,一个长度为len的字符串str,str是字符串s的子串 str是s删掉除过a,b两字符剩下的子串,现在求s,多种情况输 ...
- Codeforces Round #627 (Div. 3) E - Sleeping Schedule(递推)
题意: 每天有 h 小时,有一序列 an,每次可以选择 ai 或 ai - 1 小时后睡觉,问从 0 次 0 时开始,最多在 l ~ r 时间段入睡多少次. 思路: 如果此时可达,计算此时可达的时间点 ...
- SpringSecurity简单使用
什么是SpringSecurity? Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置 ...
- spring再学习之AOP实操
一.spring导包 2.目标对象 public class UserServiceImpl implements UserService { @Override public void save() ...
- JPG学习笔记1(附完整代码)
#topics h2 { background: rgba(43, 102, 149, 1); border-radius: 6px; box-shadow: 0 0 1px rgba(95, 90, ...