序列化:将对象的状态信息转换为可以存储或传输的形式的过程,在序列化期间,对象将其当前状态写入到临时或者持久性的存储区。
反序列化:通过从存储区读取或者反序列化对象,重新创建该对象。
1:对象如何序列化
首先创建一个序列化的类
class Person implements Serializable { private String name; private Integer age; /** * 1表示男 2表示女 */ private Integer sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer getSex() { return sex; } public void setSex(Integer sex) { this.sex = sex; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age='" + age + '\'' + ", sex=" + sex + '}'; }}
然后创建一个序列化的方法,如下:
public static void serialize() throws IOException { Person person = new Person(); person.setName("nick"); person.setAge(22); person.setSex(1); ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("person.txt"))); objectOutputStream.writeObject(person); objectOutputStream.close(); System.out.println("序列化完成!"); System.out.println("=============================================="); }
执行后的结果如下:
序列化完成!==============================================
然后再反序列化,执行以下的反序列化方法
public static void deserialize() throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("person.txt"))); Person person = (Person) objectInputStream.readObject(); objectInputStream.close(); System.out.println("反序列化结果如下:"); System.out.println(person); }
反序列化得到的结果如下:
反序列化结果如下:Person{name='nick', age='22', sex=1}
可以看到通过上面的方法能够进行正确的序列化和反序列化。
2:Serializable接口有何用?
上面在定义Person类时,实现了一个Serializable接口,然而当点进Serializable接口内部查看,发现它是一个空接口!
* If a serializable class does not explicitly declare a serialVersionUID, then * the serialization runtime will calculate a default serialVersionUID value * for that class based on various aspects of the class, as described in the * Java(TM) Object Serialization Specification. However, it is <em>strongly * recommended</em> that all serializable classes explicitly declare * serialVersionUID values, since the default serialVersionUID computation is * highly sensitive to class details that may vary depending on compiler * implementations, and can thus result in unexpected * <code>InvalidClassException</code>s during deserialization. Therefore, to * guarantee a consistent serialVersionUID value across different java compiler * implementations, a serializable class must declare an explicit * serialVersionUID value. It is also strongly advised that explicit * serialVersionUID declarations use the <code>private</code> modifier where * possible, since such declarations apply only to the immediately declaring * class--serialVersionUID fields are not useful as inherited members. Array * classes cannot declare an explicit serialVersionUID, so they always have * the default computed value, but the requirement for matching * serialVersionUID values is waived for array classes. * * @author unascribed * @see java.io.ObjectOutputStream * @see java.io.ObjectInputStream * @see java.io.ObjectOutput * @see java.io.ObjectInput * @see java.io.Externalizable * @since JDK1.1 */public interface Serializable {}
但是如果不实现Serializable接口,又会报错。
Exception in thread "main" java.io.NotSerializableException: com.serial.Person at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at com.serial.Serialize.serialize(Serialize.java:29) at com.serial.Serialize.main(Serialize.java:18)
那为啥会报这个错呢?
通过跟踪代码可以看到如下代码
// remaining cases
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else { if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString()
); } else {
throw new NotSerializableException(cl.getName());
} }
从上面的代码可以看到假如一个对象既不是字符串、数组、枚举,同时也没有实现Serializable接口的话,在序列化的时候就会抛出NotSerializableException异常!
3:serialVersionUID的作用
经常可以看到如下的定义
private static final long serialVersionUID = -4392638638228609589L;
它有以下两个作用
-
serialVersionUID是是否可以序列化的唯一标识符。serialVersionUID序列化ID,可以看成是序列化和反序列化过程中的“暗号”,在反序列化时,JVM会把字节流中的序列号ID和被序列化类中的序列号ID做比对,只有两者一致,才能重新反序列化,否则就会报异常来终止反序列化的过程。
-
如果没有显式定义serialVersionUID,编译器会为它自动生成一个。 如果在定义一个可序列化的类时,没有人为显式地给它定义一个serialVersionUID的话,则Java运行时环境会根据该类的各方面信息自动地为它生成一个默认的serialVersionUID,一旦像上面一样更改了类的结构或者信息,则类的serialVersionUID也会跟着变化!
所以,为了serialVersionUID的确定性,凡是implements Serializable的类,最好人为显式地声明一个serialVersionUID值!
4:还有两种特殊情况
-
凡是被static修饰的字段是不会被序列化的,因为序列化保存的是对象的状态而static是类的状态,所以会忽略static静态域是理所应当的。
-
凡是被transient修饰符修饰的字段也是不会被序列化的,大家可以去了解一下transient修饰符的作用。如果在序列化某个类的对象时,就是不希望某个字段被序列化(比如密码),那这时就可以用transient修饰符来修饰该字段。
我们是一个技术分享平台,欢迎大家积极邮件投稿(请附带银行卡号和姓名)。为了激励大家的积极性,投稿发布后产生的广告收入,会全部通过转账的方式回馈给大家,大家也可以把文章分享到朋友圈,这样可以获得更多的广告收入。请关注《技术派青年》微信号