概念

  • 实现 Serializable 接口, 它只是一个标记接口,不实现也能够进行序列化
  • RMI: 远程方法调用
  • RPC: 远程过程调用

序列化ID

解决了序列化与反序列出现代码不一致的问题, 不一致将导致序列化失败
private static final long serialVersionUID = 1L; // 便于进行代码版本控制
private static final long serialVersionUID = -5453781658505116230L; //便于控制代码结构

静态变量序列化

  • x*- 序列化的是对象,而不是类,静态变量属于类级别,所以序列化不会保存静态变量

父类序列化与Trancient关键字

  • 一个子类实现了 Serializable 接口,它的父类没有实现 Serializable 接口,那么序列化子类时,父类的值都不会进行保存

    •   需要父类保存值 ,就需要让父类也实现Serializable 接口
    •   取父对象的变量值时,它的值是调用父类无参构造函数后的值,出现如 int 型的默认是 0,string 型的默认是 null, 要指定值,那么需要在父类构造方法中进行指定
  • Trancient关键字指定的内容将不会被保存(阻止序列化)
    •   在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 nul
    • 使用继承关系同样可以实现,Trancient一样的效果,,即为父类不需要实现Serializable接口

利用PutField getPutField字段进行加密

原理:

  • 1: 进行序列化时,JVM试图调用对象的writeObject() readObject() 方法(允许自己私有化实现)
  • 2:默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法
private void writeObject(ObjectOutputStream out) {
try {
PutField putFields = out.putFields(); //放到
System.out.println("原密码:" + password);
password = "encryption";// 模拟加密
putFields.put("password", password);
System.out.println("加密后的密码" + password);
out.writeFields();
} catch (IOException e) {
e.printStackTrace();
}
}
private void readObject(ObjectInputStream in) {
try {
GetField readFields = in.readFields();
Object object = readFields.get("password", "");
System.out.println("要解密的字符串:" + object.toString());
password = "pass";// 模拟解密,需要获得本地的密钥
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 调用的时候直接调用 out的writeObject(),或者in的readObject() 即可

序列化存储规则

  • 对同一对象两次写入文件, 第一次写入实际对象的序列化后的数据,第二次写入同一个对象的引用数据.(即为指向同一个对象)

    • 1: 节约了磁盘存储空间  
    • 2: 反序列化后的数据的值,应该是第一次保存的数据的值,(对于同一个对象第二次序列化,值是不会进行保存的)  

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
Test test = new Test();
test.i = ; // 有效
out.writeObject(test);
out.flush();
test.i = ; //无效 第二次反序列化 只写出对象的引用关系 表示为同一个 引用对象,节约了磁盘空间
out.writeObject(test);
out.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(
"result.obj"));
Test t = (Test) oin.readObject();
Test t = (Test) oin.readObject();
System.out.println(t.i);//
System.out.println(t.i);//

Serializable接口的定义:

 public interface Serializable {} // 可以知道这个只是 一个标记接口, 并且JVM 并没有实现相应的反射代码,真的据说是起到标记作用! 那么这个标记 是在哪里进行判断的?

标记的具体定义地方:

writeObject0方法中有这么一段代码:

if (obj instanceof String) {
2 writeString((String) obj, unshared);
3 } else if (cl.isArray()) {
4 writeArray(obj, desc, unshared);
5 } else if (obj instanceof Enum) {
6 writeEnum((Enum<?>) obj, desc, unshared);
7 } else if (obj instanceof Serializable) {
8 writeOrdinaryObject(obj, desc, unshared);
9 } else {
10 if (extendedDebugInfo) {
11 throw new NotSerializableException(
12 cl.getName() + "/n" + debugInfoStack.toString());
13 } else {
14 throw new NotSerializableException(cl.getName());
15 }
16 }

可以看出:  在进行序列化操作时,会判断要被序列化的类是否是Enum、Array和Serializable类型,如果不是则直接抛出 NotSerializableException

ArrayList分析

  • 要实现序列化 必须实现Serializable接口,ArrayList 也实现了这个接口

transient Object[] elementData; //为什么要让ArrayList 存储数据的结构丢弃呢?
答案:
ArrayList实际上是动态数组,每次在放满以后自动增长设定的长度值,如果数组自动增长长度设为100,
而实际只放了一个元素,那就会序列化99个null元素。为了保证在序列化的时候不会将这么多null同时进行序列化,
ArrayList把元素数组设置为transient (一句话只对实际有效的值进行保存)

  • -* 实现策略:

ArrayList 对writeObject readObject 方法进行了重写, 对NULL值数据进行了过滤

具体分析:

在ArrayList中定义了来个方法: writeObject 和 readObject

private void readObject(java.io.ObjectInputStream s)
2 throws java.io.IOException, ClassNotFoundException {
3 elementData = EMPTY_ELEMENTDATA;
4 // Read in size, and any hidden stuff
5 s.defaultReadObject();
6 // Read in capacity
7 s.readInt(); // ignored
8 if (size > 0) {
9 // be like clone(), allocate array based upon size not capacity
10 ensureCapacityInternal(size);
11 Object[] a = elementData;
12 // Read in all elements in the proper order.
13 for (int i=0; i<size; i++) {
14 a[i] = s.readObject();
15 }
16 }
17 }
private void writeObject(java.io.ObjectOutputStream s)
2 throws java.io.IOException{
3 // Write out element count, and any hidden stuff
4 int expectedModCount = modCount;
5 s.defaultWriteObject();
6 // Write out size as capacity for behavioural compatibility with clone()
7 s.writeInt(size);
8 // Write out all elements in the proper order.
9 for (int i=0; i<size; i++) {
10 s.writeObject(elementData[i]);
11 }
12 if (modCount != expectedModCount) {
13 throw new ConcurrentModificationException();
14 }
15 }

总结; 如何自定义的序列化和反序列化策略 重写 writeObject 和 readObject 方法,

这两个方法是怎么被调用的?

void invokeWriteObject(Object obj, ObjectOutputStream out)
2 throws IOException, UnsupportedOperationException
3 {
4 if (writeObjectMethod != null) {
5 try {
6 writeObjectMethod.invoke(obj, new Object[]{ out });
7 } catch (InvocationTargetException ex) {
8 Throwable th = ex.getTargetException();
9 if (th instanceof IOException) {
10 throw (IOException) th;
11 } else {
12 throwMiscException(th);
13 }
14 } catch (IllegalAccessException ex) {
15 // should not occur, as access checks have been suppressed
16 throw new InternalError(ex);
17 }
18 } else {
19 throw new UnsupportedOperationException();
20 }
21 }

其中 writeObjectMethod.invoke(obj, new Object[]{ out }); 是关键,通过反射的方式调用writeObjectMethod方法。官方是这么解释这个writeObjectMethod的:

class-defined writeObject method, or null if none

在我们的例子中,这个方法就是我们在ArrayList中定义的writeObject方法。通过反射的方式被调用了

那么怎么反射的呢?

  在  ObjectStreamClass这个方法中 有这么一段代码: 这样 readObjectMethod readObjectNoDataMethod 就拿到 了

                    if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[] { ObjectOutputStream.class },
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[] { ObjectInputStream.class },
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
domains = getProtectionDomains(cons, cl);
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
}

Java序列化总结(最全)的更多相关文章

  1. Java序列化技术与Protobuff

    http://www.cnblogs.com/fangfan/p/4094175.html http://www.cnblogs.com/fangfan/p/4094175.html 前言: Java ...

  2. 【总结】你所不知道的Java序列化

    我们都知道,Java序列化可以让我们记录下运行时的对象状态(对象实例域的值),也就是我们经常说的对象持久化 .这个过程其实是非常复杂的,这里我们就好好理解一下Java的对象序列化. 1. 首先我们要搞 ...

  3. java面试题小全

    面向对象的特征有哪些方面   1. 抽象:抽象就是忽略一个主题中与当前目标2. 无关的那些方面,3. 以便更充分地注意与当前目标4. 有关的方面.抽象并不5. 打算了解全部问题,而6. 只是选择其中的 ...

  4. Java 序列化 JDK序列化总结

    Java 序列化 JDK序列化总结 @author ixenos Java序列化是在JDK 1.1中引入的,是Java内核的重要特性之一.Java序列化API允许我们将一个对象转换为流,并通过网络发送 ...

  5. Java序列化与反序列化,文件操作

    参考两篇博客: http://blog.csdn.net/moreevan/article/details/6697777 http://blog.csdn.net/moreevan/article/ ...

  6. Java序列化框架性能比較

    博客: http://colobu.com jvm-serializers提供了一个非常好的比較各种Java序列化的的測试套件. 它罗列了各种序列化框架. 能够自己主动生成測试报告. 我在AWS c3 ...

  7. Java基础18:Java序列化与反序列化

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

  8. Java序列化的理解与学习

    1.什么是Java序列化 Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比 JVM的生命周期更长.但在现实应 ...

  9. 浅析若干Java序列化工具【转】

    在Java中socket传输数据时,数据类型往往比较难选择.可能要考虑带宽.跨语言.版本的兼容等问题.比较常见的做法有: 采用java对象的序列化和反序列化 把对象包装成JSON字符串传输 Googl ...

随机推荐

  1. java字符串加密解密

    java字符串加密解密 字符串加密解密的方式很多,每一种加密有着相对的解密方法.下面要说的是java中模拟php的pack和unpack的字符串加密解密方法. java模拟php中pack: /** ...

  2. Python的6种运算符(日记)

    学习了许久的Python,我单独总结出了Python中比较常见的6种运算符,感觉略有不全,希望大伙可以一起讨论与研究Python! 一.算术运算符 加 减 - 乘 * 除 / 取余 % 取整 // 异 ...

  3. STA——multicycle path

    之前去地平线面试的时候被问到了multicycle path的一点问题,其实这个问题我应该知道,看过<Constraining Designs for Synthesis and Timing  ...

  4. CountDownLatch、CyclicBarrier和Semaphore使用

    CountDownLatch CountDownLatch是用来线程计数的.等待一组线程全部执行完后再本线程继续执行.如:A线程需要等待B.C和D(由初始化CountDownLatch参数觉得等待多少 ...

  5. zookeeper学习(一)_简介

    上篇文章 我们已经安装上了zookeeper,也简单的体验了一把,但是如果让你给别人介绍下zookeeper,可能也是说不出来.本篇文章就参考了网上各位优秀博主的文章,整理出自己更能理解的内容 优秀博 ...

  6. Net基础篇_学习笔记_第十二天_面向对象继承(命名空间 、值类型和引用类型)

    命名空间可以认为类是属于命名空间的. 解决类的重名问题,可以看做类的“文件夹”如果在当前项目中没有这个类的命名空间,需要我们手动的导入这个类所在的命名空间.1).用鼠标去点2).alt+shift+F ...

  7. DirectX12 3D 游戏开发与实战第一章内容

    DirectX12 3D 第一章内容 学习目标 1.学习向量在几何学和数学中的表示方法 2.了解向量的运算定义以及它在几何学中的应用 3.熟悉DirectXMath库中与向量有关的类和方法 1.1 向 ...

  8. sql字段为逗号分开的字符串值的关联查询

    1.TREE表: [strID] [int] IDENTITY(1,1) NOT NULL,[strName] [nvarchar](50) NOT NULL, 2.SubInfo CREATE TA ...

  9. 控制执行流程之ForEach语法

    1.定义 : 一种更加简洁的for语法,用于数组和容器===>>>foreach,无需创建int类型变量对被访问项构建的序列进行计数. 2.经典应用:  对于数组的初始化,如果选用f ...

  10. php tp5 composer

    ## php tp5 composer安装tp5.1需要先去装个Apache或者Nginx,再装个php环境.一般Windows可以直接使用xmapp.然后tp5好像python的django啊... ...