• java序列化


    序列化:保存对象的状态

    反序列化:把对象的状态再读出来

    一般一下几种情况下,会用到序列化:

    1. 想把内存中的对象状态保存到文件中
    2. 想用套接字再网络上传输对象
    3. 通过RMI(remote method Invocation)传输对象

    示例一:

    简单序列化用法:

    Box类:

    public class Box implements Serializable {
        private static final long serialVersionUID = 1L;
        private int width;
        private int height;
        private String name;
    
        public Box(String name, int width, int height) {
        this.name = name;
        this.width = width;
        this.height = height;
        }
    
        @Override
        public String toString() {
        return "[" + name + ": (" + width + ", " + height + ") ]";
        }
    }
    public class SerialTest1 {
        private static final String PATH = "D:\baiduyun\filetest\ddd.txt";
    
        public static void main(String[] args) throws Exception {
        // 将“对象”通过序列化保存
        testWrite();
        // 将序列化的“对象”读出来
        testRead();
    
        }
    
        private static void testRead() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(PATH));
        Box box = (Box) ois.readObject();
        System.out.println(box);
        ois.close();
        }
    
        private static void testWrite() throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(PATH));
        Box box = new Box("box", 152, 132);
        oos.writeObject(box);
        oos.close();
        }
    
    }

     输出结果:[box: (152, 132) ]

    通过上面的示例,我们知道:我们可以自定义类,让他支持序列化(实现Serializable接口),从而支持对象的保存和恢复.

    其实"java基本类型和java自带的实现了serializable接口"的类都支持序列化,下面通过示例二来看.

    示例二:

    private static final String PATH = "D:\baiduyun\filetest\ddd.txt";
    
        public static void main(String[] args) throws Exception {
        testWrite();
        testRead();
    
        }
    
        private static void testRead() throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(PATH));
        System.out.printf("boolean:%b
    ", in.readBoolean());
        System.out.printf("byte:%d
    ", (in.readByte() & 0xff));
        System.out.printf("char:%c
    ", in.readChar());
        System.out.printf("int:%d
    ", in.readInt());
        System.out.printf("float:%f
    ", in.readFloat());
        System.out.printf("double:%f
    ", in.readDouble());
        // 读取HashMap对象
        HashMap map = (HashMap) in.readObject();
        Iterator iter = map.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            System.out.printf("%-6s -- %s
    ", entry.getKey(), entry.getValue());
        }
        in.close();
        }
    
        private static void testWrite() throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(PATH));
        out.writeBoolean(true); // 写入Boolean值
        out.writeByte((byte) 65);// 写入Byte值
        out.writeChar('a'); // 写入Char值
        out.writeInt(20131015); // 写入Int值
        out.writeFloat(3.14F); // 写入Float值
        out.writeDouble(1.414D);// 写入Double值
        // 写入HashMap对象
        HashMap map = new HashMap();
        map.put("one", "red");
        map.put("two", "green");
        map.put("three", "blue");
        out.writeObject(map);
        out.close();
    
        }

    输出结果:

    boolean:true
    byte:65
    char:a
    int:20131015
    float:3.140000
    double:1.414000
    one -- red
    two -- green
    three -- blue

    上面两个示例我们知道了序列化和反序列化的简单应该,下面来看看序列化的高级应该.

    1. 序列化对static和transient变量,是不会自动进行状态保存的
    2. 对于Socket,Thread类,不支持序列化,若实现序列化的接口中,有Thread成员,对该类进行编译时会报错

     下面演示序列化对static和transient的处理:

    示例三:

    public class Box implements Serializable {
        private static final long serialVersionUID = 1L;
        private static int width;
        private transient int height;
        private String name;
    
        public Box(String name, int width, int height) {
        this.name = name;
        this.width = width;
        this.height = height;
        }
    
        @Override
        public String toString() {
        return "[" + name + ": (" + width + ", " + height + ") ]";
        }
    }
    public class SerialTest3 {
        private static final String PATH = "D:\baiduyun\filetest\ddd.txt";
    
        public static void main(String[] args) throws Exception {
        // 将“对象”通过序列化保存
        testWrite();
        // 将序列化的“对象”读出来
        testRead();
    
        }
    
        private static void testRead() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(PATH));
        Box box = (Box) ois.readObject();
        System.out.println(box);
        ois.close();
        }
    
        private static void testWrite() throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(PATH));
        Box box = new Box("box", 15, 13);
        oos.writeObject(box);
        oos.close();
        }
    
    }

     输出结果:

    [box: (15, 0) ]

    为什么height=0呢?因为height是int类型,int的默认值是0.

    为什么width=15呢,因为width是static类型,static意味着所有类共享该值,在testWrite()中我们初始化了box的width为15,所以取出来是15.

     理解上面的内容之后,我们应该可以推断出下面的代码的运行结果。

    示例四:

    /**
     * 序列化的演示测试程序
     *
     * */
    
    import java.io.FileInputStream;   
    import java.io.FileOutputStream;   
    import java.io.ObjectInputStream;   
    import java.io.ObjectOutputStream;   
    import java.io.Serializable;   
      
    public class SerialTest4 { 
        private static final String TMP_FILE = ".serialtest4.txt";
      
        public static void main(String[] args) {   
            // 将“对象”通过序列化保存
            testWrite();
            // 将序列化的“对象”读出来
            testRead();
        }
      
    
        /**
         * 将Box对象通过序列化,保存到文件中
         */
        private static void testWrite() {   
            try {
                // 获取文件TMP_FILE对应的对象输出流。
                // ObjectOutputStream中,只能写入“基本数据”或“支持序列化的对象”
                ObjectOutputStream out = new ObjectOutputStream(
                        new FileOutputStream(TMP_FILE));
                // 创建Box对象,Box实现了Serializable序列化接口
                Box box = new Box("desk", 80, 48);
                // 将box对象写入到对象输出流out中,即相当于将对象保存到文件TMP_FILE中
                out.writeObject(box);
                // 打印“Box对象”
                System.out.println("testWrite box: " + box);
                // 修改box的值
                box = new Box("room", 100, 50);
    
                out.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
     
        /**
         * 从文件中读取出“序列化的Box对象”
         */
        private static void testRead() {
            try {
                // 获取文件TMP_FILE对应的对象输入流。
                ObjectInputStream in = new ObjectInputStream(
                        new FileInputStream(TMP_FILE));
                // 从对象输入流中,读取先前保存的box对象。
                Box box = (Box) in.readObject();
                // 打印“Box对象”
                System.out.println("testRead  box: " + box);
                in.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    
    /**
     * Box类“支持序列化”。因为Box实现了Serializable接口。
     *
     * 实际上,一个类只需要实现Serializable即可实现序列化,而不需要实现任何函数。
     */
    class Box implements Serializable {
        private static int width;   
        private transient int height; 
        private String name;   
    
        public Box(String name, int width, int height) {
            this.name = name;
            this.width = width;
            this.height = height;
        }
    
        @Override
        public String toString() {
            return "["+name+": ("+width+", "+height+") ]";
        }
    }

     运行结果:

    testWrite box: [desk: (80, 48) ]

    testRead box: [desk: (100, 0) ]

     现在我们更加确认"序列化不对static和transient变量进行保存"

    下面演示怎么保存static和transient变量

     示例五:

    如果要保存static和transient变量,我们需要重写writeObject()和readObject()对象

    Box5.java

    public class Box5 implements Serializable {
        /**
         * @Fields field:field:{todo}(用一句话描述这个变量表示什么)
         */
        private static final long serialVersionUID = 1L;
        private static int width;
        private transient int height;
        private String name;
    
        public Box5(String name, int width, int height) {
        this.name = name;
        this.width = width;
        this.height = height;
        }
    
        @Override
        public String toString() {
        return "[" + name + ": (" + width + ", " + height + ") ]";
        }
    
        private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(height);
        out.writeInt(width);
        }
    
        private void readObject(ObjectInputStream in) throws Exception {
        in.defaultReadObject();
        height = in.readInt();
        width = in.readInt();
        }
    
    }
    public class SerialTest5 {
        private static final String PATH = "D:\baiduyun\filetest\ddd.txt";
    
        public static void main(String[] args) throws Exception {
        // 将“对象”通过序列化保存
        testWrite();
        // 将序列化的“对象”读出来
        testRead();
        }
    
        private static void testRead() throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(PATH));
        Box5 box5 = (Box5) in.readObject();
        System.out.println("read:" + box5);
        in.close();
        }
    
        private static void testWrite() throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(PATH));
        Box5 box5 = new Box5("box5", 100, 200);
        out.writeObject(box5);
        System.out.println("write:" + box5);
        // 修改box的值
        box5 = new Box5("room", 500, 50);
        System.out.println("modify:" + box5);
        out.close();
        }
    
    }

    输出结果:

    write:[box5: (100, 200) ]
    modify:[room: (500, 50) ]
    read:[box5: (100, 200) ]

    很明显我们把static和transient变量也序列化了.

    接下来,我们来看看"对于socket,thread类,不支持序列化"

    示例六:

    box6中添加Thread变量

    public class Box6 implements Serializable {
    
        /**
         * @Fields field:field:{todo}(用一句话描述这个变量表示什么)
         */
        private static final long serialVersionUID = 1L;
        private static int width;
        private transient int height;
        private String name;
        private Thread thread = new Thread(new Runnable() {
    
        @Override
        public void run() {
            System.out.println("Serializable test!");
    
        }
        });
    
        public Box6(String name, int width, int height) {
        this.name = name;
        this.width = width;
        this.height = height;
        }
    
        @Override
        public String toString() {
        return "[" + name + ": (" + width + ", " + height + ") ]";
        }
    
        private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(height);
        out.writeInt(width);
        }
    
        private void readObject(ObjectInputStream in) throws Exception {
        in.defaultReadObject();
        height = in.readInt();
        width = in.readInt();
        }
    
    }
    public class SerialTest6 {
        private static final String PATH = "D:\baiduyun\filetest\ddd.txt";
    
        public static void main(String[] args) throws Exception {
        // 将“对象”通过序列化保存
        testWrite();
        // 将序列化的“对象”读出来
        testRead();
        }
    
        private static void testRead() throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(PATH));
        Box6 box6 = (Box6) in.readObject();
        System.out.println("read:" + box6);
        in.close();
        }
    
        private static void testWrite() throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(PATH));
        Box6 box6 = new Box6("box6", 100, 200);
        out.writeObject(box6);
        System.out.println("write:" + box6);
        // 修改box的值
        box6 = new Box6("room", 500, 50);
        System.out.println("modify:" + box6);
        out.close();
        }
    
    }

    运行结果:

    Exception in thread "main" java.io.NotSerializableException: java.lang.Thread
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
        at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441)
        at com.zhangj.ymm.thinking.io.serial.Box6.writeObject(Box6.java:38)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1140)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
        at com.zhangj.ymm.thinking.io.serial.SerialTest6.testWrite(SerialTest6.java:28)
        at com.zhangj.ymm.thinking.io.serial.SerialTest6.main(SerialTest6.java:13)

    结果报错啦,事实证明我们不能对Thread进行序列化.若要编译通过我们可以在Thread变量前添加static或者transient修饰.

     如果一个类要完全负责自己的序列化,则要实现Externalizable接口,然后重写writeExternal()和readExterbal()方法.声明类实现Externalizble接口会有重大的安全风险,writeExterbal()和readExternal()方法为public,恶意类可以用这些方法读取和写入对象.

    示例七:

    实现Externalizable接口

    public class Box7 implements Externalizable {
        private int width;
        private int height;
        private String name;
    
        public Box7() {
        }
    
        public Box7(String name, int width, int height) {
        this.name = name;
        this.width = width;
        this.height = height;
        }
    
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeInt(height);
        out.writeInt(width);
    
        }
    
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        height = in.readInt();
        width = in.readInt();
        }
    
        @Override
        public String toString() {
        return "[" + name + ": (" + width + ", " + height + ") ]";
        }
    
    }
    public class SerialTest7 {
        private static final String PATH = "D:\baiduyun\filetest\ddd.txt";
    
        public static void main(String[] args) throws Exception {
        testWrite();
        testRead();
    
        }
    
        private static void testRead() throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(PATH));
        Box7 box7 = (Box7) in.readObject();
        System.out.println(box7);
        in.close();
        }
    
        private static void testWrite() throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(PATH));
        Box7 box7 = new Box7("box7", 100, 200);
        out.writeObject(box7);
        box7 = new Box7("box77", 150, 250);
        out.close();
        }
    }

    运行结果:

    [box7: (100, 200) ] 

  • 相关阅读:
    Eclipse
    文件递归查找
    BeanFactory 和 AppliactionContext的区别?
    文件上传
    Servlet路径的使用
    FileInputStream和FileOutputStream文件复制
    CentOS 7安装Nginx
    C语言程序设计100例之(6):数字反转
    C语言程序设计100例之(5):分解质因数
    C语言程序设计100例之(4):水仙花数
  • 原文地址:https://www.cnblogs.com/zhangj-ymm/p/9878647.html
Copyright © 2020-2023  润新知