要使一个对象可以序列化,必须实现Serializable或者Externalizable标记接口
使用对象流(ObjectInputStream/ObjectOutputStream)可以读写序列化对象
一.序列化对象
普通java对象的序列化
先定义一个普通的可序列化对象
public class Person implements Serializable{
private int age;
private String name;
private int weight;
private int height;
private int[] intArray=new int[]{1,2,3,4,5};
private String[] strArray=new String[]{"a","b","c"};
static ArrayList<String> list=new ArrayList<String>();
static {
list.add("beijing");
list.add("wuhan");
}
//省略get和set方法
}
序列化实现
/**
* 将对象序列化后保存到文件
*/
public static void object2File(){
Person p=new Person();
p.setAge(10);
p.setHeight(120);
p.setName("zhang");
p.setWeight(90);
try {
ObjectOutputStream objOut=new ObjectOutputStream(new FileOutputStream("person.txt"));
objOut.writeObject(p);
objOut.close();
System.out.println("对象已保存");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 从文件中加载序列化对象
*/
public static void file2Object(){
Person p=null;
try {
ObjectInputStream objIn=new ObjectInputStream(new FileInputStream("person.txt"));
p=(Person)objIn.readObject();
System.out.println("从文件中读取对象Person:");
System.out.println(p.getAge()+" "+p.getHeight()+" "+p.getName()+" "+p.getWeight());
//验证数组和基本数据集合的序列化问题,结果:可行
System.out.println(Arrays.toString(p.getIntArray()));
System.out.println(Arrays.toString(p.getStrArray()));
System.out.println(Arrays.toString(p.getList().toArray()));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
问题:对象引用的序列化,有对象A,B,C,A和B都含有C的引用,我们依次序列化A,B,C.会产生多少个对象?
实例验证:
Test_A 引用Test_C
public class Test_C implements java.io.Serializable{
}
class Test_A implements java.io.Serializable{
Test_C c;
public Test_A(Test_C c){
this.c=c;
}
public Test_C getC() {
return c;
}
public void setC(Test_C c) {
this.c = c;
}
}
查看比较:
/**
* 含引用对象的序列化问题
*/
public static void test2(){
Test_C c=new Test_C();
Test_A a=new Test_A(c);
Test_A b=new Test_A(c);
//写入
try {
ObjectOutputStream objOut=new ObjectOutputStream(new FileOutputStream("Test_C.txt"));
objOut.writeObject(c);
objOut.writeObject(a);
objOut.writeObject(b);
objOut.close();
System.out.println("对象已保存");
} catch (Exception e) {
e.printStackTrace();
}
Test_C c1=null;
Test_A a1=null;
Test_A b1=null;
//读取
try {
ObjectInputStream objIn=new ObjectInputStream(new FileInputStream("Test_C.txt"));
c1=(Test_C)objIn.readObject();
a1=(Test_A)objIn.readObject();
b1=(Test_A)objIn.readObject();
//读取的对象是新建的
System.out.println(c==c1);
System.out.println(a==a1);
System.out.println(b==b1);
/*对象的引用是不变的,也就是说只有一个对象Test_C
*
*/
System.out.println(c1==a1.getC());
System.out.println(c1==b1.getC());
System.out.println(a1.getC()==b1.getC());
} catch (Exception e) {
e.printStackTrace();
}
}
说明,Java的序列化机制:
虚拟机对每一个对象维护一个序列化编号,当首次序列化对象时会创建一个序列化编号,当多次序列化同一个对象时,只是引用一个序列化编号.注意当原来对象的值改变后,再次序列化的值也不会改变.
二.自定义序列化
/**
* 自定义序列化,
* 通过覆盖下列方法实现自定义序列化
*
* 1.private void writeObject(java.io.ObjectOutputStream out) throws IOException;
* 2.private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException ;
* 3.private void readObjectNoData() throws ObjectStreamException;
* 4.private Object writeReplace() throws ObjectStreamException ;
*
* 1,2要同时使用才能正确解析.3,4是可选的;
* 通过验证得知:
* 对象在序列化时会首先调用对象本身的方法writeReplace(),该方法可以将对象替换成另一个对象B,然后再序列化B.
* 之后再调用 writeObject()方法(如果B中有的话).
* 读取序列化对象的时候会先调用对象本身的readObject()方法.
* 注意:writeObject()与readObject()具体步骤应该是相对应的,否则无法正确读取对象;
*/
public class CustomSerializable {
public static void main(String[] args) {
object2File(new Color(),"color.txt");
}
public static void object2File(Object obj, String filename) {
try {
ObjectOutputStream objOut = new ObjectOutputStream(
new FileOutputStream(filename));
objOut.writeObject(obj);
objOut.close();
System.out.println("对象已保存");
ObjectInputStream objIn=new ObjectInputStream(new FileInputStream("color.txt"));
Color c=(Color)objIn.readObject();
System.out.println(c.getValue());
// int n=(Integer)objIn.readObject();
// System.out.println(n);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Color implements java.io.Serializable {
private int value=10;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
System.out.println("1调用方法writeObject()");
out.writeInt(22);
}
private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException {
System.out.println("1调用方法readObject()");
value=in.readInt();
}
private void readObjectNoData() throws ObjectStreamException {
System.out.println("1调用方法readObjectNoData()");
}
private Object writeReplace() throws ObjectStreamException {
System.out.println("1调用方法writeReplace()");
return new Color();
// return new Integer(2);
}
}
第二种自定义序列化(完全定制)
/**
* 自定义序列化的另一种方式是实现java.io.Externalizable
* 注意,必须显式提供无参的构造方法.
*/
public class CustomSerializable2 {
public static void main(String[] args) {
Rect r=new Rect();
try {
ObjectOutputStream objOut = new ObjectOutputStream(
new FileOutputStream("rect.txt"));
objOut.writeObject(r);
objOut.close();
System.out.println("对象已保存");
ObjectInputStream objIn=new ObjectInputStream(new FileInputStream("rect.txt"));
Rect r2=(Rect)objIn.readObject();
System.out.println(r2.getA()+" "+r2.getB());
objIn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Rect implements java.io.Externalizable{
private int a=1;
private int b=2;
public Rect(){}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.a=in.readInt();
this.b=in.readInt();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(a);
out.writeInt(b);
}
}
三.序列化版本问题
java在反序列化对象时会检查序列化版本,类的修改会导致反序列化失败,为了避免这种情况通常在类中设置序列化ID
private static fianl long serialVersionID=(value)