• 输入和输出--java序列化机制


    • 对象的序列化

    • 什么是Java对象的序列化?
    对象序列化的目标是将对象保存到磁盘上,或允许在网络中直接传输对象。对象序列化机制允许把内存中的Java对象转换成与平台无关的二进制流,从而保存或者传输。其他的程序一旦获得这种二进制流,还可以把这种二进制流恢复成原来的Java对象

    序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础。要养成良好的习惯,在写一个javabean的时候都要去实现Serializable接口。


    如果需要让某个对象支持序列化机制,则必须让的类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable;Externalizable。在实际开发中一般都是直接实现Serializable就好了,后面一种不怎么使用,至少我从来没有用过。
    • 关于这2者的区别:
    1:Serializable  系统自动存储必要信息,在使用的时候只需要实现接口,无需其他的代码支持,性能略差。
    2:Externalizable 程序员决定存储哪些信息,在使用的必须实现这个接口里面的2个空方法(一个读,一个写),性能略好。


    •   关于Java对象的序列化,要有一个值得理解的地方就是Java序列化机制的序列化算法:
      1,所有保存到磁盘中的对象都有一个序列化编号
      2,当程序试图去系列化一个对象时,首先会去检查这个对象是否已经被序列化了
      3,要是对象已经被序列化过了,那好,程序就直接输出一个序列化编号,而不是再次重新序列化该对象

      4,要是这个对象没有被序列化过,那好,程序会将这个对象转换成字节序列然后输出


    那么问题来了: 

    1,要是我连续2次调用了ObjectOutputStream的writeobject方法去序列化一个对象,然后在中间过程中我修改了这个对象的属性,那么这个修改操作会生效么?    不会生效,因为第2次序列化并没有实际发生,只是输出了一个序列号号而已。

        2,反序列化读取的是什么?是Java对象的数据还是Java类呢?  明显的:Java的反序列化读取的是Java对象的数据,所以我们在采用反序列化恢复Java数据时,一定要提供Java对象所属类的class文件,否则将引发ClassNotFoundException。
        3,反序列化机制在恢复Java对象时,无须调用构造器来初始化对象。如果一个类的构造器是private的,但是反序列化依然可以创建Java对象,如何解决这个问题呢?关于这点,其实我理解也不深刻。有一个解决方案就是:重写readResolve()方法。

        4,反序列化Java对象必须提供这个对象所属类的class文件,现在的问题是随着项目的升级,系统的class文件也会跟着改变,Java如何保证2个class文件的兼容性呢?   这个时候版本号派上用场了(private static final long serialVersionUID = 1L)。版本号用来标示Java类的序列号版本,不管一个类有没有升级和改变,只要他的版本号不变,序列化机制就会把他们当成同一个序列化版本。


        在这里严肃的批评下自己,每天在写Javabean,每次都觉得生成那个serialVersionUID太麻烦,就把myeclipse设置了下不提示这个版本号了,这个习惯不好,一定要改过来,另外的,在使用有引用的Java对象时,注意要把所有的类都去实现Serializable接口,这样子就会保证序列化和反序列化都正常。如果某个类的字段不是基本数据类型或 String  类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化。


    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    /**
     *
     * @version 1L
     * @author  LinkinPark 
     * @since   2014-12-31
     * @motto   梦似烟花心似水,同学少年不言情
     * @desc    ^ 注意这里序列化对象,也使用了IO流,所以最后不要忘记去关闭流
     */
    public class Linkin
    {
    
    	public static void main(String[] args) throws Exception
    	{
    		//创建一个ObjectOutputStream输出流,这是一个处理流,包装了一个普通的低级流
    		ObjectOutputStream oos = null;
    		//创建一个ObjectInputStream输入流,同样也是一个处理流,包装了一个普通的低级流
    		ObjectInputStream ois = null;
    		try
    		{
    			//第一次输出对象
    			oos = new ObjectOutputStream(new FileOutputStream("src/LinkinPark..."));
    			Person person = new Person();
    			person.setName("LinkinPark...");
    			person.setAge(25);
    			oos.writeObject(person);
    			//第2次输出对象,这里只是输出了一个序列化号
    			oos = new ObjectOutputStream(new FileOutputStream("src/LinkinPark1..."));
    			oos.writeObject(person);
    
    			ois = new ObjectInputStream(new FileInputStream("src/LinkinPark..."));
    			Person person1 = (Person) ois.readObject();
    			System.out.println("这里是反序列化之后的对象:" + person1.getName() + ";" + person1.getAge());
    		}
    		catch (Exception e)
    		{
    			e.printStackTrace();
    		}
    		finally
    		{
    			if (oos != null)
    			{
    				oos.close();
    			}
    			if (ois != null)
    			{
    				ois.close();
    			}
    		}
    	}
    }
    
    //定义一个类,只要实现Serializable就表示这个对象是可以序列化的,其实实现这个接口IDE会提示要初始化一个版本号的,只不过我自己设置过了不再提示了
    class Person implements Serializable
    {
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 1L;
    	private String name;
    	private Integer age;
    
    	public Person()
    	{
    		//下面这行代码不会执行的
    		System.out.println("反序列化机制在恢复Java对象时,无须调用构造器来初始化对象");
    	}
    	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;
    	}
    
    }


    • 自定义序列化
    在一些特殊的情况下,我们不希望某个类中的一些信息被序列化,这个时候就要自定义序列化了。

    有2种做法:

    1,这种比较简单的,就是在我们不希望序列化的属性前面加上transient就好了。注意了:transient只能用于修饰属性,不可修饰Java的其他成员。使用这种方式很方便的,但是有一个缺陷的:被transient修饰的属性完全隔离在序列化机制之外,这样会导致反序列化恢复Java对象时无法获取该属性值。

    2,通过自定义序列化机制可以让程序控制如何序列化各个成分。在需要特殊处理的类中提供如下3个方法,就可以实现了,使用情景不是很多,了解下就好了。
    private void wirteObject(ObjectOutputStream out):写对象
    private void readObject(ObjectInputStream in):读对象
    private void readObjectNoData():当序列流不完整的时候,用来正确初始化反序列化的对象

  • 相关阅读:
    Linxu下段错误(segmentation fault)的调试
    n900破解无线路由密钥(wep)
    windows 7 下安装运行VC6
    【转载】W32Dasm反汇编工具使用详解
    用PreCode Snippet代码高亮
    转——别告诉我能懂PPT
    程序员必须掌握的基本正则表达式
    简单之美—软件开发实践者的思考—故事场景1
    xml解析
    Android Frameworktouch事件派发过程深入理解
  • 原文地址:https://www.cnblogs.com/LinkinPark/p/5233104.html
Copyright © 2020-2023  润新知