zoukankan      html  css  js  c++  java
  • Java对象序列化

    Java对象序列化

    1、什么是序列化

    Java序列化是指把Java对象转换为字节序列的过程;而反序列化是指把字节序列恢复为Java对象的过程。从而实现网络传输、本地存储等需求;

    • 一般Java对象的生命周期比Java虚拟机短,而实际开发中如果需要JVM停止后能够继续持有对象,则需要用到序列化技术将对象持久化到磁盘或数据库;

    • 在多个项目进行RPC(Remote Procedure Call)调用时,需要在网络上传输JavaBean对象,而网络上只允许二进制形式的数据进行传输,这时则需要用到序列化技术;

    2、如何实现序列化

    将被序列化的类实现Serializable或Externalizable接口即可;在实现Serializable接口的默认情况下,Java会自动将非transient(短暂的,临时的)关键字修饰的属性序列化到指定文件中去;

    3、Serializable接口示例

     1 //JavaBean
     2   public class StudentBean implements Serializable{
     3       
     4       private String code;
     5       private String name ;
     6       private Integer age ;
     7       private List<Score> scores  ;
     8       // get/set/toString ...
     9 10   }
    11   public class Score implements Serializable {
    12       
    13       private Integer code ;
    14       private String name;
    15       private Integer score ;
    16       // get/set/toString ...
    17   }
    18   //SerializableUtils
    19   public class SerializableUtils {
    20       
    21       /**
    22        * 序列化
    23        * 
    24        * @param obj   实现了Serializable接口的类
    25        * @param fileName 文件输出路径
    26        * @throws IOException
    27        */
    28       public static void serialize(Object obj, String fileName)
    29               throws IOException {
    30    
    31           FileOutputStream fos = new FileOutputStream(fileName);
    32           BufferedOutputStream bos = new BufferedOutputStream(fos);
    33           ObjectOutputStream oos = new ObjectOutputStream(bos);
    34           oos.writeObject(obj);
    35           oos.close();
    36       }
    37       
    38       /**
    39        * 反序列化
    40        * 
    41        * @param fileName  反序列化时读取的文件路径
    42        * @return
    43        * @throws IOException
    44        * @throws ClassNotFoundException
    45        */
    46       public static Object deserialize(String fileName) throws IOException, ClassNotFoundException {
    47           FileInputStream fis = new FileInputStream(fileName);
    48           BufferedInputStream bis = new BufferedInputStream(fis);
    49           ObjectInputStream ois = new ObjectInputStream(bis);
    50           Object obj = ois.readObject();
    51           ois.close();
    52           return obj;
    53       }
    54   }
    55   // TestClass
    56   public class MyTest {
    57 58       private StudentBean stu;
    59 60       @Before
    61       public void before() {
    62           stu = new StudentBean();
    63           stu.setAge(18);
    64           stu.setCode("101");
    65           stu.setName("张三");
    66           Score s1 = new Score(101, "高等数学", 100);
    67           Score s2 = new Score(102, "计算机", 100);
    68           Score s3 = new Score(103, "大学物理", 100);
    69           List<Score> list = new ArrayList<Score>();
    70           list.add(s1);
    71           list.add(s2);
    72           list.add(s3);
    73           stu.setScores(list);
    74       }
    75 76       @Test
    77       public void Test() throws IOException, ClassNotFoundException {
    78           // 序列化
    79           SerializableUtils.serialize(stu, "student.txt");
    80 81           // 反序列化
    82           StudentBean student = (StudentBean) SerializableUtils.deserialize("student.txt");
    83           System.out.println(student.getName());
    84           for (Score s : student.getScores()) {
    85               System.out.println(s);
    86           }
    87       }
    88   }

    4、serialVersionUID

    • 实现Serializable接口之后可选择添加一个serialVersionUID 属性作为该类的一个序列化版本号,该编码可选择自动生成或者自定义编辑,其主要作用是为当前类添加一个版本标识,UID不变则认为类的内容没变。反序列化时会判断UID是否未发生变化,若序列化后修改了此UID值,则会导致反序列化失败 ,抛出InvalidClassException异常;

    • 不指定版本号另一个明显隐患是,不利于jvm间的移植,可能class文件没有更改,但不同jvm可能计算的规则不一样,这样也会导致无法反序列化;

    5、Externalizable接口示例

    Externalizable(可外部化的)继承自Serializable,须手动实现writeExternal以及readExternal方法;

     1 //修改StudentBean
     2   public class StudentBean implements Externalizable{
     3  4       private String code;
     5       private String name ;
     6       private Integer age ;
     7       private List<Score> scores  ;
     8  9       /*
    10        * 自定义序列化规则
    11        */
    12       @Override
    13       public void writeExternal(ObjectOutput out) throws IOException {
    14           out.writeObject(code);
    15           out.writeObject(name);
    16           out.writeInt(age);
    17           out.writeObject(scores);
    18       }
    19 20       /*
    21        * 自定义反序列化规则
    22        */
    23       @Override
    24       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    25           
    26           code = (String)in.readObject();
    27           name = (String)in.readObject();
    28           age = in.readInt();
    29           scores = (List<Score>)in.readObject();
    30       }
    31       // get/set/toString
    32   }

    6、部分属性序列化

    6.1 transient关键字

    transient关键字,在实现Serializable接口情况下,只需要对不需要序列化的属性使用transient关键字进行修饰即可实现在序列化时忽略该字段;

    1   private transient String code

    6.2 添加writeObject和readObject方法

    添加writeObject和readObject方法,在实现Serializable接口的情况下添加如下两个方法,自定义选择序列化哪些字段,但注意若该字段没有参加序列化,则反序列化时亦不能参与;

    defaultWriteObject()会执行默认序列化操作即序列化所有非static、transient属性;transient关键字修饰的属性可以用writeObject()方式实现序列化;

    原理:调用ObjectOutputStream中writeObject方法时会检查序列化对象是否实现了自己的writeObject方法,如果是则跳过常规序列化转流程转而调用writerObject方法实现序列化readObject同理;

     1       private Integer code ;
     2       private transient String name;
     3       private Integer score ;
     4  5       //序列化
     6       private void writeObject(ObjectOutputStream oos) throws IOException {
     7           oos.defaultWriteObject();
     8           oos.writeObject(name);
     9       }
    10       //反序列化
    11       private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    12           ois.defaultReadObject();
    13           name = (String)ois.readObject();
    14       }

    6.3 修改writeExternal和readExternal方法

    修改writeExternal和readExternal方法,原理同上,且序列化与反序列化属性须一致;

     1 /*
     2        * 自定义序列化规则
     3        */
     4       @Override
     5       public void writeExternal(ObjectOutput out) throws IOException {
     6           //out.writeObject(code);
     7           out.writeObject(name);
     8           out.writeInt(age);
     9           out.writeObject(scores);
    10       }
    11 12       /*
    13        * 自定义反序列化规则
    14        */
    15       @Override
    16       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    17           
    18           //code = (String)in.readObject();
    19           name = (String)in.readObject();
    20           age = in.readInt();
    21           scores = (List<Score>)in.readObject();
    22       }

    7、总结

    • Serializable是标识接口,没有需要实现的方法;Externalizable必须实现writeExternal和readExternal方法;

    • Serializable不需要有空构造,但Externalizable若含有参构造则必须有无参构造函数;

    • Serializable有两种实现方式,默认方式下自动序列化非static、transient关键字修饰的属性;

    • Serializable默认方式下使用反射实现序列化,性能稍弱,对属性顺序无要求;Externalizable要求属性的序列化与反序列化顺序一致,否则抛出 java.io.OptionalDataException 异常;

    • 所有需要网络传输的对象都需要实现序列化接口,通过建议所有的javaBean都实现Serializable接口;

    • 同一对象序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化编号,不会重复序列化;

    • 建议所有可序列化的类加上serialVersionUID 版本号,方便项目升级、维护;

    • 序列化可以实现深拷贝;

    8、预告:Redis中的序列化

  • 相关阅读:
    tp5 自定义配置文件
    php 连接redis
    Redis Desktop manager连接redis
    支付宝面对面扫码支付教程
    java.lang.IllegalAccessError: org.apache.commons.dbcp.DelegatingPreparedStatement.isClosed()
    Windows环境下用Myeclipse创建Maven项目Jsp页面报错的问题
    maven项目中配置自定义welcome-file
    Bootstrap实现图片轮播
    Java中的final 修饰参数问题
    Java迭代器Iterator理解
  • 原文地址:https://www.cnblogs.com/lijizhi/p/13300483.html
Copyright © 2011-2022 走看看