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\n", in.readBoolean());
    System.out.printf("byte:%d\n", (in.readByte() & 0xff));
    System.out.printf("char:%c\n", in.readChar());
    System.out.printf("int:%d\n", in.readInt());
    System.out.printf("float:%f\n", in.readFloat());
    System.out.printf("double:%f\n", 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\n", 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) ] 

 

posted @ 2018-10-30 18:31  橘右京  阅读(196)  评论(0编辑  收藏  举报