zoukankan      html  css  js  c++  java
  • 输入和输出--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():当序列流不完整的时候,用来正确初始化反序列化的对象

  • 相关阅读:
    Truck History(poj 1789)
    Highways poj 2485
    117. Populating Next Right Pointers in Each Node II
    116. Populating Next Right Pointers in Each Node
    115. Distinct Subsequences
    114. Flatten Binary Tree to Linked List
    113. Path Sum II
    109. Convert Sorted List to Binary Search Tree
    106. Construct Binary Tree from Inorder and Postorder Traversal
    105. Construct Binary Tree from Preorder and Inorder Traversal
  • 原文地址:https://www.cnblogs.com/LinkinPark/p/5233104.html
Copyright © 2011-2022 走看看