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) 把对象的字节序列保存到硬盘上,通常存放 ...
随机推荐
- 存储型XSS
DVWA系列(二)存储型XSS https://www.imooc.com/article/284686 网络安全:存储型XSS https://blog.csdn.net/qq_41500251/a ...
- Java POI 导出EXCEL经典实现
import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; i ...
- 19.损坏磁盘阵列及修复&磁盘阵列+备份盘
1.在确认有一块物理硬盘设备出现损坏而不能继续正常使用后,应该使用mdadm 命令将其移除,然后查看RAID 磁盘阵列的状态,可以发现状态已经改变. [root@Centos ~]# mdadm /d ...
- CSS奇思妙想 -- 使用 background 创造各种美妙的背景
本文属于 CSS 绘图技巧其中一篇,系列文章: 在 CSS 中使用三角函数绘制曲线图形及展示动画 CSS奇思妙想 -- 使用 CSS 创造艺术 将介绍一些利用 CSS 中的 background.mi ...
- Pytest(1)安装与入门
pytest介绍 pytest是python的一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高.根据pytest的官方网站介绍,它 ...
- 既有Nginx重新动态编译增加http2.0模块
1.HTTP2.0 HTTP2.0相较于http1.x,大幅度的提升了web性能,在与http1.1完全语义兼容的基础上,进一步减少了网络延时.我们现在很多对外的网站都采用https,但是F12一下看 ...
- P2120 [ZJOI2007] 仓库建设(斜率优化DP)
题意:\(1\sim N\) 号工厂,第\(i\) 个工厂有\(P_i\)个成品,第\(i\)个工厂建立仓库需要\(C_i\)的费用,该工厂距离第一个工厂的距离为\(X_i\),编号小的工厂只能往编号 ...
- POJ_2112 二分图多重匹配
题意: //题意就是给你k个挤奶池和c头牛,每个挤奶池最多可以来m头牛,而且每头牛距离这k这挤奶池//有一定的距离,题目上给出k+c的矩阵,每一行代表某一个物品距离其他物品的位置//这里要注意给出的某 ...
- Codeforces ECR 83 C. Adding Powers (位运算)
题意:给你n个数和一个底数k,每个数每次能减去k^i(i=0,1,2,....),每个k^i只能用一次,问是否能够将每个数变为0. 题解:我们将每个数转化为k进制,因为每个k^i只能用一次,所以我们统 ...
- Codeforces Round #304 (Div. 2) C. Basketball Exercise (DP)
题意:给你两个长度相同的数组,每次从两个数组中选数(也可以不选),但是不可以在同一个数组中连续选两次,问能选的最大值是多少? 题解:dp,\(dp[i][0]\)表示第\(i\)个位置不选,\(dp[ ...