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) 把对象的字节序列保存到硬盘上,通常存放 ...
随机推荐
- Language Guide (proto3) | proto3 语言指南(九)Oneof结构
Oneof - Oneof结构 如果消息包含多个字段,并且最多只能同时设置一个字段,则可以使用oneof功能强制执行此行为并节省内存. oneof字段与常规字段类似,但oneof共享内存中的所有字段除 ...
- Tomcat 详解URL请求
这里分析一个实际的请求是如何在Tomcat中被处理的,以及最后是怎么样找到要处理的Servlet的?当我们在浏览器中输入http://hostname:port/contextPath/servlet ...
- Django(静态文件or路由)
静态文件 在开发中同创会使用到 css,js,img等静态文件,这里带大家简单的介绍一下django如何处理静态页面, 配置 setting.py文件 STATIC_URL = '/static/' ...
- 当前日期减去TIMESTAMP(6)日期
select trunc(sysdate- to_date(to_char(j.create_date,'yyyy-mm-dd'),'yyyy-mm-dd')) 相差天数 from sys_user ...
- Asp.net core通过自定义特性实现双端数据验证的一些想法
asp.net core集成了非常方便的数据绑定和数据校验机制,配合操作各种easy的vs,效率直接高到飞起. 通过自定义验证特性(Custom Validation Attribute)可以实现对于 ...
- Codeforces Round #626 Div2 D,E
比赛链接: Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics) D.Present 题意: 给定大 ...
- Broken robot CodeForces - 24D (三对角矩阵简化高斯消元+概率dp)
题意: 有一个N行M列的矩阵,机器人最初位于第i行和第j列.然后,机器人可以在每一步都转到另一个单元.目的是转到最底部(第N个)行.机器人可以停留在当前单元格处,向左移动,向右移动或移动到当前位置下方 ...
- hdu5247 找连续数
Problem Description 小度熊拿到了一个无序的数组,对于这个数组,小度熊想知道是否能找到一个k 的区间,里面的 k 个数字排完序后是连续的. 现在小度熊增加题目难度,他不想知道是否有这 ...
- 吉哥系列故事――恨7不成妻 HDU - 4507
题目: 单身! 依然单身! 吉哥依然单身! DS级码农吉哥依然单身! 所以,他生平最恨情人节,不管是214还是77,他都讨厌! 吉哥观察了214和77这两个数,发现: 2+1+4=7 7+7=7*2 ...
- MySQL 字符集及校验规则
字符集 Mysql 的字符集有4个级别的默认设置:服务器级,数据库级,表级和字段级,客户端交互时,也可以指定字符集 # 字符集:是一个系统支持的所有抽象字符的集合.字符是各种文字和符号的总称,包括各国 ...