JAVA序列化/反序列化与单例

97 阅读3分钟

单例设计类:

package com.test.singleton;  
  
import java.io.IOException;  
import java.io.ObjectStreamException;  
import java.io.Serializable;  
  
  
public class SingleTon implements Serializable{  
  
    /** 
     *  
     */  
    private static final long serialVersionUID = 768457893059530646L;  
  
    private SingleTon(){  
        System.out.println("here...");  
        //避免反射机制,导致的多例问题,通过反射机制仍然可以对私有构造函数进行操作  
        if(instance != null){  
            return;  
        }  
    }  
      
    private static final SingleTon instance = new SingleTon();  
      
    public static SingleTon getInstance(){  
        return instance;  
    }  
      
    public void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{  
        //  
    }  
    /** 
     * 严格单例,确保remote instance不会干扰单例模式,避免在发序列化过程中对单例的影响. 
     * @return 
     * @throws ObjectStreamException 
     */  
    public Object readResolve() throws ObjectStreamException{  
        return instance;  
    }  
} 

 

///流方式:  
package com.test.singleton;  
  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
  
public class SerializeCheckMain {  
  
    /** 
    * @param args 
    */  
    public static void main(String[] args) throws Exception{  
        // TODO Auto-generated method stub  
        SingleTon s1 = SingleTon.getInstance();  
        File file = new File("D:\\singleTome.txt");  
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));  
        oos.writeObject(s1);  
        oos.close();  
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));  
        SingleTon s2 = (SingleTon)ois.readObject();  
        System.out.println("HashCode 1 : " + s1.hashCode());  
        System.out.println("HashCode 2 : " + s2.hashCode());  
        System.out.println("Equal : " + (s1 == s2));//打破单例  
    }  
  
}

  

//反射方式:  
package com.test.singleton;  
  
import java.lang.reflect.Constructor;  
  
/** 
 * @author liuguanqing 
 *  反射机制,测试单例 
 */  
public class RefletCheckMain {  
  
    /** 
    * @param args 
    */  
    public static void main(String[] args) throws Exception{  
        SingleTon s1 = SingleTon.getInstance();  
        Constructor[] constructors = SingleTon.class.getDeclaredConstructors();  
        Constructor<SingleTon> c = constructors[0];  
        c.setAccessible(true);  
        SingleTon s2 = c.newInstance(null);  
        System.out.println("HashCode 1 : " + s1.hashCode());  
        System.out.println("HashCode 2 : " + s2.hashCode());  
        System.out.println("Equal : " + (s1 == s2));//打破单例  
    }  
  
}

  

 

注意,序列化和反序列化,是java使用字节码技术生成对象,将不会执行构造器方法.

注意到在writeReplace和readResolve,我们可以严格控制singleton的对象,在同一个JVM中完完全全只有唯一的对象,控制不让singleton对象产生副本.

package com.test.main;  
  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
import java.io.ObjectStreamException;  
import java.io.Serializable;  
  
public class TestSerializable implements Serializable{  
  
    /** 
     *  
     */  
    private static final long serialVersionUID = -1740221592194879964L;  
  
    private Integer age;  
      
    private String name;  
      
    public TestSerializable(){  
        System.out.println("Contrustor..");  
    }  
      
    public Integer getAge() {  
        return age;  
    }  
  
  
    public void setAge(Integer age) {  
        this.age = age;  
    }  
  
  
    public String getName() {  
        return name;  
    }  
  
  
    public void setName(String name) {  
        this.name = name;  
    }  
      
    private void writeObject(java.io.ObjectOutputStream out) throws IOException{  
        System.out.println("writeObject");  
        out.writeInt(this.age);  
        out.writeInt(this.name.getBytes("utf8").length);  
        out.write(this.name.getBytes("utf8"));  
    }  
      
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{  
        this.age = in.readInt();  
        int chars = in.readInt();  
        byte[] bytes = new byte[chars];  
        in.readFully(bytes);  
        this.name = new String(bytes,"utf8");  
        System.out.println("readObject");  
    }  
      
    public Object readResolve() throws ObjectStreamException{  
        System.out.println("readResolve");  
        return this;  
    }  
      
    public Object writeReplace() throws ObjectStreamException{  
        System.out.println("writeReplace");  
        return this;  
    }  
  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args) throws Exception{  
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\sss.txt"));  
        TestSerializable ss = new TestSerializable();  
        ss.setAge(12);  
        ss.setName("zhangsan");  
        oos.writeObject(ss);  
        oos.close();  
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\sss.txt"));  
        TestSerializable ss2 = (TestSerializable)ois.readObject();  
        System.out.println(ss == ss2);  
        System.out.println(ss2.getName());  
        ois.close();  
        //执行结果  
        //Contrustor..  
        //writeReplace  
        //writeObject  
        //readObject  
        //readResolve  
        //false  
        //zhangsan  
          
    }  
  
} 

 

//Externalizable接口是Serializable的子接口,不过此接口提供了两个特殊的扩展方法:  
    @Override  
    public void writeExternal(ObjectOutput out) throws IOException {  
        System.out.println("writeEx");  
        out.writeInt(50);  
          
    }  
  
    @Override  
    public void readExternal(ObjectInput in) throws IOException,  
            ClassNotFoundException {  
        System.out.println("readEx");  
        this.age = in.readInt();  
    } 

 

注意实现Externalizable接口的类,在发序列化时,将会执行构造函数,因为对于流操作而言,此对象是有明确类型的(Serializable接口是个标记接口).

而且,如果实现了writeExternal和readExternal,将不会在执行readObject和writeObject,因为此时这两个方法已经被"擦除".

 

对于java序列化和反序列化,被序列化的类中有关于serialVersionUID会带来一些问题;

1) 如果你调整了Class结构(比如新增/去除某个属性值,但是不能引入编译错误),但没有修改serialVersionUID;那么在反序列化和序列化时不会带来异常,只是可能导致部分属性在get时为null.

2) 如果你调整了Class结构,同时也修改了serialVersionUID;如果序列化和反序列双方没有保持uid一致的话,将会直接导致反序列化异常.(java.io.InvalidClassException)

3) 如果你没有显式的声明serialVersionUID,那么对于JVM而言,不同的class类结构(属性列表和方法列表)将会得出不同的uid;因此如果序列化双方不能保持一致的uid,仍然会带来问题.

 

本文转载自http://shift-alt-ctrl.iteye.com/blog/1842040