Serializable
ObjectOutputStream(序列化)ObjectInputStream(反序列化)
serialVersionUID
字段不参与序列化
Externalizable
序列化的应用
ObjectOutputStream(序列化)ObjectInputStream(反序列化)
package com.datang.netty.serializabletest;
import java.io.Serializable;
public class Amimal{
private String hobby;
private String eat;
private int age;
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getEat() {
return eat;
}
public void setEat(String eat) {
this.eat = eat;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
}
}
package com.datang.netty.serializabletest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Run {
public static void main(String[] args)throws Exception {
Amimal amimal = new Amimal();
amimal.setAge(11);
amimal.setEat("鸡蛋");
amimal.setHobby("打游戏");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\bykj\\Desktop\\obj.txt"));
oos.writeObject(amimal);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\bykj\\Desktop\\obj.txt"));
Amimal a = (Amimal)ois.readObject();
System.out.println(a);
}
}
ObjectOutputStream将对象写入到本地文件,ObjectInputStream从本地文件中获取一个对象。这一对方法是需要实现Serializable接口的。以上代码中Animal并未实现Serializable所以会抛出一个java.io.NotSerializableException: com.datang.netty.serializabletest.Amimal异常。
Animal实现Aerializable后执行结果如下
Amimal [hobby=打游戏, eat=鸡蛋, age=11]
查看源码可以发现如果被写入本地的对象类型不是String,不是数组,不是Enum,也不是Serializable便会抛出异常。
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
boolean oldMode = bout.setBlockDataMode(false);
depth++;
try {
// handle previously written and non-replaceable objects
int h;
if ((obj = subs.lookup(obj)) == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
}
// check for replacement object
Object orig = obj;
Class<?> cl = obj.getClass();
ObjectStreamClass desc;
for (;;) {
// REMIND: skip this check for strings/arrays?
Class<?> repCl;
desc = ObjectStreamClass.lookup(cl, true);
if (!desc.hasWriteReplaceMethod() ||
(obj = desc.invokeWriteReplace(obj)) == null ||
(repCl = obj.getClass()) == cl)
{
break;
}
cl = repCl;
}
if (enableReplace) {
Object rep = replaceObject(obj);
if (rep != obj && rep != null) {
cl = rep.getClass();
desc = ObjectStreamClass.lookup(cl, true);
}
obj = rep;
}
// if object replaced, run through original checks a second time
if (obj != orig) {
subs.assign(orig, obj);
if (obj == null) {
writeNull();
return;
} else if (!unshared && (h = handles.lookup(obj)) != -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
}
}
// 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());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}
serialVersionUID
在Eclipse编辑器或IDEA编辑器时实现Serializable接口编辑器会提示我们做额外的操作。
先看下有几种处理方案。
1 private static final long serialVersionUID = 1L;
2 private static final long serialVersionUID = 7643364732831133867L;
3 @SuppressWarnings("serial")
4 忽略它的提示,什么都不做。
第四种方案忽略提示什么都不做。执行Run后执行Run2此时结果是正确的。
package com.datang.netty.serializabletest;
import java.io.Serializable;
public class Amimal implements Serializable{
private String hobby;
private String eat;
private int age;
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getEat() {
return eat;
}
public void setEat(String eat) {
this.eat = eat;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
}
}
package com.datang.netty.serializabletest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Run {
public static void main(String[] args)throws Exception {
Amimal amimal = new Amimal();
amimal.setAge(11);
amimal.setEat("鸡蛋");
amimal.setHobby("打游戏");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\bykj\\Desktop\\obj.txt"));
oos.writeObject(amimal);
}
}
package com.datang.netty.serializabletest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Run2 {
public static void main(String[] args)throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\bykj\\Desktop\\obj.txt"));
Amimal a = (Amimal)ois.readObject();
System.out.println(a);
}
}
若此时我们修改了Animal类再次执行Run2就会抛出异常。异常原因为Class中的serialVersionUID 和和本地文件中的serialVersionUID不相同导致的。得出结论,就算我们不使用1,2,3编译器也会自动帮我们生成serialVersionUID。
package com.datang.netty.serializabletest;
import java.io.Serializable;
public class Amimal implements Serializable{
private String hobby;
private String eat;
private int age;
public void a() {}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getEat() {
return eat;
}
public void setEat(String eat) {
this.eat = eat;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
}
}
Exception in thread "main" java.io.InvalidClassException: com.datang.netty.serializabletest.Amimal; local class incompatible: stream classdesc serialVersionUID = 7643364732831133867, local class serialVersionUID = 6225763375310236200
第三种方案其实只是将警告给抑制了,只是让有强迫症的程序员看起来舒服点,并没有做其他操作。重复第四种方案的测试步骤得出相同的结果。
package com.datang.netty.serializabletest;
import java.io.Serializable;
@SuppressWarnings("serial")
public class Amimal implements Serializable{
private String hobby;
private String eat;
private int age;
public int a;
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getEat() {
return eat;
}
public void setEat(String eat) {
this.eat = eat;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
}
}
Exception in thread "main" java.io.InvalidClassException: com.datang.netty.serializabletest.Amimal; local class incompatible: stream classdesc serialVersionUID = 7643364732831133867, local class serialVersionUID = 5410859156642948805
第二种方案显式的将生成的serialVersionUID,在这种情况下可以在修改Animal类后再次执行Run2并不会抛出异常,新增加的属性为null。
package com.datang.netty.serializabletest;
import java.io.Serializable;
public class Amimal implements Serializable{
/**
*
*/
private static final long serialVersionUID = 7643364732831133867L;
private String hobby;
private String eat;
private int age;
private String name;
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getEat() {
return eat;
}
public void setEat(String eat) {
this.eat = eat;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + ", name=" + name + "]";
}
}
Amimal [hobby=打游戏, eat=鸡蛋, age=11, name=null]
第一种方案和第二种没有太大区别只是将根据类特征生成的serialVersionUID写成了固定值1L,但是需要注意3,4两个方案虽然可以修改源类,但并不能修改serialVersionUID值,不然还是会报错。
package com.datang.netty.serializabletest;
import java.io.Serializable;
public class Amimal implements Serializable{
/**
*
*/
private static final long serialVersionUID = 2L;
private String hobby;
private String eat;
private int age;
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getEat() {
return eat;
}
public void setEat(String eat) {
this.eat = eat;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
}
}
Exception in thread "main" java.io.InvalidClassException: com.datang.netty.serializabletest.Amimal; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
字段不参与序列化
若类中有些字段不需要参与序列号则可以使用以下三种方式排除
1 transient关键字
package com.datang.netty.serializabletest;
import java.io.Serializable;
public class Amimal implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String hobby;
private transient String eat;
private int age;
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getEat() {
return eat;
}
public void setEat(String eat) {
this.eat = eat;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
}
}
Amimal [hobby=打游戏, eat=null, age=11]
2 static关键字
package com.datang.netty.serializabletest;
import java.io.Serializable;
public class Amimal implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private static String hobby;
private transient String eat;
private int age;
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getEat() {
return eat;
}
public void setEat(String eat) {
this.eat = eat;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
}
}
Amimal [hobby=null, eat=null, age=11]
3 serialPersistentFields数组,这种方式指定哪些需要参与序列化。
package com.datang.netty.serializabletest; import java.io.ObjectStreamField; import java.io.Serializable; public class Amimal implements Serializable { /** * */ private static final long serialVersionUID = 1L; private String hobby; private String eat; private int age; private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("hobby", String.class), new ObjectStreamField("eat", String.class) }; public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } public String getEat() { return eat; } public void setEat(String eat) { this.eat = eat; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]"; } }
Amimal [hobby=打游戏, eat=鸡蛋, age=0]
Externalizable
externalizable实现了Serializable接口,所以我们需要被序列化的类实现该类也可以。
但是有两个必要条件
1 需要被序列号的类必须有空的构造函数
2 必须实现writeExternal和readExternal两个方法。在这两个方法内写需要被序列化的字段,需要注意的是write和read字段必须是有顺序的。
package com.datang.netty.serializabletest;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectStreamField;
import java.io.Serializable;
public class Amimal implements Externalizable {
private String hobby;
private String eat;
private int age;
public Amimal() {
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getEat() {
return eat;
}
public void setEat(String eat) {
this.eat = eat;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(hobby);
out.writeObject(eat);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
hobby = (String) in.readObject();
eat = (String) in.readObject();
age = in.readInt();
}
}
序列化的应用
序列化和反序列化在Java中的一个应用场景就是深拷贝,详情可以查看我的另一篇博客。
https://www.cnblogs.com/zumengjie/p/12198362.html