浅谈Java序列化

本文深入探讨了Java序列化的基本概念、应用场景以及实现方法,包括如何通过序列化将对象状态保存到文件或网络传输,如何保护敏感信息不被序列化,如何序列化静态变量,以及如何在不同机器间实现对象的准确传输。通过实例代码演示了实现序列化的主要步骤,包括基本实现、使用transient关键字、静态变量处理等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.什么是Java序列化

Serialization(序列化)是一种将对象以一连串的字节描述的过程;反序列化deserialization是一种将这些字节重建成一个对象的过程。
它面向那些实现了Serializable 接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。这一过程亦可通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。换句话说,可以先在 Windows 机器上创建一个对象,对其序列化,然后通过网络发给一台 Unix 机器,然后在那里准确无误地重新“装配”。不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。

2.为什么要进行序列化

  • 当你想把的内存中的对象保存到一个文件中或者数据库中时候;
  • 当你想用套接字在网络上传送对象的时候;
  • 当你想通过RMI传输对象的时候;

3.如何实现序列化

1.最基本,实现Serializable接口即可
例子:

public class User implements Serializable {
    private String username;
    private String password;

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

    @Override
    public String toString() {
        return username+" "+password;
    }

    public static void main(String[] args) {
        User user = new User("jason","123456");
        System.out.println(user);
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.out"));
            objectOutputStream.writeObject(user);
            System.out.println("recover object");
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.out"));
            User user1 = (User) objectInputStream.readObject();
            System.out.println(user1);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

jason 123456
recover object
jason 123456

2.使用transient变量,保护一些变量不被序列化
password加入了transient变量,就不被序列化

private transient String password;

输出结果

jason 123456
recover object
jason null

password变量未被序列化
3.static变量不被序列化

public class User implements Serializable {
    private String username;
    private String password;
    private static int a = 2;

    public User(String username,String password) {
        this.username = username;
        this.password = password;
        a++;
    }

    @Override
    public String toString() {
        return username+" "+password+" "+a;
    }

    public static void main(String[] args) {
        User user = new User("jason","123456");
        System.out.println(user);
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.out"));
            objectOutputStream.writeObject(user);
            System.out.println("recover object");
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.out"));
            User user1 = (User) objectInputStream.readObject();
            System.out.println(user1);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

jason 123456 3
recover object
jason 123456 3

不是说static变量不能被序列化么,怎么序列出来还是3
静态成员属于类级别的,所以不能序列化
static变量不能序列化指的是序列化信息中不包含这个静态成员域
上述代码测试成功是因为都在同一个机器(而且是同一个进程),因为当前jvm已经把count加载进来了,所以获取的是加载好的count,如果你是传到另一台机器或者你关掉程序重写写个程序读入user.out,或者将反序列化写在另外一个代码里,此时因为别的机器或新的进程是重新加载count的,所以count信息就是初始时的信息。
单独的测试代码

public class Client {
    public static void main(String[] args) {
        try {
            System.out.println("recover object");
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.out"));
            User user1 = (User) objectInputStream.readObject();
            System.out.println(user1);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

测试结果:
recover object
jason 123456 2
还是2

4.如何序列化写入static变量
三种方法:
实现Externalilzable接口;
仍然实现Serializable接口,添加writeObject(), readObject()方法;
仍然实现Serializable,给ObjectOutputStream写入static变量,ObjectInputStream读入static变量

  • 实现Externalilzable接口
public class User2 implements Externalizable {
    private String username;
    private String password;
    private static int a = 2;

    public User2() {

    }

    public User2(String username, String password) {
        this.username = username;
        this.password = password;
        a++;
    }

    @Override
    public String toString() {
        return username+" "+password+" "+a;
    }

    public static void main(String[] args) {
        User2 user = new User2("jason","123456");
        System.out.println(user);
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.out"));
            objectOutputStream.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(username);
        out.writeObject(password);
        out.writeInt(a);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        username = (String) in.readObject();
        password = (String) in.readObject();
        a = in.readInt();
    }
}

测试代码,另外一个文件

public class Client {
    public static void main(String[] args) {
        try {
            System.out.println("recover object");
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.out"));
            User2 user1 = (User2) objectInputStream.readObject();
            System.out.println(user1);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

recover object
jason 123456 3
  • 仍然实现Serializable接口,添加writeObject(), readObject()方法

注意writeObject()和readObject()两个方法的函数签名必须和下面一样

private void writeObject(ObjectOutputStream oos) throws IOException
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException
public class User3 implements Serializable {
    private String username;
    private String password;
    private static int a = 2;

    public User3(String username,String password) {
        this.username = username;
        this.password = password;
        a = 3;
    }

    @Override
    public String toString() {
        return username+" "+password+" "+a;
    }

    public static void main(String[] args) {
        User3 user = new User3("jason","123456");
        System.out.println(user);
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.out"));
            objectOutputStream.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        oos.writeInt(a);
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        a = ois.readInt();
    }
}

输出结果:

recover object
jason 123456 3
  • 仍然实现Serializable,给ObjectOutputStream写入static变量,ObjectInputStream读入static变量
public class User4 implements Serializable {
    private String username;
    private String password;
    private static int a = 2;

    public User4(String username, String password) {
        this.username = username;
        this.password = password;
        a = 3;
    }

    @Override
    public String toString() {
        return username+" "+password+" "+a;
    }

    public static void main(String[] args) {
        User4 user = new User4("jason","123456");
        System.out.println(user);
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("user.out"));
            User4.serializeStatic(objectOutputStream);
            objectOutputStream.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void serializeStatic(ObjectOutputStream oos) throws IOException {
        oos.writeInt(a);
    }

    public static void deseralizeStatic(ObjectInputStream ois) throws IOException,ClassNotFoundException {
        a = ois.readInt();
    }
}

测试文件

public class Client {
    public static void main(String[] args) {
        try {
            System.out.println("recover object");
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("user.out"));
            User4.deseralizeStatic(objectInputStream);
            User4 user4 = (User4) objectInputStream.readObject();
            System.out.println(user4);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

测试结果

recover object
jason 123456 3

注意此时写入static变量在写入整体变量前面,否则报错

4.序列化总结

  1. transient变量不被序列化,这样就可包含一些变量,如密码之类的
  2. static变量默认不被序列化,为什么呢?
    static是类共享的,当该类一个对象被序列化后,这个static变量可能会被另一个对象改变,所以这决定了静态变量是不能序列化的,但如果是final,就可以,因为这是一个常量
  3. 上述三种写入static的方法,其实也是序列化中提供给我们三种 自定义写序列化的方式,不仅仅是static变量,我们可以自己定义哪些变量需要写入,哪些变量不需要写入。
  4. 只要将所有东西都序列化到单独一个数据流里,就能恢复获得与以前写入时完全一样的对象网,不会不慎造成对象的重复。如果不在同一个序列化里面,我们就无法判断该对象是否和其他流里面的对象是否一样。
    例子:

House.java

public class House implements Serializable {
}

Animal.java

public class Animal implements Serializable {
    private String name;
    private House preferredHouse;

    public Animal(String name,House preferredHouse) {
        this.name = name;
        this.preferredHouse = preferredHouse;
    }

    @Override
    public String toString() {
        return name + "[" + super.toString() +
                "], " + preferredHouse + "\n";
    }

}

测试代码

public class Client {

    public static void main(String[] args) {
        House house = new House();
        Vector<Animal> animals = new Vector<Animal>();

        animals.addElement(new Animal("cat",house));
        animals.addElement(new Animal("dog",house));
        animals.addElement(new Animal("snake",house));

        try {
            ByteArrayOutputStream byteArrayOutputStream1 = new ByteArrayOutputStream();
            ObjectOutputStream out1 = new ObjectOutputStream(byteArrayOutputStream1);
            out1.writeObject(animals);
            out1.writeObject(animals);
            ObjectInputStream in1 = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream1.toByteArray()));
            Vector<Animal> animals1 = (Vector<Animal>) in1.readObject();
            Vector<Animal> animals2 = (Vector<Animal>) in1.readObject();

            ByteArrayOutputStream byteArrayOutputStream2 = new ByteArrayOutputStream();
            ObjectOutputStream out2 = new ObjectOutputStream(byteArrayOutputStream2);
            out2.writeObject(animals);
            ObjectInputStream in2 = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream2.toByteArray()));
            Vector<Animal> animals3 = (Vector<Animal>) in2.readObject();

            System.out.println(animals1);
            System.out.println(animals2);
            System.out.println(animals3);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

}

输出结果:

[cat[Serialization.Persistence.Animal@3764951d], Serialization.Persistence.House@4b1210ee
, dog[Serialization.Persistence.Animal@4d7e1886], Serialization.Persistence.House@4b1210ee
, snake[Serialization.Persistence.Animal@3cd1a2f1], Serialization.Persistence.House@4b1210ee
]
[cat[Serialization.Persistence.Animal@3764951d], Serialization.Persistence.House@4b1210ee
, dog[Serialization.Persistence.Animal@4d7e1886], Serialization.Persistence.House@4b1210ee
, snake[Serialization.Persistence.Animal@3cd1a2f1], Serialization.Persistence.House@4b1210ee
]
[cat[Serialization.Persistence.Animal@2f0e140b], Serialization.Persistence.House@7440e464
, dog[Serialization.Persistence.Animal@49476842], Serialization.Persistence.House@7440e464
, snake[Serialization.Persistence.Animal@78308db1], Serialization.Persistence.House@7440e464
]

我们发现,animal1,animal2是完全一样的,因为在一个流里面,不会造成对象重复,而恢复animal3没有办法知道另一个流内的对象是第一个流内对象的化身,所以会产生一个完全不同的对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值