1. Overview
1.概述
In this quick tutorial, we’ll demonstrate how to validate a Serializable object in Java.
在这个快速教程中,我们将演示如何在Java中验证一个可序列化的对象。
2. Serialization and Deserialization
2.序列化和反序列化
Serialization is the process of converting the state of an object into a byte stream. Serialized objects are primarily used in Hibernate, RMI, JPA, EJB, and JMS technologies.
序列化是将对象的状态转换为字节流的过程。串行化对象主要用于Hibernate、RMI、JPA、EJB和JMS技术。
Switching directions, deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This process is often used to persist the object.
换个方向,反序列化是一个相反的过程,字节流被用来在内存中重新创建实际的Java对象。这个过程通常被用来持久化对象。
3. Serialization Validation
3.序列化验证
We can verify serialization using a variety of methods. Let’s take a look at a few.
我们可以用各种方法来验证序列化。让我们看一下其中的几种。
3.1. Validate implements Serialization
3.1.验证implements序列化
The simplest approach to determine whether an object is serializable is to check whether that object is an instance of java.io.Serializable or java.io.Externalizable. However, this method does not guarantee that we can serialize an object.
确定一个对象是否可序列化的最简单方法是:检查该对象是否是java.io.Serializable或java.io.Externalizable的实例。然而,这个方法并不能保证我们能够序列化一个对象。
Let’s say we have an Address object that doesn’t implement the Serializable interface:
假设我们有一个Address对象,它没有实现Serializable接口。
public class Address {
private int houseNumber;
//getters and setters
}
While attempting to serialize an Address object, a NotSerializableException might occur:
在尝试序列化一个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);
}
}
Now, let’s say we have a Person object that implements the Serializable interface:
现在,假设我们有一个Person对象,它实现了Serializable接口。
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private int age;
private String name;
// getters and setters
}
In this case, we’ll be able to serialize and deserialize to re-create the object back:
在这种情况下,我们就可以通过序列化和反序列化来重新创建对象了。
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
3.2.Apache Commons SerializationUtils
Another way to validate the serialization of an object is to utilize the serialize method from Apache Commons SerializationUtils. This method will not accept an object that is not serializable.
另一种验证对象序列化的方法是利用Apache Commons的serialize方法 SerializationUtils。这个方法不会接受一个不可序列化的对象。
What if we try to serialize the non-serializable Address object by explicitly type-casting to compile the code? At runtime, we’ll encounter a ClassCastException:
如果我们试图通过明确的类型转换来序列化不可序列化的Address对象来编译代码,会怎么样?在运行时,我们会遇到一个ClassCastException。
Address address = new Address();
address.setHouseNumber(10);
SerializationUtils.serialize((Serializable) address);
Let’s use the above to validate the serializable Person object:
让我们用上述方法来验证可序列化的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
3.3.Spring Core SerializationUtils
We’ll now look at the SerializationUtils method from spring-core, which is similar to the method from Apache Commons. This method also does not accept the non-serializable Address object.
我们现在来看看SerializationUtils方法,它来自spring-core,与Apache Commons的方法类似。这个方法也不接受不可序列化的Address对象。
Such code will throw a ClassCastException at runtime:
这样的代码将在运行时抛出一个ClassCastException。
Address address = new Address();
address.setHouseNumber(10);
org.springframework.util.SerializationUtils.serialize((Serializable) address);
Let’s try with the serializable Person object:
让我们用可序列化的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. Custom Serialization Utility
3.4.自定义序列化工具
As a third option, we’ll create our own custom utility to serialize or deserialize according to our requirements. To demonstrate this, we’ll write two separate methods for serialization and deserialization.
作为第三种选择,我们将创建自己的自定义工具,根据我们的要求进行序列化或反序列化。为了证明这一点,我们将为序列化和反序列化编写两个独立的方法。
The first is an example of object validation for the serialization process:
第一个是序列化过程的对象验证的例子。
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();
}
We’ll also write a method to perform the deserialization process:
我们还将写一个方法来执行反序列化过程。
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);
}
Additionally, we can create a utility method that takes Class as a parameter and returns true if the object is serializable. This method would assume that the primitives and interfaces are implicitly serializable while validating if the input class can be assigned to Serializable or not. Also, we’re excluding transient and static fields during the validation process.
此外,我们可以创建一个实用方法,该方法以Class为参数,如果对象是可序列化的,则返回true。这个方法将假设基元和接口是隐式可序列化的,同时验证输入的类是否可以被分配到Serializable。另外,在验证过程中,我们要排除transient和static字段。
Let’s implement this method:
让我们来实现这个方法。
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;
}
Let’s now validate our utility method:
现在我们来验证一下我们的实用方法。
assertFalse(MySerializationUtils.isSerializable(Address.class));
assertTrue(MySerializationUtils.isSerializable(Person.class));
assertTrue(MySerializationUtils.isSerializable(Integer.class));
4. Conclusion
4.总结
In this article, we looked at several ways to determine whether an object is serializable or not. We’ve also demonstrated a custom implementation to accomplish the same.
在这篇文章中,我们看了几种确定一个对象是否可序列化的方法。我们还演示了一个自定义的实现来完成同样的工作。
As is the custom, all the code samples used in this tutorial are available over on GitHub.
按照惯例,本教程中使用的所有代码样本都可以在GitHub上找到。