java 数组序列化存储_Java序列化总结(最全)

概念

实现 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 voidwriteObject(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 voidreadObject(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 interfaceSerializable {} // 可以知道这个只是 一个标记接口, 并且JVM 并没有实现相应的反射代码,真的据说是起到标记作用! 那么这个标记 是在哪里进行判断的?

标记的具体定义地方:

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

if (obj instanceofString) {2writeString((String) obj, unshared);3 } else if(cl.isArray()) {4writeArray(obj, desc, unshared);5 } else if (obj instanceofEnum) {6 writeEnum((Enum>) obj, desc, unshared);7 } else if (obj instanceofSerializable) {8writeOrdinaryObject(obj, desc, unshared);9 } else{10 if(extendedDebugInfo) {11 throw newNotSerializableException(12 cl.getName() + "/n" +debugInfoStack.toString());13 } else{14 throw newNotSerializableException(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 voidreadObject(java.io.ObjectInputStream s)2 throwsjava.io.IOException, ClassNotFoundException {3 elementData =EMPTY_ELEMENTDATA;4 //Read in size, and any hidden stuff

5s.defaultReadObject();6 //Read in capacity

7 s.readInt(); //ignored

8 if (size > 0) {9 //be like clone(), allocate array based upon size not capacity

10ensureCapacityInternal(size);11 Object[] a =elementData;12 //Read in all elements in the proper order.

13 for (int i=0; i

private voidwriteObject(java.io.ObjectOutputStream s)2 throwsjava.io.IOException{3 //Write out element count, and any hidden stuff

4 int expectedModCount =modCount;5s.defaultWriteObject();6 //Write out size as capacity for behavioural compatibility with clone()

7s.writeInt(size);8 //Write out all elements in the proper order.

9 for (int i=0; i

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

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

voidinvokeWriteObject(Object obj, ObjectOutputStream out)2 throwsIOException, UnsupportedOperationException3{4 if (writeObjectMethod != null) {5 try{6 writeObjectMethod.invoke(obj, newObject[]{ out });7 } catch(InvocationTargetException ex) {8 Throwable th =ex.getTargetException();9 if (th instanceofIOException) {10 throw(IOException) th;11 } else{12throwMiscException(th);13}14 } catch(IllegalAccessException ex) {15 //should not occur, as access checks have been suppressed

16 throw newInternalError(ex);17}18 } else{19 throw newUnsupportedOperationException();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;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值