java io系统之定制序列化

本文主要解读java中的对象序列化部分,以及指定某些参数属性进行序列化和子对象的重新重建;也是记录学习“java编程思想”书籍的历程。

对象的序列化

我们在进行对象的序列化时,能够追踪到对象的所有引用,形象来说就是“对象网”。java序列化时会用优化的算法自动维护整个对象网。废话不多说,来上代码:

class Data implements Serializable{
    
    
    private int n;

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

    @Override
    public String toString() {
    
    
        return "Data{" +
                "n=" + n +
                '}';
    }
}

public class Alien implements Serializable {
    
    
    private static Random random = new Random(18);
    private Data[] datas = {
    
    
            new Data(1),
            new Data(2),
            new Data(2)
    };
    private Alien next;
    private char c;

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

    public Alien() {
    
    
        System.out.println("默认构造器...");
    }

    @Override
    public String toString() {
    
    
        return "Alien{" +
                "datas=" + Arrays.toString(datas) +
                ", next=" + next +
                ", c=" + c +
                '}';
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
    
    
        String filePath = "alien.out";
        Alien alien = new Alien(5, 'b');
        System.out.println(alien);
        System.out.println("序列化保存对象");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        oos.writeObject(alien);
        oos.close();

        System.out.println("反序列化读取对象");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        Alien alien1 = (Alien) ois.readObject();
        System.out.println(alien.hashCode());
        System.out.println(alien1.hashCode());
        System.out.println(alien1 == alien);
    }
}

Alien 构造器:5
Alien 构造器:4
Alien 构造器:3
Alien 构造器:2
Alien 构造器:1
Alien{datas=[Data{n=1}, Data{n=2}, Data{n=2}], next=Alien{datas=[Data{n=1}, Data{n=2}, Data{n=2}], next=Alien{datas=[Data{n=1}, Data{n=2}, Data{n=2}], next=Alien{datas=[Data{n=1}, Data{n=2}, Data{n=2}], next=Alien{datas=[Data{n=1}, Data{n=2}, Data{n=2}], next=null, c=f}, c=e}, c=d}, c=c}, c=b}
序列化保存对象
反序列化读取对象
325040804
664223387
false

注意点:
1.序列化的对象必须实现Serializable接口
2.序列化和反序列化之后的对象属性值都是相同的,但不是一个对象,hashCode不一致。
3.序列化对象会将所有的子对象一起序列化;

定制序列化

从上面的实例,我们看到java对象的序列化其实并不难操作。只要实现指定的接口即可。但是有时我们想序列对象的部分属性,或者一些敏感属性不想序列化存储,这时我们应该如何操作呢?

这里我们用两种方法实现:

1.实现Externalizable接口,重写readExternal和writeExternal方法;

public class Blip3 implements Externalizable {
    
    
    private int i;
    private String s;

    //无参构造方法
    public Blip3(){
    
    
        System.out.println("无参构造方法被调用...");
    }

    public Blip3(String x,int a){
    
    
        System.out.println("有参构造方法被调用======");
        this.s = x;
        this.i = a;
    }

    @Override
    public String toString() {
    
    
        return "Blip3{" +
                "i=" + i +
                ", s='" + s + '\'' +
                '}';
    }

    /**
     * 选择性的将对象和属性进行写出到文件
     * @param out
     * @throws IOException
     */
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
    
    
        System.out.println("blip3 writeExternal...");
        out.writeObject(s);
//        out.writeInt(i);
    }

    /**
     * 先调用readExternal,在进行有参构造
     * @param in
     * @throws IOException
     * @throws ClassNotFoundException
     */
    @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("构造对象");
        Blip3 b3 = new Blip3("A String", 47);
        System.out.println(b3);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("blip3.out"));
        System.out.println("保存对象b3");
        oos.writeObject(b3);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("blip3.out"));
        System.out.println("从文件中恢复对象");
        Blip3 readB3 = (Blip3) ois.readObject();
        System.out.println(readB3);

        System.out.println(b3==readB3);
        System.out.println(b3.hashCode());
        System.out.println(readB3.hashCode());
    }
}

构造对象
有参构造方法被调用======
Blip3{i=47, s=‘A String’}
保存对象b3
blip3 writeExternal…
从文件中恢复对象
无参构造方法被调用…
blip3 readExternal
Blip3{i=0, s=‘A String’}
false
356573597
81628611

在反序列化对象时,会先调用无参构造方法,然后调用readExternal方法将属性值赋值。

2.使用transient(瞬时)关键字

在使用transient关键字逐个字段的关闭序列化,它的意思是
“不用麻烦你保存或者回复数据——我自己会处理的”

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

    public Login(String userName,String password){
    
    
        this.userName = userName;
        this.password = password;
    }

    @Override
    public String toString() {
    
    
        return "Login{" +
                "date=" + date +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
    
    
        String filePath = "login.out";
        //序列化对象
        Login login = new Login("bob", "abcde");
        System.out.println("saving object");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        oos.writeObject(login);
        oos.close();
        System.out.println(login);
        Thread.sleep(1);

        //反序列化读取对象
        System.out.println("recovering object");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        Login login1 = (Login)ois.readObject();
        System.out.println(login1);

    }
}

saving object
Login{date=Sat Sep 12 13:37:41 CST 2020, userName=‘bob’, password=‘abcde’}
recovering object
Login{date=Sat Sep 12 13:37:41 CST 2020, userName=‘bob’, password=‘null’}

这里我们发现带有transient关键字的password属性,并没有存储到磁盘中被加载出来。
由于Externalizable对象在默认情况下不保存它们的任何属性,所以transient关键字和Serializable对象一起使用。

我们在进行序列化和反序列化时都需要对应的Class对象,如果java虚拟机找不到对相应的Class文件,就会得到一个ClassNotFoundException异常

猜你喜欢

转载自blog.csdn.net/weixin_42662358/article/details/108547885