对象序列化_对象序列化和反序列化 - CSDN
精华内容
参与话题
  • Java 对象序列化

    2018-07-28 10:55:28
    Java 对象序列化 Java 的对象序列化将那些实现了 Serializable 接口的对象转换成一个字节序列(因此,只能使用 Stream 相关类进行操作),并能够在以后将这个字节序列完全恢复为原来的对象。只要对象实现了 ...

    Java 对象序列化

    Java 的对象序列化将那些实现了 Serializable 接口的对象转换成一个字节序列(因此,只能使用 Stream 相关类进行操作),并能够在以后将这个字节序列完全恢复为原来的对象。只要对象实现了 Serializable 接口(该接口仅是一个标记接口,并不包括任何方法),对象序列化和反序列化通过以下两步实现:

    • 创建 OutputStream 对象,封装在 ObjectOutputStream 对象中,只需调用 writerObject 即可将对象序列化
    • 创建 InputStream 对象,封装在 ObjectInputStream 对象中,只需调用 readObject 即可将对象序列化
    
    import java.io.*;
    
    public class Alien implements Serializable {} /* Output 
    错误: 在类 Alien 中找不到 main 方法, 请将 main 方法定义为:
       public static void main(String[] args)
    否则 JavaFX 应用程序类必须扩展javafx.application.Application
    *///:~
    import java.io.*;
    
    public class FreezeAlien {
    	public static void main(String[] args) throws IOException {
    		ObjectOutput out = new ObjectOutputStream(
    		 new FileOutputStream("X.file"));
    		Alien quellek = new Alien();
    		out.writeObject(quellek);
    	}
    }///:~
    
    import java.io.*;
    
    public class ThawAlien {
    	public static void main(String[] args) throws Exception {
    		ObjectInputStream in = new ObjectInputStream(
    		 new FileInputStream(new File("X.file")));
    		Object mystery = in.readObject();
    		System.out.println(mystery.getClass());
    	}
    }/* Output 
    class Alien
    *///:~

    但此时如果要想打开和读取序列化对象中的内容就需要 Alien.class 文件;否则,将得到一个 ClassNotFoundException 的异常。

    最后需要特别注意的是:在对对象序列化时,文件中不仅保存着对象中的所有数据,还包括版本、 package 等对象的所有信息。因此在反序列化时,必须保证所有环境完全相同才能正确反序列化。比如库版本不一致,将造成 serialVersionUID 不一致。

    序列化控制

    如果处于安全考虑,不希望对象的某一部分被序列化;或者一个对象被还原后,某个子对象需要重新创建,从而不必将该子对象序列化。Externalizable 接口继承了 Serializable 接口,并定义了 writeExternal 和 readExternal 两个方法,来对序列化和反序列化进行控制。其实现了没有任何东西可以自动序列化,并且只能通过在 writeExternal 方法中对所需部分进行显式序列化。

    在反序列化时,所有普通的默认构造器都会被调用(包括在字段定义时的初始化,因此类中必须定义有默认构造器),然后调用 readExternal。必须注意一点——所有默认的构造器都会被调用,才能使反序列化对象产生正确的行为。并且在 writeExternal 中写入的数据必须在 readExternal 中以同样的顺序读出。

    同时,如果从一个 Externalizable 对象继承,通常需要在 writeExternal 方法中将来自对象的重要信息写入,还必须在 readExternal 方法中恢复数据。

    
    import java.io.*;
    
    public class Blip implements Externalizable {
    	private int i;
    	private String s; // No initialization
    	
    	public Blip() {
    		System.out.println("Blip Constructor");
    		// s, i not initialized
    	}
    	
    	public Blip(String x, int a){
    		System.out.println("Blip(String x, int a)");
    		s = x;
    		i = a;
    		// s & i initialized only in non-default constuctor
    	}
    	
    	public String toString() {return s + i;}
    	
    	@Override
    	public void writeExternal(ObjectOutput out) throws IOException {
    		System.out.println("Blip writeExternal");
    		out.writeObject(s);
    		out.writeInt(i);
    	}
    	
    	@Override
    	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    		System.out.println("Blip readExternal");
    		s = (String)in.readObject();
    		i = in.readInt();
    	}
    	
    	public static void main(String[] args) throws IOException, ClassNotFoundException {
    		System.out.println("Constructing Objects: ");
    		Blip b = new Blip("A String", 47);
    		System.out.println(b);
    		ObjectOutputStream o = new ObjectOutputStream(
    		 new FileOutputStream("Blip.out"));
    		System.out.println("Saving object: ");
    		o.writeObject(b);
    		o.close();
    		// Now get it back:
    		ObjectInputStream in = new ObjectInputStream(
    		 new FileInputStream("Blip.out"));
    		System.out.println("Recovering b: ");
    		b = (Blip)in.readObject();
    		System.out.println(b);
    	}
    }/* Output 
    Constructing Objects:
    Blip(String x, int a)
    A String47
    Saving object:
    Blip writeExteranl
    Recovering b:
    Blip Constructor
    Blip readExteranl
    A String47
    *///:~

    serializable 中的序列化控制

    但当只有某个域(比如密码)不需要被序列化,这样使用 Externalizable 就显得很麻烦;此时可以在实现了 Serializable 的类中使用 transient 关键字来标识某个字段,这样该字段就不会被序列化,同时在反序列化中被赋值为空。

    除了关键字,还可以通过在实现了 Serializable 的类中添加 readObject 和 writeObject 来实现来序列化控制,注意此时的添加,并不是覆盖或实现,且必须按以下方式进行特征签名

    • private void writeObject(ObjectOutputStream stream) throws IOException;
    • private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException;

    从设计的角度出发,情况变得有些扑朔迷离。首先,大家可能认为这些方法不属于基础类或者 Serializable 接口的一部分,所以它们应该在自己的接口中得到定义。但请注意它们被定义成“private”,这意味着它们只能由这个类的其他成员调用。然而,我们实际并不从这个类的其他成员中调用它们,而是由 ObjectOutputStream 和 ObjectInputStream 的 writeObject() 及 readObject() 方法来调用我们对象的 writeObject() 和 readObject() 方法(注意我在这里用了很大的抑制力来避免使用相同的方法名——因为怕
    混淆)。大家可能奇怪 ObjectOutputStream 和 ObjectInputStream 如何有权访问我们的类的 private 方法——只能认为这是序列化机制玩的一个把戏。

    在任何情况下,接口中的定义的任何东西都会自动具有 public 属性,所以假若 writeObject() 和 readObject() 必须为 private,那么它们不能成为接口的一部分。但由于我们准确地加上了签名,所以最终的效果实际与实现一个接口是相同的。

    看起来似乎我们调用 ObjectOutputStream.writeObject() 的时候,我们传递给它的 Serializable 对象似乎会被检查是否实现了自己的 writeObject()。若答案是肯定的是,便会跳过常规的序列化过程,并调用 writeObject()。readObject()也会遇到同样的情况。还存在另一个问题。在我们的 writeObject() 内部,可以调用 defaultWriteObject(),从而决定采取默认的 writeObject() 行动。类似地,在 readObject() 内部,可以调用defaultReadObject()。

    但在具体实现以前,有些问题是必须解决的。如果两个对象都有指向第三个对象的句柄,该如何对这两个对象序列化呢?如果从两个对象序列化后的状态恢复它们,第三个对象的句柄只会出现在一个对象身上吗?如果将这两个对象序列化成独立的文件,然后在代码的不同部分重新装配它们,又会得到什么结果呢?

    • 只要将所有东西都序列化到单独一个数据流里,就能恢复获得与以前写入时完全一样的对象网,不会不慎造成对象的重复。当然,在写第一个和最后一个对象的时间之间,可改变对象的状态,但那必须由我们明确采取操作——序列化时,对象会采用它们当时的任何状态(包括它们与其他对象的连接关系)写入。若想保存系统状态,最安全的做法是当作一种“微观”操作序列化。如果序列化了某些东西,再去做其他一些工作,再来序列化更多的东西,以此类推,那么最终将无法安全地保存系统状态。相反,应将构成系统状态的所有对象都置入单个集合内,并在一次操作里完成那个集合的写入。这样一来,同样只需一次方法调用,即可成功恢复之。 

     

    展开全文
  • Java对象序列化(Serialization)和反序列化详解

    万次阅读 多人点赞 2019-08-04 15:59:56
    序列化(Serialization)是将对象的状态信息转化为可以存储或者传输的形式的过程,一般将一个对象存储到一个储存媒介,例如档案或记忆体缓冲等,在网络传输过程中,可以是字节或者XML等格式;而字节或者XML格式的...

    ####1.序列化和反序列化
    序列化(Serialization)是将对象的状态信息转化为可以存储或者传输的形式的过程,一般将一个对象存储到一个储存媒介,例如档案或记忆体缓冲等,在网络传输过程中,可以是字节或者XML等格式;而字节或者XML格式的可以还原成完全相等的对象,这个相反的过程又称为反序列化;

    ####2.Java对象的序列化和反序列化
    在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用此对象。但是,我们创建出来的这些对象都存在于JVM中的堆(heap)内存中,只有JVM处于运行状态的时候,这些对象才可能存在。一旦JVM停止,这些对象也就随之消失;

    但是在真实的应用场景中,我们需要将这些对象持久化下来,并且在需要的时候将对象重新读取出来,Java的序列化可以帮助我们实现该功能。

    对象序列化机制(object serialization)是java语言内建的一种对象持久化方式,通过对象序列化,可以将对象的状态信息保存未字节数组,并且可以在有需要的时候将这个字节数组通过反序列化的方式转换成对象,对象的序列化可以很容易的在JVM中的活动对象和字节数组(流)之间进行转换。

    在JAVA中,对象的序列化和反序列化被广泛的应用到RMI(远程方法调用)及网络传输中;

    ####3.序列化及反序列化相关接口及类
    Java为了方便开发人员将java对象序列化及反序列化提供了一套方便的API来支持,其中包括以下接口和类:

    java.io.Serializable
    
    java.io.Externalizable
    
    ObjectOutput
    
    ObjectInput
    
    ObjectOutputStream
    
    ObjectInputStream
    

    ####4.Serialization接口详解
    Java类通过实现java.io.Serialization接口来启用序列化功能,未实现此接口的类将无法将其任何状态或者信息进行序列化或者反序列化。可序列化类的所有子类型都是可以序列化的。序列化接口没有方法或者字段,仅用于标识可序列化的语义。

    当试图对一个对象进行序列化时,如果遇到一个没有实现java.io.Serialization接口的对象时,将抛出NotSerializationException异常。

    如果要序列化的类有父类,要想将在父类中定义过的变量序列化下来,那么父类也应该实现java.io.Serialization接口。

    下面是一个实现了java.io.Serialization接口的类:

    package common.lang;
    
    import java.io.Serializable;
    
    import org.apache.commons.lang3.builder.ToStringBuilder;
    import org.apache.commons.lang3.builder.ToStringStyle;
    
    public class User1 implements Serializable{
    
    	private String name;
    	private int age;
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	
    	@Override
    	public String toString() {
    		return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
    								   .append("name", name)
    								   .append("age", age)
    								   .toString();
    	}
    }
    
    

    通过下面的代码进行序列化及反序列化:

    package common.lang;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    public class SerializableDemo1 {
    
    	public static void main(String[] args) throws Exception, IOException {
    		//初始化对象
    		User1 user = new User1();
            user.setName("yaomy");
            user.setAge(23);
            System.out.println(user);
            //序列化对象到文件中
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("template"));
            oos.writeObject(user);
            oos.close();
            //反序列化
            File file = new File("template");
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            User1 newUser = (User1)ois.readObject();
            System.out.println(newUser.toString());
    	}
    }
    
    

    ####5.Java还提供了另一个序列化接口java.io.Externalizable
    为了了解Externalizable接口和Serializable接口的区别先来看代码,我们将上面的User1类改为实现java.io.Externalization接口;

    package common.lang;
    
    import java.io.Externalizable;
    import java.io.IOException;
    import java.io.ObjectInput;
    import java.io.ObjectOutput;
    
    import org.apache.commons.lang3.builder.ToStringBuilder;
    import org.apache.commons.lang3.builder.ToStringStyle;
    
    public class User1 implements Externalizable{
    
    	private String name;
    	private int age;
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	
    	@Override
    	public String toString() {
    		return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
    								   .append("name", name)
    								   .append("age", age)
    								   .toString();
    	}
    	@Override
    	public void writeExternal(ObjectOutput out) throws IOException {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    		// TODO Auto-generated method stub
    		
    	}
    }
    
    
    package common.lang;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    public class SerializableDemo1 {
    
    	public static void main(String[] args) throws Exception, IOException {
    		//初始化对象
    		User1 user = new User1();
            user.setName("yaomy");
            user.setAge(23);
            System.out.println(user);
            //序列化对象到文件中
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("template"));
            oos.writeObject(user);
            oos.close();
            //反序列化
            File file = new File("template");
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            User1 newUser = (User1)ois.readObject();
            System.out.println(newUser.toString());
            ois.close();
    	}
    }
    
    

    输出结构是:

    common.lang.User1@6ef64f64[
      name=yaomy
      age=23
    ]
    common.lang.User1@184c9860[
      name=<null>
      age=0
    ]
    
    

    通过上面的实例可以发现,对User1进行序列化然后再进行反序列化之后对象的属性都恢复成了默认值,也就是说之前的哪个对象的状态并没有被持久化下来,这就是Externalization和Serialization接口之间的区别;

    Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法。由于上面的代码中,并没有在这两个方法中定义序列化实现细节,所以输出的内容为空。还有一点值得注意:在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器。

    按照要求修改之后的代码是:

    package common.lang;
    
    import java.io.Externalizable;
    import java.io.IOException;
    import java.io.ObjectInput;
    import java.io.ObjectOutput;
    
    import org.apache.commons.lang3.builder.ToStringBuilder;
    import org.apache.commons.lang3.builder.ToStringStyle;
    
    public class User1 implements Externalizable{
    
    	private String name;
    	private int age;
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	
    	@Override
    	public String toString() {
    		return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
    								   .append("name", name)
    								   .append("age", age)
    								   .toString();
    	}
    	@Override
    	public void writeExternal(ObjectOutput out) throws IOException {
    		out.writeObject(name);
    		out.writeInt(age);
    		
    	}
    	@Override
    	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    		name = (String)in.readObject();
    		age = in.readInt();
    		
    	}
    }
    
    
    package common.lang;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    public class SerializableDemo1 {
    
    	public static void main(String[] args) throws Exception, IOException {
    		//初始化对象
    		User1 user = new User1();
            user.setName("yaomy");
            user.setAge(23);
            System.out.println(user);
            //序列化对象到文件中
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("template"));
            oos.writeObject(user);
            oos.close();
            //反序列化
            File file = new File("template");
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            User1 newUser = (User1)ois.readObject();
            System.out.println(newUser.toString());
            ois.close();
    	}
    }
    
    

    输出结果是:

    common.lang.User1@6cd66725[
      name=yaomy
      age=23
    ]
    common.lang.User1@19160e64[
      name=yaomy
      age=23
    ]
    
    

    这样就可以将之前的对象状态保存下来了,如果User类中没有无参数的构造函数,在运行时会抛出异常:java.io.InvalidClassException;

    ####6.静态变量的序列化

    静态变量序列化代码:

    public class Test implements Serializable {
     
        private static final long serialVersionUID = 1L;
     
        public static int staticVar = 5;
     
        public static void main(String[] args) {
            try {
                //初始时staticVar为5
                ObjectOutputStream out = new ObjectOutputStream(
                        new FileOutputStream("result.obj"));
                out.writeObject(new Test());
                out.close();
     
                //序列化后修改为10
                Test.staticVar = 10;
     
                ObjectInputStream oin = new ObjectInputStream(new FileInputStream(
                        "result.obj"));
                Test t = (Test) oin.readObject();
                oin.close();
                 
                //再读取,通过t.staticVar打印新的值
                System.out.println(t.staticVar);
                 
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    

    main 方法中,将对象序列化后,修改静态变量的数值,再将序列化对象读取出来,然后通过读取出来的对象获得静态变量的数值并打印出来,打印出来的是10还是5?

    最后的输出是 10,对于无法理解的读者认为,打印的 staticVar 是从读取的对象里获得的,应该是保存时的状态才对。之所以打印 10 的原因在于序列化时,并不保存静态变量,这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

    ####7.Transient 关键字使用
    Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

    参考:Java序列化高级进阶
    参考:深入分析Java的序列化和反序列化

    展开全文
  • 对象序列化的含义和意义

    万次阅读 2018-06-03 19:48:03
    序列化机制允许将实现序列化的Java对象转换成字节序列 ,这些字节序列可以被保存在磁盘上,或者通过网络传输,以备以后重新恢复成原来的对象.1.对象序列化(serialize)指将一个Java对象写入IO流中,对象的反序列化机制...

    序列化机制允许将实现序列化的Java对象转换成字节序列 ,这些字节序列可以被保存在磁盘上,或者通过网络传输,

    以备以后重新恢复成原来的对象.

    1.对象的序列化(serialize)指将一个Java对象写入IO流中,对象的反序列化机制(Deserialize)则指从IO流中恢复该Java对象.

    如果需要让某个对象可以支持序列化机制,必须让它的类是可序列化的(实现Serialize接口或者Externalizable接口)

    一.使用对象流实现序列化

        ObjectOutputStream oos=new ObjectOutputStream( new FileOutputStream("..."));//输出流(处理流)

        oos.writeObject(new Object())//对象输出到输出流中

    二.使用对象流反序列化

        ObjectInputStream ois=new ObjectInputStream( new FileInputStream("..."));//输入流

        Object obj=ois.readObject();//从IO流中读取该Java对象,完成反序列化机制

    2.对象引用的序列化

        1)如果某个类的基本类型是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型属性类是不可序列化的.

        2)   Java序列化算法:

            所有保存到磁盘的对象都有一个序列化编号,  当程序试图序列化一个对象时,程序先检查该对象是否已经被序列化过了,只有当该对象从未被序列化过,系统才会将该对象转换成字节序列并输出.  如果某个对象已经被序列化过了,程序将只是输出一个序列化编号,而不是再次重新序列化该对象.

        3)由于Java的序列化机制,当程序序列胡一个可变对象时,只有第一次使用writeObject方法输出时才会将该对象转换成字节序列并输出,即使后面该对象的属性已经改变,程序再次调用writeObject方法时,只是输出一个序列化编号,所以改变的属性值不会被输出.

    三.自定义序列化

        当对某个对象进行序列化时,系统会自动把该对象的所有属性依次进行序列化,如果某个属性引用到另一个对象,则被引用的对象也会被序列化,如果被引用的对象属性也引用了其他对象,则被引用的对象也会被序列化,这种被称为递归序列化.

    1)被transient修饰的属性,Java对该属性不进行序列化.  被transient修饰的属性将被完全隔离在序列化机制外,在反序列化恢复该Java对象时无法获得该属性值.

            transient关键字只能用于修饰属性,不可修饰Java中其他成分.

    2)Java提供了另外一种序列化机制,通过这种自定义序列化机制可以让程序控制如何序列化各属性,甚至完全不序列化某些属性.

       

        

    //上述代码为自定义序列化

    writeObject方法负责写入特定类的实例状态, 以便相应的readObject可以恢复它. 通过重写该方法, 可以完全获得对序列化机制的控制.(可以自主决定哪些属性需要序列化,如何序列化)

    readObject方法负责从流中读取并恢复对象属性, 通过重写该方法, 可以完全获得对反序列化机制的控制,(自主决定需要反序列化哪些属性,如何反序列化)

    <注意>当序列化流不完整时, readObjectNoData 方法可以用来正确地初始化反序列化对象, 例如,当接收方使用的反序列化类的版本不同于发送方, 或者序列化流被篡改时, 系统都会调用 readObjectNoData 方法来初始化反序列化对象.

    writeObject 方法存储属性的顺序应该和 readObject方法中恢复属性的顺序一致, 否则不能正常恢复该对象.

    3)更彻底的序列化机制

        如果需要在实现序列化某对象时替换该对象, 应为序列化类提供 Object writeReplace() 方法.

        Java的序列化机制保证在序列化某个对象之前, 先调用该对象的 writeReplace()方法, 如果该方法返回另一个Java对象, 系统

        将再次调用另一个对象的writeReplace方法, 直到该方法不再返回另一个对象为止. 程序最后将调用该对象的 writeObject 方法来保存该对象的状态.

          

    //序列化机制在序列化Person对象时, 实际是转换为序列化 ArrayList对象

    <注意>与writeReplace()方法相对的是, 序列化机制中还有一个特殊的方法, 可以实现保护性复制整个对象.

    Object readResolve() 方法会紧接着readObject() 之后被调用, 该方法的返回值将会代替原来反序列化的对象, 而原来readObject 反序列化对象将会被立即丢弃.


    四.使用 Externalizable 接口实现自定义序列化机制

    Java提供了另一种序列化机制,这种序列化方式完全由程序员决定存储和恢复对象数据. 要实现该目标,Java类必须实现Externalizable接口.

    1)Externalizable接口强制自定义序列化.

       

    <重点>1.程序需要序列化实现Externalizable接口的对象, 一样调用ObjectOutputStream 的writeObject()方法输出该对象;反序列化该对象

    ,调用ObjectInputStream 的readObject()方法即可.

          2.需要实现序列化的类,最好给出无参构造,否则程序会出现InvalidClassException异常.


       
        
    五.两种序列化机制的对比

        实现Serializable接口                                            实现Externalizable接口
                            

        系统自动存储必要信息                                        程序员决定存储哪些信息(自定义序列化)


        Java内建支持,易于实现,只需实现该接口                提供两个空方法,实现该接口必须为两个空方法提供实现
        即可,无序任何代码支持

        
        性能略差                                                            性能略高


    <注意>虽然实现Externalizable接口能带来性能提升, 但由于该接口提升了编程的复杂度, 所以实际开发中大多使用Serializable接口来实现
    序列化.

    六.(重点)关于对象序列化的注意事项

        1)对象的类名,属性(包括基本类型,数组,对其他对象的引用)都会被序列化; 方法, static属性(静态属性), transient属性(瞬态属性)
        都不会被序列化.

        2)实现Serializable接口的类如果要想让某个属性不被序列化, 可用transient修饰该属性, 而不是用static修饰该属性.(虽然static)
        也能达到这样的效果, 但static不能这样用.

        3)保证序列化对象的属性的类型也是可序列化的, 否则, 该类是不可序列化的.

        4)反序列化对象时必须有对象的class文件.

        5)当通过文件或者网络来读取序列化后对象时,必须按实际写入的顺序读取.


    七.版本

    反序列化对象时必须提供该对象的class文件, 有时随着项目的升级, 系统的class 文件也会升级, Java为了保证两个class文件的兼容性

    Java序列化机制允许为序列化类提供一个private static final 的 serialVersionUID 属性值, 该属性值用于标识该Java类的序列化版本,

    当一个类升级后, 只要它的serialVersionUID属性值保持不变, 序列化机制也会把它们当成同一个序列化版本.

    <*>不显示定义serialVersionUID属性的另一个弊端是: 不利于在JVM之间的移植, 不同编译器计算该属性的计算策略可能不同, 即使该类

    完全没有改变, 但因为JVM的不同, 也会出现序列化版本不兼容而无法正确反序列的现象.

    <重点>对类的修改会导致该类反序列化失败时,应该为该类重新分配一个serialVersionUID属性值.

        对类的哪些修改会导致反序列失败,分两种情况:

        1)修改类时仅仅修改了方法, 静态属性或者瞬时属性, 则反序列化不受任何影响, 类定义无须修改serialVersionUID属性值.

        2)如果修改了类中的非静态 , 非瞬态属性, 则可能导致序列化版本不兼容.



        


    展开全文
  • 对象序列化

    2020-10-10 22:07:00
    对象序列化 序列化 : sum s = new sum(); s.soul =526; s.sun=256; try { FileOutputStream ou = new FileOutputStream("yGame.ser"); ObjectOutputStream os = new ObjectOutputStream(ou); os....

    对对象的序列化

    • 序列化

          sum s = new sum();
              s.soul =526;
              s.sun=256;
              try {
                  FileOutputStream ou = new FileOutputStream("yGame.ser");
                  ObjectOutputStream os = new ObjectOutputStream(ou);
                  os.writeObject(s);
                  os.close();
              } catch (Exception e) {
                  e.printStackTrace();
              }
      
    • 从文件中读出对象的值

      try {
          FileInputStream om = new FileInputStream("yGame.ser");
          ObjectInputStream input = new ObjectInputStream(om);
          Object[] ser = new Object[12];
          int i=0;
      
              ser[i++] = input.readObject();
              sum mm= new sum();
              mm = (sum) ser[0];
              input.close();
          System.out.println(mm.sun);
          System.out.println(mm.soul);
      
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      } catch (ClassNotFoundException e) {
          e.printStackTrace();
      }
      
    • 读出过程 :

      1 . 对象从 Stream中读出来

      2.Java JVM通过储存的信息判断对象的class类型

      3.Java JVM 尝试寻找和加载对象的类,如果Java JVM找不到或无法加载时该类,则Java JVM

      会抛出例外。

      4.新的对象会被配置在堆上,但是构造函数不会执行!这样对象就成了新的状态,我们需要的是储存时候的状态 。

      5.if 对象在继承树上有个不可序列化的祖先类,则该不可序列化樊以及在它之上的类的构造函数(就

      算是可序列化也一样 会执行一旦构造函数披连锁启动之后将无法停止 也就是说。从第 个不可

      序列化的父类开始.全部都会重新初始状态。

      6.对象的实例变量会被还原成实例化时顶的状态值。transient变量会被复制null的对量。引用或primitive主,那数据类型的默认值为0,false等值。

    • 静态变量不会被序列化

      要点

      • 你可以通过序列化来存储对象的状态。
      • 使用ObjctOuputStean来序列化对象(java.io)。
      • Surcam是连接申流或是链接用的串流。
      • 连接串流用来表示源或目的地、文件、则络套接字连接。
      • 链接用串流用来衔接连接串流。
      • 用FilcOutpuSream链接ObjectouputStream来将对象序列化
        到文件上。
      • 调用ObjectoupuStrcam的writeobject(heobject)来将对象序列化,不需调用FileOutputStream的方法。
      • 对象必须实现序列化这个接口才能被序列化。如果父类实现序列化,则子类也就自动地有实现,而不管是否有明确的声明。
      • 当对象被序列化时,整个对象版图都会被序列化。这代表它的实例变量所引用的对象也会被序列化。
      • 如果有不能序列化的对象,执行期间就会抛出异常。
      • 除非该实例变量被标记为transient。否则,该变量在还原的时候会被赋子予null或primitive主数据类型的默认值。
      • 在解序列化时(deserialization),所有的类都必须能让Java虚拟机找到。
      • 读取对象的顺序必须与写人的顺序相同。
      • readObject()的返回类型是Object,因此解序列化回来的对象还需要转换成原来的类型。
      • 静态变量不会被序列化,因为所有对象都是共享同一份静态变量值。

    • 1)序列化适用于存储散据输非J加a程序使用的情境。False
      (3)OyeciOutputscam是个用来存健序列化对象的类。True
      (4)磁接申流可以单独使用成与做接申流并用。False
      (5)调用witcObjec()可能会存储好几个对象。True
      (7)transicnt这个修饰符让你将实例变量标记为可序列化的。False
      (8)如果父类是不可序列化的,则子类就不能设定成可序列化。False
      (9)当对象被序列化时,它的读取顺序必须是相反的。False
      (10)当对象还原时,构造函数不会执行。True
      (11)序列化与使用文本文件保存的操作都可能会抛出异常。True
      (12)BufferedWriter可以链接到FileWriter上。True
      (13)File对象代表文件而不是目录。False
      (15)文件的读写申流机制可以用到缓冲区机制。True
      (16)String的splitO金将分隔字符解析成结果的一部分。False
      (17)对类的任何修改都会破坏之前的序列化对象。False

    展开全文
  • 对象序列化

    2019-06-06 15:07:17
    18.12 对象序列化 当你创建对象时,只要你需要,他就会一直存在,但是在程序终止时,无论如何他都不会继续存在。尽管这么做肯定是有意义的。但是仍旧存在某些情况,如果对象能够在程序不运行的情况下仍能存在并保存...
  • 序列化和反序列化的详解

    万次阅读 多人点赞 2018-09-19 09:09:29
     (1)Java序列化就是指把Java对象转换为字节序列的过程  Java反序列化就是指把字节序列恢复为Java对象的过程。  (2)序列化最重要的作用:在传递和保存对象时.保证对象的完整性和可传递性。对象转换为有序字节流,...
  • Java对象序列化详解

    万次阅读 多人点赞 2019-06-27 11:37:17
    一、定义 序列化:把Java对象转换为字节序列的过程。    反序列化:把字节序列恢复为Java对象的过程。二、用途 对象序列化主要有两种用途:    1) 把对象的字节序列永久地保存到硬盘上,通常存放在一...
  • Java基础——对象序列化(通俗易懂,排版优美)

    千次阅读 多人点赞 2018-05-24 10:01:12
    Java基础——对象的序列化什么是对象的序列化...通过对象序列化,可以方便的实现对象的持久化储存以及在网络上的传输。大致的过程如下图所示: 对象被转换成“字节流”后可以存入文件,内存,亦或者是数...
  • 对象序列化是什么 对象序列化(Serialize)指将一个 Java 对象写入 IO 流中,与此对应的是,对象的反序列化(Deserialize)则指从 IO 流中恢复该 Java 对象。如果想让某个 Java 对象能够序列化,则必须让它的类实现 ...
  • 序列化和反序列化的底层实现原理是什么?

    万次阅读 多人点赞 2019-06-02 14:37:11
    序列化和反序列化作为Java里一个较为基础的知识点,大家心里也有那么几句要说的,但我相信很多小伙伴掌握的也就是那么几句而已,如果再深究问一下Java如何实现序列化和反序列化的,就可能不知所措了!遥记当年也被问...
  • Java序列化与反序列化

    万次阅读 多人点赞 2012-09-18 16:49:33
    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨。  1.Java序列化与反序列化  Java序列化是指把Java对象转换为字节序列的过程;而Java反...
  • 任何序列化该类的尝试都会因NotSerializableException而失败,但这可以通过在Java中为其设置瞬态(trancient)变量来轻松解决。 Java序列化相关的常见问题 Java序列化是一个重要概念,但它很少用作持久性解决方案...
  •  序列化简单来说就保存对象在内存中的状态也可以说是实例化变量。这是Java提供的用来保存 Object state,一种保存对象状态的机制。只有实现了serializable接口的类的对象才能被实例化。 2什么情况下会用到序列化?...
  • 序列化是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列...
  • 1. 序列化 对象 /** 使用 Newtonsoft.Json 序列化对象 **/ [WebMethod] public String getPersonInfos() { // 初始化数据 List mlist = new List(); for (int i = 0; i ;i+
  • Java 序列化与反序列化

    万次阅读 2020-09-29 18:08:09
    序列化 :把对象转换为字节序列存储于磁盘或者进行网络传输的过程称为对象序列化。 反序列化:把磁盘或网络节点上的字节序列恢复到对象的过程称为对象的反序列化。 一、序列化对象 【1】、必须实现序列化接口 ...
  • 序列化和反序列化作为Java里一个较为基础的知识点,大家心里也有那么几句要说的,但我相信很多小伙伴掌握的也就是那么几句而已,如果再深究问一下Java如何实现序列化和反序列化的,就可能不知所措了!遥记当年也被问...
  • Android中序列化的原理与机制

    万次阅读 2018-09-30 17:12:34
    引用Java语言程序设计-基础篇中第539页中写道:并不是所有对象都是可以序列化的(这句话说明了序列化必须要具备某种条件才可以进行序列化),因为序列化对象是Java.Io.Serializable接口的实例。也就是说如果需要...
  • 序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,将数据分解成字节流,以便存储在文件中或在网络上传输。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是...
  • Java序列化的作用和反序列化

    万次阅读 2013-05-09 14:30:02
    1、序列化是干什么的?  简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你...
1 2 3 4 5 ... 20
收藏数 571,989
精华内容 228,795
关键字:

对象序列化