1. 什么是序列化和反序列化
-
序列化:将对象的状态信息转换为可以存储或传输的形式(通常是字节序列)的过程。例如,将一个 Java 对象保存到文件中或者通过网络发送给其他程序。
-
反序列化:将字节序列恢复为对象的过程。比如,从文件中读取字节序列并将其转换为 Java 对象。
2. 为什么要实现 Serializable
接口
Serializable
接口是一个标记接口,它本身不包含任何方法。其作用是告诉 Java 虚拟机(JVM)该类的对象可以被序列化。当一个类实现了 Serializable
接口,就相当于给这个类打上了一个 “可以被序列化” 的标记。JVM 在进行序列化操作时,会检查对象所属的类是否实现了 Serializable
接口,如果没有实现,就会抛出 NotSerializableException
异常。
示例代码:
import java.io.*;
// 实现 Serializable 接口
class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class SerializationExample {
public static void main(String[] args) {
Person person = new Person("Alice", 25);
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
oos.writeObject(person);
System.out.println("对象已序列化");
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person deserializedPerson = (Person) ois.readObject();
System.out.println("对象已反序列化");
System.out.println("Name: " + deserializedPerson.getName());
System.out.println("Age: " + deserializedPerson.getAge());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
代码中,Person
类实现了 Serializable
接口,因此可以对其对象进行序列化和反序列化操作。
3. 除了Serializable接口,还有
哪些方式可以控制Java对象的序列化?
1. 使用 transient
关键字
transient
关键字用于修饰类的成员变量,被 transient
修饰的变量在序列化过程中会被忽略,即不会被保存到字节流中。在反序列化时,这些变量会被赋予默认值(如 null
、0
等)。
示例代码:
import java.io.*;
class User implements Serializable {
private String username;
private transient String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
public class TransientExample {
public static void main(String[] args) {
User user = new User("admin", "123456");
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
oos.writeObject(user);
User deserializedUser = (User) ois.readObject();
System.out.println("Username: " + deserializedUser.getUsername());
System.out.println("Password: " + deserializedUser.getPassword());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
2. 使用 Externalizable
接口
Externalizable
接口继承自 Serializable
接口,它要求实现类必须重写 writeExternal
和 readExternal
方法,通过这两个方法可以完全自定义对象的序列化和反序列化过程。
import java.io.*;
class Book implements Externalizable {
private String title;
private int year;
// 必须有一个无参构造函数
public Book() {}
public Book(String title, int year) {
this.title = title;
this.year = year;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(title);
out.writeInt(year);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
title = (String) in.readObject();
year = in.readInt();
}
public String getTitle() {
return title;
}
public int getYear() {
return year;
}
}
public class ExternalizableExample {
public static void main(String[] args) {
Book book = new Book("Java Programming", 2023);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("book.ser"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("book.ser"))) {
oos.writeObject(book);
Book deserializedBook = (Book) ois.readObject();
System.out.println(deserializedBook.getTitle() + " - " + deserializedBook.getYear());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
3. 自定义 writeObject
和 readObject
方法
在实现 Serializable
接口的类中,可以定义私有方法 writeObject
和 readObject
来定制序列化和反序列化的逻辑。当 Java 进行序列化和反序列化操作时,会自动调用这两个方法。
import java.io.*;
class Product implements Serializable {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
// 可以添加额外的序列化逻辑
out.writeDouble(price * 0.9); // 序列化打折后的价格
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
// 可以添加额外的反序列化逻辑
price = in.readDouble();
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
public class CustomSerializationExample {
public static void main(String[] args) {
Product product = new Product("Laptop", 1000);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("product.ser"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("product.ser"))) {
oos.writeObject(product);
Product deserializedProduct = (Product) ois.readObject();
System.out.println(deserializedProduct.getName() + " - $" + deserializedProduct.getPrice());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
4. 使用 writeReplace
和 readResolve
方法
writeReplace
方法允许在序列化之前替换要序列化的对象,而 readResolve
方法允许在反序列化之后替换反序列化得到的对象。这两个方法可以用于实现单例模式的序列化和反序列化,确保单例的唯一性。
import java.io.*;
class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
private Object writeReplace() throws ObjectStreamException {
return INSTANCE;
}
private Object readResolve() throws ObjectStreamException {
return INSTANCE;
}
}
public class ReplaceExample {
public static void main(String[] args) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton.ser"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton.ser"))) {
oos.writeObject(Singleton.getInstance());
Singleton deserializedSingleton = (Singleton) ois.readObject();
System.out.println(Singleton.getInstance() == deserializedSingleton); // 输出 true
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
这些方式可以根据不同的需求灵活控制 Java 对象的序列化过程,以满足安全性、性能等方面的要求。