Java中对象的序列化详解及实例攻略
什么是序列化
序列化是将对象转换为字节序列的过程,以便将其存储到文件或内存缓冲区中,也可以通过网络传输到另一个计算机中。反序列化则是从字节序列中重构对象的过程。
在Java中,序列化是通过实现Serializable
接口来实现的。该接口中没有方法,只是用来指示该类是可序列化的。
序列化的作用
序列化在实际开发中非常有用。以下是序列化的主要用途:
- 持久化:序列化对象并将其保存到文件或数据库中以供以后使用。
- 交换数据:跨操作系统平台的应用程序可以使用序列化来交换数据。
- 远程调用:远程调用(RMI)机制使用序列化来传递对象参数和返回值。
序列化的基本用法
下面有一个简单的示例,展示了如何序列化和反序列化一个对象:
import java.io.*;
public class SerializationDemo {
public static void main(String[] args) {
// 序列化一个对象到文件中
Student student = new Student(101, "John Doe");
try {
FileOutputStream fileOut = new FileOutputStream("student.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(student);
out.close();
fileOut.close();
System.out.println("Serialized data is saved as student.ser");
} catch (IOException i) {
i.printStackTrace();
}
// 从文件中反序列化对象
Student deserializedStudent = null;
try {
FileInputStream fileIn = new FileInputStream("student.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
deserializedStudent = (Student) in.readObject();
in.close();
fileIn.close();
} catch (IOException | ClassNotFoundException i) {
i.printStackTrace();
}
// 打印反序列化后的对象
System.out.println("ID: " + deserializedStudent.id);
System.out.println("Name: " + deserializedStudent.name);
}
}
class Student implements Serializable {
private static final long serialVersionUID = 1L;
int id;
String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
}
上述代码需要先定义一个Student
类,然后使用FileOutputStream
和ObjectOutputStream
将对象序列化为student.ser
文件,再使用FileInputStream
和ObjectInputStream
将对象从文件中反序列化。最后,打印反序列化后的对象。
序列化的问题和解决方法
虽然序列化看起来很简单,但是它有很多问题,我会在下面的几个例子中进行说明。
示例1:序列化带有transient
字段的对象
假设我们有以下Person
类:
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
String name;
transient String password;
public Person(String name, String password) {
this.name = name;
this.password = password;
}
public String toString() {
return "Name: " + name + ", Password: " + password;
}
}
其中,password
字段被标记为transient
,表示它不应被序列化。这是因为密码通常是机密信息,不应该保存在磁盘上。
我们可以使用以下代码序列化和反序列化该对象:
import java.io.*;
public class SerializationTransientDemo {
public static void main(String[] args) {
// 序列化带transient字段的对象
Person person = new Person("John Doe", "123456");
try {
FileOutputStream fileOut = new FileOutputStream("person.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(person);
out.close();
fileOut.close();
System.out.println("Serialized data is saved as person.ser");
} catch (IOException i) {
i.printStackTrace();
}
// 反序列化带transient字段的对象
Person deserializedPerson = null;
try {
FileInputStream fileIn = new FileInputStream("person.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
deserializedPerson = (Person) in.readObject();
in.close();
fileIn.close();
} catch (IOException | ClassNotFoundException i) {
i.printStackTrace();
}
System.out.println(deserializedPerson);
}
}
然而,当你运行上面的代码后,你会发现输出为:Name: John Doe, Password: null
。这是因为密码字段被标记为transient
,它不会被序列化,所以在反序列化时,密码字段被设置为null
。
为了解决这个问题,可以在Person
类中添加以下方法:
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(password);
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
password = (String) ois.readObject();
}
这两个方法将会在序列化和反序列化时得到调用,writeObject()
方法会自动对transient字段进行处理,并序列化其它non-transient字段,readObject()
方法会自动将序列化后的transient字段恢复成原来的值。
现在,在你运行上述代码后,输出应为:Name: John Doe, Password: 123456
。
示例2:序列化一个继承自非实现Serializable接口的类的对象
假设我们有以下Animal
接口和Dog
类:
interface Animal {
int getAge();
}
class Dog implements Animal {
int age;
public Dog(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
这里我们使用了interface
来定义Animal
类型,Dog
类实现了该接口并定义了一个age
字段。
我们可以使用以下代码将Dog
对象序列化和反序列化:
import java.io.*;
public class SerializationNonSerializableDemo {
public static void main(String[] args) {
// 序列化继承自非实现Serializable接口的类的对象
Dog dog = new Dog(2);
try {
FileOutputStream fileOut = new FileOutputStream("dog.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(dog);
out.close();
fileOut.close();
System.out.println("Serialized data is saved as dog.ser");
} catch (IOException i) {
i.printStackTrace();
}
// 反序列化继承自非实现Serializable接口的类的对象
Dog deserializedDog = null;
try {
FileInputStream fileIn = new FileInputStream("dog.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
deserializedDog = (Dog) in.readObject();
in.close();
fileIn.close();
} catch (IOException | ClassNotFoundException i) {
i.printStackTrace();
}
System.out.println("Age: " + deserializedDog.getAge());
}
}
然而,你会发现在反序列化时会出现java.io.NotSerializableException
。这是因为Dog
类没有实现Serializable
接口。
为了解决这个问题,我们可以让Dog
类继承Serializable
接口:
class Dog implements Animal, Serializable {
int age;
public Dog(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
现在,你再运行上述代码,你会发现输出是:Age: 2
。
总结
在Java中,序列化是一种非常重要的技术,因为它可以将对象转换为字节序列以便持久化、交换数据或进行远程调用。在使用序列化时要注意一些问题,比如如何序列化transient字段、如何序列化继承自非实现Serializable接口的类的对象等,但是我们可以通过一些技巧来解决这些问题。在实际开发中,应用程序需要考虑如何正确地使用序列化来确保程序的正确性和稳定性。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中对象的序列化详解及实例 - Python技术站