欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

java Serializbale,javaserializbale

来源: javaer 分享于  点击 22047 次 点评:91

java Serializbale,javaserializbale


JAVA 序列化

第一部分:What

Java序列化是指把Java对象保存为二进制字节码的过程,Java反序列化是指把二进制码重新转换成Java对象的过程。

那么为什么需要序列化呢?

第一种情况是:一般情况下Java对象的声明周期都比Java虚拟机的要短,实际应用中我们希望在JVM停止运行之后能够持久化指定的对象,这时候就需要把对象进行序列化之后保存。

第二种情况是:需要把Java对象通过网络进行传输的时候。因为数据只能够以二进制的形式在网络中进行传输,因此当把对象通过网络发送出去之前需要先序列化成二进制数据,在接收端读到二进制数据之后反序列化成Java对象。

第二部分:How

 public class SerializableTest {

public static void main(String[] args) throws Exception {

FileOutputStream fos = new FileOutputStream("temp.out");

ObjectOutputStream oos = new ObjectOutputStream(fos);

TestObject testObject = new TestObject();

oos.writeObject(testObject);

oos.flush();

oos.close();

 

FileInputStream fis = new FileInputStream("temp.out");

ObjectInputStream ois = new ObjectInputStream(fis);

TestObject deTest = (TestObject) ois.readObject();

System.out.println(deTest.testValue);

System.out.println(deTest.parentValue);

System.out.println(deTest.innerObject.innerValue);

}

}

 

class Parent implements Serializable {

 

private static final long serialVersionUID = -4963266899668807475L;

 

public int parentValue = 100;

}

 

class InnerObject implements Serializable {

 

private static final long serialVersionUID = 5704957411985783570L;

 

public int innerValue = 200;

}

 

class TestObject extends Parent implements Serializable {

 

private static final long serialVersionUID = -3186721026267206914L;

 

public int testValue = 300;

 

public InnerObject innerObject = new InnerObject();

}

第三部分:Why

调用ObjectOutputStream.writeObject()和ObjectInputStream.readObject()之后究竟做了什么?temp.out文件中的二进制分别代表什么意思?

1. ObjectStreamClass类

ObjectStreamClass这个是类的序列化描述符,这个类可以描述需要被序列化的类的元数据,包括被序列化的类的名字以及序列号。可以通过lookup()方法来查找/创建在这个JVM中加载的特定的ObjectStreamClass对象。

2. 序列化:writeObject()

从源码中可以看出:

注意:

Serializbale接口是个空的接口,并没有定义任何方法,为什么需要序列化的接口只要实现Serializbale接口就能够进行序列化。

答案是:Serializable接口这是一个标识,告诉程序所有实现了”我”的对象都需要进行序列化。

总结:

 

3. 反序列化:readObject()

反序列化过程就是按照序列化算法来解析二进制数据。

有一个需要注意的问题就是,如果子类实现了Serializable接口,但是父类没有实现Serializable接口,这个时候进行反序列化会发生什么情况?

答:如果父类有默认构造函数的话,即使没有实现Serializable接口也不会有问题,反序列化的时候会调用默认构造函数进行初始化,否则的话反序列化的时候会抛出.InvalidClassException:异常,异常原因为no valid constructor。

 

第四部分:Other

1. static和transient字段不能被序列化。

序列化的时候所有的数据都是来自于ObejctStreamClass对象,在生成ObjectStreamClass的构造函数中会调用fields = getSerialFields(cl);这句代码来获取需要被序列化的字段,getSerialFields()方法实际上是调用getDefaultSerialFields()方法的,getDefaultSerialFields()实现如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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) {             // 如果字段既不是static也不是transient的才会被加入到需要被序列化字段列表中去             list.add(new ObjectStreamField(clFields[i], false, true));         }     }     int size = list.size();     return (size == 0) ? NO_FIELDS :         list.toArray(new ObjectStreamField[size]); }

从上面的代码中可以很明显的看到,在计算需要被序列化的字段的时候会把被static和transient修饰的字段给过滤掉。

在进行反序列化的时候会给默认值。

2. 如何实现自定义序列化和反序列化?

只需要被序列化的对象所属的类定义了void writeObject(ObjectOutputStream oos)和void readObject(ObjectInputStream ois)方法即可,Java序列化和反序列化的时候会调用这两个方法,那么这个功能是怎么实现的呢?

1. 在ObjectClassStream类的构造函数中有下面几行代码:

1 2 3 4 5 6 7 8 9 10 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);

getPrivateMethod()方法实现如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private static Method getPrivateMethod(Class<?> cl, String name,                                    Class<?>[] argTypes,                                    Class<?> returnType) {     try {         Method meth = cl.getDeclaredMethod(name, argTypes);         meth.setAccessible(true);         int mods = meth.getModifiers();         return ((meth.getReturnType() == returnType) &&                 ((mods & Modifier.STATIC) == 0) &&                 ((mods & Modifier.PRIVATE) != 0)) ? meth : null;     } catch (NoSuchMethodException ex) {         return null;     } }

可以看到在ObejctStreamClass的构造函数中会查找被序列化类中有没有定义为void writeObject(ObjectOutputStream oos) 的函数,如果找到的话,则会把找到的方法赋值给writeObjectMethod这个变量,如果没有找到的话则为null。

2. 在调用writeSerialData()方法写入序列化数据的时候有

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private void writeSerialData(Object obj, ObjectStreamClass desc)     throws IOException {     ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();     for (int i = 0; i < slots.length; i++) {         ObjectStreamClass slotDesc = slots[i].desc;         if (slotDesc.hasWriteObjectMethod()) {             // 其他一些省略代码             try {                 curContext = new SerialCallbackContext(obj, slotDesc);                 bout.setBlockDataMode(true);                 // 在这里调用用户自定义的方法                 slotDesc.invokeWriteObject(obj, this);                 bout.setBlockDataMode(false);                 bout.writeByte(TC_ENDBLOCKDATA);             } finally {                 curContext.setUsed();                 curContext = oldContext;                 if (extendedDebugInfo) {                     debugInfoStack.pop();                 }             }               curPut = oldPut;         } else {             defaultWriteFields(obj, slotDesc);         }     } }

首先会调用hasWriteObjectMethod()方法判断有没有自定义的writeObject(),代码如下

1 2 3 boolean hasWriteObjectMethod() {     return (writeObjectMethod != null); }

hasWriteObjectMethod()这个方法仅仅是判断writeObjectMethod是不是等于null,而上面说了,如果用户自定义了void writeObject(ObjectOutputStream oos)这么个方法,则writeObjectMethod不为null,在if()代码块中会调用slotDesc.invokeWriteObject(obj, this);方法,该方法中会调用用户自定义的writeObject()方法。

 

 

相关文章

    暂无相关文章
相关栏目:

用户点评