zoukankan      html  css  js  c++  java
  • Java序列化(含transient)

    什么是序列化?

    我们创建的对象只有在Java虚拟机保持运行时,才会存在于内存中。如果想要超出Java虚拟机的生命周期,就可以将对象序列化,将对象状态转换为字节序列,写入文件(或socket传输),后面使用时再读入文件,读入原始字节并创建一个完全相同的对象。

    PS:只有对象的状态会被序列化,类本身或方法都不会被序列化。

    三种序列化方式

    1、默认机制

    需要序列化的对象,实现java.io.Serializable接口即可。

    例子:

    import java.io.Serializable;
    import java.util.Date;
    import java.util.Calendar;
    
    public class PersistentTime implements Serializable {
        private Date time;
    
        public PersistentTime() {
            time = Calendar.getInstance().getTime();
        }
    
        public Date getTime() {
            return time;
        }
    }
    
    
    import java.io.ObjectOutputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class FlattenTime {
        public static void main(String[] args) {
            String filename = "time.ser";
            if (args.length > 0) {
                filename = args[0];
            }
            PersistentTime time = new PersistentTime();
            FileOutputStream fos = null;
            ObjectOutputStream out = null;
            try {
                fos = new FileOutputStream(filename);
                out = new ObjectOutputStream(fos);
                out.writeObject(time);
                out.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
    
    
    import java.io.ObjectInputStream;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.util.Calendar;
    
    public class InflateTime {
        public static void main(String[] args) {
            String filename = "time.ser";
            if (args.length > 0) {
                filename = args[0];
            }
            PersistentTime time = null;
            FileInputStream fis = null;
            ObjectInputStream in = null;
            try {
                fis = new FileInputStream(filename);
                in = new ObjectInputStream(fis);
                time = (PersistentTime) in.readObject();
                in.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            } catch (ClassNotFoundException ex) {
                ex.printStackTrace();
            }
            // print out restored time
            System.out.println("Flattened time: " + time.getTime());
            System.out.println();
            // print out the current time
            System.out.println("Current time: " + Calendar.getInstance().getTime());
        }
    }
    View Code

    注意:

    1、持久化的对象必现实现Serializable接口或继承的父类实现了。

    2、不能(例如系统级别的类Thread、OutPutStream)或不想被序列化的字段必需加transient声明。

    transient是字段的修饰符,当一个字段声明为transient时,它就不能被序列化。

    transient不能声明方法、类、接口,它们不需要被序列化。

    2、自定义默认协议

    由于类的构造函数只要在对象创建时候才调用,而反序列化的对象是用readObject创建的,不会再去调构造函数。如果构造函数里有执行某些行为,那么反序列化回来的对象就会丢失这些行为。

    解决方法:增加两个方法,在保证序列化的默认行为后,增加自己要的行为。

    private void writeObject(ObjectOutputStream out) throws IOException;
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

    例子:

    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    public class PersistentAnimation implements Serializable, Runnable {
        
        private static final long serialVersionUID = 2850527457134768151L;
        
        transient private Thread animator;
        private int animationSpeed;
    
        public PersistentAnimation(int animationSpeed) {
            this.animationSpeed = animationSpeed;
            startAnimation();
        }
    
        public void run() {
            while (true) {
                // do animation here
            }
        }
    
        private void writeObject(ObjectOutputStream out) throws IOException {
            out.defaultWriteObject();
        }
    
        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            // our "pseudo-constructor"
            in.defaultReadObject();
            // now we are a "live" object again, so let's run rebuild and start
            startAnimation();
        }
    
        private void startAnimation() {
            animator = new Thread(this);
            animator.start();
        }
    }
    View Code

    同理,如果父类已经实现Serializable接口,子类又不想被序列化,就可以添加这两个内容为空的方法。

    3、自定义协议

    实现Externalizable接口,复写writeExternal、readExternal两个方法,自己定义序列化协议(例如pdf的存取)。

    public void writeExternal(ObjectOutput out) throws IOException;
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;

    序列化的陷阱

    1、缓存流中的对象,再修改对象状态再写入是无效的。

    ObjectOutputStream out = new ObjectOutputStream(...);
    MyObject obj = new MyObject(); // must be Serializable
    obj.setState(100);
    out.writeObject(obj); // saves object with state = 100
    obj.setState(200);
    out.writeObject(obj); // does not save new object state 

    2、版本控制

    当你序列化一个对象存储到文件后,修改类的定义(例如增加个字段),当你反序列化时,会报一个java.io.InvalidClassException的异常。这是因为所有可以持续化的类都会有一个唯一的标识符,如果类的标识符跟反序列化对象的标识符不一样时,就会报这个异常。

    解决方法:增加一个serialVersionUID字段作为类的标识符,只要标识符不变,就可反序列化。

    添加方法:在eclipse中,鼠标悬浮到类名上,就可以看到“添加已生成的串行版本标识”,点击即可添加serialVersionUID。

    3、性能问题

    序列化对象写入文件的性能会比自己手动写入文件低。

    参考文献

    http://www.oracle.com/technetwork/articles/java/javaserial-1536170.html

  • 相关阅读:
    PDOStatement::closeCursor
    PDOStatement::bindValue
    oracle drop table recyclebin恢复
    mysql基准测试工具tpcc-mysql安装、使用、结果解读
    MySQL字符集 GBK、GB2312、UTF8区别 解决 MYSQL中文乱码问题 收藏 MySQL中涉及的几个字符集
    [MySQL FAQ]系列 — EXPLAIN结果中哪些信息要引起关注
    [MySQL优化案例]系列 — 优化InnoDB表BLOB列的存储效率
    数据库专业词语
    老叶观点:MySQL开发规范之我见
    [MySQL FAQ]系列 — 为什么InnoDB表要建议用自增列做主键
  • 原文地址:https://www.cnblogs.com/lovesong/p/8893928.html
Copyright © 2011-2022 走看看