• Java对象序列化的使用和定制


    序列化的概念及使用场合

    序列化就是把对象转化为字节序列并持久化保存,可以保存在内存中、磁盘文件系统,甚至通过网络传递,并能够在以后将这个字节序列完全恢复为原来的对象。

    对象序列化的概念引入Java是为了支持两种主要特性

    • 一是为了远程调用(Remote Method Invocation,RMI),它使存活于其他计算机上的对象使用起来就像是存活于本机上一样。当想远程对象发送消息时,需要通过对象序列化来传输参数和返回值。
    • 二是对于Java Beans来说,对象的序列化也是必须的。使用一个Bean时,一般情况下是在设计阶段对它的状态信息进行配置,而这种状态信息必须保存下来,并在程序启动时进行后期恢复。

    序列化操作步骤:

    1. 要使得一个对象可以被序列化,首先要实现实现接口Serializable(该接口仅是一个标记接口,不包括任何方法);
    2. 使用ObjectOutputStream包装一个OutputStream对象,在调用ObjectOutputStream的writeObject()来序列化;
    3. 使用ObjectInputStream包装一个InputStream对象,在调用ObjectInputStream的readObject()来反序列化;

    因为序列化最终是字节序列,所以必须字节流,而不能使用字符流(关于字节流和字符流请参考这里)。

    对象序列化特别“聪明”的一个地方是它不仅保存了对象的“全景图”,而且能追踪对象内包含的所有引用并保存那些对象;接着又能对每个这样的对象内包含的引用进行追踪;以此类推。这种情况有时被称为“对象网”,单个对象可与之建立连接,而且它还包含了对象的引用数组以和成员对象。如果必须保持一套自己的对象序列化机制,那么维护那些可追踪到所有链接的代码可能会显得非常麻烦。然而,由于 Java的对象序列化似乎找不出什么缺点,所以请尽量不要自己动手,让它用优化的算法自动维护整个对象网。

    序列化Demo

    public class App 
    {
        public static void main( String[] args ) throws Exception, IOException
        {
        	User user=new User();
        	user.setName("net.oseye");
        	user.setAge(18);;
        	
        	//序列化
            //使用ObjectOutputStream包装OutputStream
        	ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("out.txt"));
        	out.writeObject("下面是序列化的User:");
        	out.writeObject(user);
        	out.close();
        	
        	//反序列化
        	//
        	ObjectInputStream in=new ObjectInputStream(new FileInputStream("out.txt"));
        	System.out.println(in.readObject());
        	//查看反序列化后的对象类型,方便反射
        	Object obj=in.readObject();
        	System.out.println(obj.getClass());
        	
        	user=(User)obj;
        	System.out.println(user);
        	System.out.println("user的name:"+user.getName());
        	System.out.println("user的age:"+user.getAge());
        	in.close();
        }
    }
    
    //实现标记接口Serializable
    @SuppressWarnings("serial")
    class User implements Serializable{	
    	private String name;
    	public String getName(){
    		return name;
    	}
    	public void setName(String name){
    		this.name=name;
    	}
    	
    	private int age;
    	public int getAge(){
    		return age;
    	}
    	public void setAge(int age){
    		this.age=age;
    	}
    }

    输出

    下面是序列化的User:
    class net.oseye.SerializationDemo.User
    net.oseye.SerializationDemo.User@9980d5
    user的name:net.oseye
    user的age:18

    如果我们不想序列化某个字段可以使用transient(瞬时)关键字来修饰字段,如:

    public class App 
    {
        public static void main( String[] args ) throws Exception, IOException
        {
    	//如前面示例代码....
        }
    }
    
    //实现标记接口Serializable
    class User implements Serializable{	
    	private String name;
    	public String getName(){
    		return name;
    	}
    	public void setName(String name){
    		this.name=name;
    	}
    	
    	private transient int age;
    	public int getAge(){
    		return age;
    	}
    	public void setAge(int age){
    		this.age=age;
    	}
    }

    这样就不会序列化age字段了。

    使用Externalizable定制序列化

    使用transient关键字可以简单定制序列化,但如果更复杂的定制可以使用Externalizable接口代替标记接口Serializable,因为Externalizable有序列化和反序列化的方法

    public class App 
    {
        public static void main( String[] args ) throws Exception, IOException
        {
    	//如前面示例代码....
        }
    }
    
    //实现标记接口Externalizable
    class User implements Externalizable{
    	public User(){}	
    	private String name;
    	public String getName(){
    		return name;
    	}
    	public void setName(String name){
    		this.name=name;
    	}
    	
    	private transient int age;
    	public int getAge(){
    		return age;
    	}
    	public void setAge(int age){
    		this.age=age;
    	}
    	
    	public void writeExternal(ObjectOutput out) throws IOException {
    		this.name="已经被序列化了:"+this.name;
    		out.writeObject(this.name);
    	}
    	public void readExternal(ObjectInput in) throws IOException,
    			ClassNotFoundException {
    		this.name=(String)in.readObject();
    	}
    }

    输出

    下面是序列化的User:
    class net.oseye.SerializationDemo.User
    net.oseye.SerializationDemo.User@ff2413
    user的name:已经被序列化了:net.oseye
    user的age:0

    问题:我发现使用Externalizable接口实现的类必须显示声明构造函数,如上例不显示声明

    public User(){}

    将会报异常:

    Exception in thread "main" java.io.InvalidClassException: net.oseye.SerializationDemo.User; no valid constructor

    至于原因有待查明....,如果你知道也请告诉我,谢谢。

    使用Serializable定制序列化

    其实如果你不是特别坚持使用Externalizable接口的话,使用Serializable接口也可以实现定制,但却使用迷惑,因为需要添加

    private void writeObject(ObjectOutputStream out) throws IOException;
    private void readObject(ObjectInputStream in) throws IOException,
    			ClassNotFoundException;

    这两个方法,如果你细心你会发现和Externalizable接口的中的两个方法很像。迷惑的地方是Serializable是标记接口,而我们感觉无端增加了两个私有方法!!

    一旦在接口Serializable的实现类中添加了上面两个方法,序列化和反序列化就会自动调用:

    //实现标记接口Serializable
    @SuppressWarnings("serial")
    class User implements Serializable{	
    	private String name;
    	public String getName(){
    		return name;
    	}
    	public void setName(String name){
    		this.name=name;
    	}
    	
    	private int age;
    	public int getAge(){
    		return age;
    	}
    	public void setAge(int age){
    		this.age=age;
    	}
    	
    	private void writeObject(ObjectOutputStream out) throws IOException {
    		this.name="已经被序列化了:"+this.name;
    		out.writeObject(this.name);
    	}
    	private void readObject(ObjectInputStream in) throws IOException,
    	ClassNotFoundException {
    		this.name=(String)in.readObject();
    	}
    }
    出处:http://www.zhaiqianfeng.com    
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    字符输入输出
    每日一例
    每日一例
    结构
    指针数组的初始化
    装箱,拆箱,正则表达式
    数据类型的转换
    怎样让程序不断执行
    SQL练习1关于插入删除,修改,单表查询
    SQLSERVER 总结1
  • 原文地址:https://www.cnblogs.com/zhaiqianfeng/p/4620338.html
Copyright © 2020-2023  润新知