1. 概述
在这个快速教程中,我们将展示如何在Java中验证可序列化对象。
2. 序列化与反序列化
序列化是指将一个对象的状态转换为字节流的过程。序列化后的对象主要在Hibernate、RMI、JPA、EJB和JMS等技术中使用。相反,反序列化是将字节流用于在内存中重建实际的Java对象的过程,通常用于持久化对象。
3. 序列化验证
我们可以使用多种方法来验证序列化。让我们来看看一些方法。
3.1. 通过implements验证序列化
检查一个对象是否是java.io.Serializable
或java.io.Externalizable
接口的实例,是最简单的确定对象是否可序列化的做法。然而,这种方法并不能保证我们能成功序列化一个对象。
假设我们有一个不实现Serializable
接口的Address
对象:
public class Address {
private int houseNumber;
//getters and setters
}
当我们尝试序列化Address
对象时,可能会出现NotSerializableException
:
@Test(expected = NotSerializableException.class)
public void whenSerializing_ThenThrowsError() throws IOException {
Address address = new Address();
address.setHouseNumber(10);
FileOutputStream fileOutputStream = new FileOutputStream("yofile.txt");
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {
objectOutputStream.writeObject(address);
}
}
现在,假设我们有一个实现了Serializable
接口的Person
对象:
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private int age;
private String name;
// getters and setters
}
在这种情况下,我们可以序列化并反序列化以重新创建对象:
Person p = new Person();
p.setAge(20);
p.setName("Joe");
FileOutputStream fileOutputStream = new FileOutputStream("yofile.txt");
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {
objectOutputStream.writeObject(p);
}
FileInputStream fileInputStream = new FileInputStream("yofile.txt");
try ( ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
Person p2 = (Person) objectInputStream.readObject();
assertEquals(p2.getAge(), p.getAge());
assertEquals(p2.getName(), p.getName());;
}
3.2. Apache Commons SerializationUtils
另一种验证对象序列化的办法是使用Apache Commons库中的SerializationUtils
类的serialize
方法。这个方法不会接受不可序列化的对象。
如果我们试图通过显式类型转换来序列化非序列化的Address
对象以编译代码,运行时会遇到ClassCastException
:
Address address = new Address();
address.setHouseNumber(10);
SerializationUtils.serialize((Serializable) address);
让我们用这个方法来验证可序列化的Person
对象:
Person p = new Person();
p.setAge(20);
p.setName("Joe");
byte[] serialize = SerializationUtils.serialize(p);
Person p2 = (Person)SerializationUtils.deserialize(serialize);
assertEquals(p2.getAge(), p.getAge());
assertEquals(p2.getName(), p.getName());
3.3. Spring Core SerializationUtils
接下来,我们将看看Spring Core库中的SerializationUtils
方法(https://www.javadoc.io/doc/org.springframework/spring-core/5.0.8.RELEASE/org/springframework/util/SerializationUtils.html),它与Apache Commons的方法类似。这个方法也不接受不可序列化的Address
对象。
这样的代码在运行时会抛出ClassCastException
:
Address address = new Address();
address.setHouseNumber(10);
org.springframework.util.SerializationUtils.serialize((Serializable) address);
让我们用可序列化的Person
对象试一试:
Person p = new Person();
p.setAge(20);
p.setName("Joe");
byte[] serialize = org.springframework.util.SerializationUtils.serialize(p);
Person p2 = (Person)org.springframework.util.SerializationUtils.deserialize(serialize);
assertEquals(p2.getAge(), p.getAge());
assertEquals(p2.getName(), p.getName());
3.4. 自定义序列化工具
作为第三个选项,我们将创建自己的自定义工具,根据需要进行序列化或反序列化。为了演示这一点,我们将编写两个独立的序列化和反序列化方法。
首先,这是序列化过程中的对象验证示例:
public static byte[] serialize(T obj) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
return baos.toByteArray();
}
我们还会编写一个执行反序列化过程的方法:
public static T deserialize(byte[] b, Class cl) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(b);
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
return cl.cast(o);
}
此外,我们还可以创建一个工具方法,它接受一个Class
参数,如果对象可序列化则返回true
。这个方法会假设基本类型和接口默认可序列化,并验证输入类是否可以赋值给Serializable
,同时在验证过程中排除transient
和static
字段。
让我们实现这个方法:
public static boolean isSerializable(Class<?> it) {
boolean serializable = it.isPrimitive() || it.isInterface() || Serializable.class.isAssignableFrom(it);
if (!serializable) {
return false;
}
Field[] declaredFields = it.getDeclaredFields();
for (Field field : declaredFields) {
if (Modifier.isVolatile(field.getModifiers()) || Modifier.isTransient(field.getModifiers()) ||
Modifier.isStatic(field.getModifiers())) {
continue;
}
Class<?> fieldType = field.getType();
if (!isSerializable(fieldType)) {
return false;
}
}
return true;
}
现在,让我们验证我们的工具方法:
assertFalse(MySerializationUtils.isSerializable(Address.class));
assertTrue(MySerializationUtils.isSerializable(Person.class));
assertTrue(MySerializationUtils.isSerializable(Integer.class));
4. 总结
在这篇文章中,我们探讨了多种判断对象是否可序列化的途径。我们也展示了如何实现一个自定义方法来完成同样的任务。
如往常一样,本教程的所有代码示例都可以在GitHub上找到:https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-serialization。