Java序列化

文章主要切入点

Java序列化

Serializable接口

Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。也就是,序列化是将一个对象编码成一个字节流。 反序列化是将一个字节流转换成对象。

如何序列化一个对象:首先要创建某些OutputStream对象,然后将其封装在一个ObjectOutputStream对象内。这时,只需要调用writeObject()即可将对象序列化,并将其发送给OutputStream。要反向进行该过程,需要将一个InputStream封装在ObjectInputStream内,然后调用readObject()。和往常一样,我们最后获得的是一个引用,它指向一个向上转型的Object,所以必须向下转型才能直接设置它们。

示例:

class Data implements Serializable {
    private int n;

    public Data(int n) {
        this.n = n;
    }

    @Override
    public String toString() {
        return Integer.toString(n);
    }
}

public class Worm implements Serializable {
    private static Random rand = new Random(47);
    private Data[] d = {new Data(rand.nextInt(10)),
            new Data(rand.nextInt(10)),
            new Data(rand.nextInt(10))};
    private Worm next;
    private char c;

    public Worm(int i, char x) {
        System.out.println("Worm constructor:" + i);
        c = x;
        if (--i > 0) {
            next = new Worm(i, (char) (x + 1));
        }
    }

    public Worm() {
        System.out.println("Default constructor");
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder(":");
        result.append(c);
        result.append("(");
        for (Data dat : d) {
            result.append(dat);
        }
        result.append(")");
        if (next != null) {
            result.append(next);
        }
        return result.toString();
    }

    public static void main(String[] args) throws ClassNotFoundException, IOException {
        Worm w = new Worm(6, 'a');
        System.out.println("w = " + w);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("worm.out"));
        out.writeObject("Worm storage\n");
        out.writeObject(w);
        // 刷新输出
        out.close();
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("worm.out"));
        String s = (String) in.readObject();
        Worm w2 = (Worm) in.readObject();
        System.out.println(s + "w2 = " + w2);
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream out2 = new ObjectOutputStream(bout);
        out2.writeObject("Worm storage\n");
        out2.writeObject(w);
        out2.flush();
        ObjectInputStream in2 = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));
        s = (String) in2.readObject();
        Worm w3 = (Worm) in2.readObject();
        System.out.println(s + "w3 = " + w3);
    }
}

output:

Worm constructor:6
Worm constructor:5
Worm constructor:4
Worm constructor:3
Worm constructor:2
Worm constructor:1
w = :a(853):b(119):c(802):d(788):e(199):f(881)
Worm storage
w2 = :a(853):b(119):c(802):d(788):e(199):f(881)
Worm storage
w3 = :a(853):b(119):c(802):d(788):e(199):f(881)

创建一个Serializable类,它包含一个对第二个Serializable类的对象的引用。将其序列化到硬盘上,然后可以正确地恢复它。

import java.io.*;
import java.util.Random;

class A implements Serializable {
    private int i;

    A(int i) {
        this.i = i;
    }

    @Override
    public String toString() {
        return "A:" + i;
    }
}


class B implements Serializable {
    private char c;
    private A a;

    B(char c, A a) {
        this.c = c;
        this.a = a;
    }

    @Override
    public String toString() {
        return "B:" + c + " " + a;
    }
}

public class SerializationTest implements Serializable {
    private static Random rand = new Random();

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 序列化
        B b = new B('x', new A(rand.nextInt(100)));
        System.out.println("b:" + b);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("STest.out"));
        out.writeObject(b);
        out.close();
        // 反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("STest.out"));
        B b2 = (B) in.readObject();
        System.out.println("b2:" + b2);

    }
}

打开文件和读取对象中的内容都需要对应的Class对象;如果Java虚拟机找不到*.class对象,就会报ClassNotFundException异常。必须保证Java虚拟机能找到相关的.class文件。

Externalizable接口

默认的序列化机制使用方式如上所示。如果有特殊的需要,我们可通过Externalizable接口——代替实现Serializable接口——来对序列化过程进行控制。Externalizabl接口继承Serializable接口,增加了两个方法:writeExternal(ObjectOutput out)和readExternal(ObjectInput in)。这两个方法会在序列化和反序列化还原的过程中被自动调用。
示例:

import java.io.*;

class Blip1 implements Externalizable {
    public Blip1() {
        System.out.println("Blip1 Constructor");
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Blip1.writeExternal");

    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Blip1.readExternal");
    }
}

class Blip2 implements Externalizable {
    /**
     * 不是公共构造器
     */
    Blip2() {
        System.out.println("Blip2 Constructor");
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Blip2.writeExternal");
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Blip2.writeExternal");
    }
}

public class Blips {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        System.out.println("Constructing objects:");
        Blip1 b1 = new Blip1();
        Blip2 b2 = new Blip2();
        // 序列化
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("BlipCheck.out"));
        System.out.println("Saving objects:");
        o.writeObject(b1);
        o.writeObject(b2);
        o.close();
        // 反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("BlipCheck.out"));
        System.out.println("Recovering b1:");
        b1 = (Blip1)in.readObject();
        // b2不是公共构造器,恢复会产生异常
        System.out.println("Recovering b2:");
        b2 = (Blip2)in.readObject();
    }

}

output:

Constructing objects:
Blip1 Constructor
Blip2 Constructor
Saving objects:
Blip1.writeExternal
Blip2.writeExternal
Recovering b1:
Blip1 Constructor
Blip1.readExternal
// 将Blip2构造器改为public
Recovering b2:
Blip2 Constructor
Blip2.writeExternal

Externalizable对象与Serializable对象的不同

  • 对于Serializable对象,对象完全以它存储的二进制位为基础来构造,而不是调用构造器。
  • 对于Externalizable对象,所有普通的默认构造器都会被调用,然后调用readExternal( )方法。
  • 所有默认的构造器都会被调用,才能使Externalizable对象产生正确的行为。

完整示例:

import java.io.*;

public class Blip3 implements Externalizable {

    private int i;
    private String s;

    public Blip3() {
        System.out.println("Blip3 Constructor");
    }

    public Blip3(int i, String s) {
        System.out.println("Blip3(int i, String s)");
        this.i = i;
        this.s = s;
    }

    @Override
    public String toString() {
        return s + i;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("Blip3.writeExternal");
        out.writeObject(s);
        out.writeInt(i);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("Blip3.readExternal");
        s = (String) in.readObject();
        i = in.readInt();
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        System.out.println("Constructing objects:");
        Blip3 b3 = new Blip3(47, "A String ");
        System.out.println(b3);
        // 序列化
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Blip3.out"));
        System.out.println("Saving object:");
        o.writeObject(b3);
        o.close();

        // 取出来
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("Blip3.out"));
        System.out.println("Recovering b3:");
        b3 = (Blip3) in.readObject();
        System.out.println(b3);

    }
}

output:

Constructing objects:
Blip3(int i, String s)
A String 47
Saving object:
Blip3.writeExternal
Recovering b3:
Blip3 Constructor
Blip3.readExternal
A String 47

用途:实现Externalizable接口的类,在默认情况下不保存它们的任何字段。类实现Externalizable,可以防止对象的敏感部分被序列化。没有任何东西可以自动序列化,并且可以在writeExternal()内部只对所需部分进行显式的序列化。

transient(瞬时)关键字

如果我们操作的是一个Serializable对象,那么所有序列化操纵都会自动进行。如果某个字段不想序列化,那么用transient关键字进行修饰就可以了。

import java.io.*;
import java.util.Date;
import java.util.concurrent.TimeUnit;

public class Logon implements Serializable {
    private Date date = new Date();
    private String username;
    private transient String password;

    public Logon(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public String toString() {
        return "logon info:\n username:" + username +
                "\n date:" + date + "\n password:" + password;
    }

    public static void main(String[] args) throws Exception {
        Logon a = new Logon("Hulk", "myLittlePony");
        System.out.println("logon a = " + a);
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Logon.out"));
        o.writeObject(a);
        o.close();
        TimeUnit.SECONDS.sleep(1);

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("Logon.out"));
        System.out.println("Recovering object at " + new Date());
        a = (Logon) in.readObject();
        System.out.println("logon a = " + a);
    }
}

output:

logon a = logon info:
 username:Hulk
 date:Thu May 16 10:43:19 CST 2019
 password:myLittlePony
Recovering object at Thu May 16 10:43:20 CST 2019
logon a = logon info:
 username:Hulk
 date:Thu May 16 10:43:19 CST 2019
 password:null

static

static字段的序列化需要自己去动手实现。
序列化代码:

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

abstract class Shape implements Serializable {
    public static final int RED = 1, BLUE = 2, GREEN = 3;
    private int xPos, yPos, dimension;
    private static Random rand = new Random(47);
    private static int counter = 0;

    public abstract void setColor(int newColor);

    public abstract int getColor();

    public Shape(int xVal, int yVal, int dim) {
        xPos = xVal;
        yPos = yVal;
        dimension = dim;
    }

    @Override
    public String toString() {
        return getClass() + " color[" + getColor() + "] xPos[" + xPos + "] yPos[" + yPos + "] dim[" + dimension + "]\n";
    }

    public static Shape randomFactory() {
        int xVal = rand.nextInt(100);
        int yVal = rand.nextInt(100);
        int dim = rand.nextInt(100);
        switch (counter++ % 3) {
            default:
            case 0:
                return new Circle(xVal, yVal, dim);
            case 1:
                return new Square(xVal, yVal, dim);
            case 2:
                return new Line(xVal, yVal, dim);
        }
    }
}

class Circle extends Shape {
    private static int color = RED;

    //public static void serializeStaticState(ObjectOutputStream os)
    //        throws IOException {
    //    os.writeInt(color);
    //}
    //
    //public static void deserializeStaticState(ObjectInputStream os)
    //        throws IOException {
    //    color = os.readInt();
    //}

    public Circle(int xVal, int yVal, int dim) {
        super(xVal, yVal, dim);
    }

    @Override
    public void setColor(int newColor) {
        color = newColor;
    }

    @Override
    public int getColor() {
        return color;
    }
}


class Square extends Shape {
    private static int color;

    //public static void serializeStaticState(ObjectOutputStream os)
    //        throws IOException {
    //    os.writeInt(color);
    //}
    //
    //public static void deserializeStaticState(ObjectInputStream os)
    //        throws IOException {
    //    color = os.readInt();
    //}

    public Square(int xVal, int yVal, int dim) {
        super(xVal, yVal, dim);
        color = RED;
    }

    @Override
    public void setColor(int newColor) {
        color = newColor;
    }

    @Override
    public int getColor() {
        return color;
    }
}

class Line extends Shape {
    private static int color = RED;

    public static void serializeStaticState(ObjectOutputStream os) throws IOException {
        os.writeInt(color);
    }

    public static void deserializeStaticState(ObjectInputStream os) throws IOException {
        color = os.readInt();
    }

    public Line(int xVal, int yVal, int dim) {
        super(xVal, yVal, dim);
    }

    @Override
    public void setColor(int newColor) {
        color = newColor;
    }

    @Override
    public int getColor() {
        return color;
    }
}

public class StoreCADState {
    public static void main(String[] args) throws IOException {
        List<Shape> shapes = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
            shapes.add(Shape.randomFactory());
        }

        // 将所有静态颜色设置为绿色
        for (int i = 0; i < 10; i++) {
            shapes.get(i).setColor(Shape.GREEN);
        }

        // 保存状态向量
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("CADState.out"));
        
        //Circle.serializeStaticState(out);
        //Square.serializeStaticState(out);
        Line.serializeStaticState(out);
        out.writeObject(shapes);
        System.out.println(shapes);
    }

}

output:

[class Circle color[3] xPos[58] yPos[55] dim[93]
, class Square color[3] xPos[61] yPos[61] dim[29]
, class Line color[3] xPos[68] yPos[0] dim[22]
, class Circle color[3] xPos[7] yPos[88] dim[28]
, class Square color[3] xPos[51] yPos[89] dim[9]
, class Line color[3] xPos[78] yPos[98] dim[61]
, class Circle color[3] xPos[20] yPos[58] dim[16]
, class Square color[3] xPos[40] yPos[11] dim[22]
, class Line color[3] xPos[4] yPos[83] dim[6]
, class Circle color[3] xPos[75] yPos[10] dim[42]
]

反序列化代码:

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;

public class RecoverCADState {
    public static void main(String[] args) throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("CADState.out"));
        //Circle.deserializeStaticState(in);
        //Square.deserializeStaticState(in);
        Line.deserializeStaticState(in);
        List<Shape> shapes = (List<Shape>) in.readObject();
        System.out.println(shapes);
    }
}

output:

[class Circle color[1] xPos[58] yPos[55] dim[93]
, class Square color[0] xPos[61] yPos[61] dim[29]
, class Line color[3] xPos[68] yPos[0] dim[22]
, class Circle color[1] xPos[7] yPos[88] dim[28]
, class Square color[0] xPos[51] yPos[89] dim[9]
, class Line color[3] xPos[78] yPos[98] dim[61]
, class Circle color[1] xPos[20] yPos[58] dim[16]
, class Square color[0] xPos[40] yPos[11] dim[22]
, class Line color[3] xPos[4] yPos[83] dim[6]
, class Circle color[1] xPos[75] yPos[10] dim[42]
]

从输出的结果可以知道,static数据根本没有被序列化,只有Line.class中显示的是正确的。所以想序列化static的值,必须动手去实现。将上面的代码中注视的代码恢复,就可以得到正确的值了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值