Serializable详解(1):代码验证Java序列化与反序列化

说明:本文为Serializable详解(1),最后两段内容在翻译上出现歧义(暂时未翻译),将在后续的Serializable(2)文中补充。

介绍:本文根据JDK英文文档翻译而成,本译文并非完全按照原文档字面文字直译,而是结合文档内容及个人经验翻译成更为清晰和易于理解的文字,并附加代码验证,帮助大家更好地理解Serializable。

性质:接口类

package java.io

public interface Serializable

1.1 翻译文档

Serializability of a class is enabled by the class implementing the java.io.Serializable interface.

通过实现java.io.Serializable interface接口来序列化一个类。

Classes that do not implement this interface will not have any of their state serialized or deserialized.

没有实现此接口的类任何状态都不会序列化或反序列化。

All subtypes of a serializable class are themselves serializable.

可序列化类的所有子类而本身都是可序列化的。

The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.

序列化接口没有方法或字段域,它仅用来标识可序列化的语义。

(1)To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype’s public, protected, and (if accessible) package fields.

(2)The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class’s state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.

(3)During deserialization, the fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class. A no-arg constructor must be accessible to the subclass that is serializable. The fields of serializable subclasses will be restored from the stream.

(1)为了让非序列化类的子类可以被序列化,这个子类可以承担保存和恢复超类的pulic,protected,package字段(如果可访问的话)。

(2)只有当它拓展的类具有可访问无参构造函数来初始化类的状态时,子类才可以承担这样的责任。如果不是这种情况,就不能声明一个类是可序列化的。这个错误将在运行的时候被检测出来。

(3)在反序列化期间,非序列化类的字段将通过类的以public或者protected修饰的空参构造函数实例化。无参数构造函数必须可访问可序列化的子类。序列化子类的字段能够从字符流里被还原。

1.2 辅助理解

(1)(2)(3)三块主要说了三件事:

  • 非序列化的父类,其子类实现序列化时承担保存和恢复父类public、protected、package等子类可访问到子类的字段;
  • 非序列化的父类,其子类进行序列化时,父类需要有用public或者protected修饰的空参构造函数;
  • 若无空参构造函数的父类,其子类在运行序列化时将正常进行,但反序列化时会发生错误,并抛出异常。但父类有空参构造函数,子类完成序列化,父类属性却没有参与到序列化中。

1.3 注意:此处有三个坑。

  • (1)中所述父类未实现序列化,实现序列化的子类会承担保存和恢复父类的public、protected、package等子类可访问到子类的字段。此处我个人理解为实现序列化的子类进行序列化的时候继承了未实现序列化的父类中子类可访问到的属性,但序列化时无法记录下父类对象的状态信息;
  • 此处文档若要正确读取理解,切记(1)(2)(3)不可拆分,要放在一起去理解(上文之所以分开是便于翻译);
  • 此处英文翻译成汉字,难以理解其真实含义,所以通过下面的代码验证来辅助理解

1.4 代码验证

辅以A/B两套类型代码对比理解:

1)A套

父类:Biology 类

package com.springboot.SpringBootDemo.serializable;
    
            public class Biology {
            	
            	public String type;
            	
            	private int num;
            
            	public Biology(String type, int num) {
            		this.type = type;
            		this.num = num;
            	}
            
            	public String getType() {
            		return type;
            	}
            
            	public void setType(String type) {
            		this.type = type;
            	}
            
            	public int getNum() {
            		return num;
            	}
            
            	public void setNum(int num) {
            		this.num = num;
            	}
            }

子类:People 类

package com.springboot.SpringBootDemo.serializable;
    
            import java.io.Serializable;
            
            public class People extends Biology implements Serializable{
            
            	private static final long serialVersionUID = -6623611040000763479L;
            
            	public String name;
            	
            	protected String gender;
            	
            	private int age;
            
            	public People(String type, int num, String name ,String gender ,int age) {
            		super(type, num);
            		this.name = name;
            		this.gender = gender;
            		this.age = age;
            	}
            
            	public String getName() {
            		return name;
            	}
            
            	public void setName(String name) {
            		this.name = name;
            	}
            
            	public String getGender() {
            		return gender;
            	}
            
            	public void setGender(String gender) {
            		this.gender = gender;
            	}
            
            	public int getAge() {
            		return age;
            	}
            
            	public void setAge(int age) {
            		this.age = age;
            	}
            }

测试类:

           import java.io.FileInputStream;
            import java.io.FileOutputStream;
            import java.io.IOException;
            import java.io.ObjectInputStream;
            import java.io.ObjectOutputStream;
            
            public class Test {
            	
            	public static void main(String[] args) throws IOException, ClassNotFoundException {
            		People pp = new People("human",10000,"张三","男",25);
            		
            		FileOutputStream fos = new FileOutputStream("test.txt");
                    ObjectOutputStream oos = new ObjectOutputStream(fos);
                    oos.writeObject(pp);
                    oos.flush();
                    oos.close();
                    
                    //反序列化
                    FileInputStream sfis = new FileInputStream("test.txt");
                    ObjectInputStream sois = new ObjectInputStream(sfis);
                    People p = (People) sois.readObject();
                    System.out.println(
                    		p.getType() +" "+
                    		p.getNum() +" "+
                    		p.getName() +" "+
                    		p.getGender() +" "+
                    		p.getAge()
                    		);
            	}
            }

结果:

   Exception in thread "main" java.io.InvalidClassException: com.springboot.SpringBootDemo.serializable.People; no valid constructor
            	at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(Unknown Source)
            	at java.io.ObjectStreamClass.checkDeserialize(Unknown Source)
            	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
            	at java.io.ObjectInputStream.readObject0(Unknown Source)
            	at java.io.ObjectInputStream.readObject(Unknown Source)
            	at com.springboot.SpringBootDemo.serializable.Test.main(Test.java:23)

结果说明:在序列化时未发生异常,而在反序列化readObject()时发生异常。也就是说,父类没有无参构造函数时,序列化正常进行,但反序列化时抛出newInvalidClassException异常。

2)B套

父类:Person类

            public class Person {
            	
            	public String name;
            	
            	public String gender;
            	
            	public int age;
            	
            	float height;
            	
            
            	public String getName() {
            		return name;
            	}
            
            	public void setName(String name) {
            		this.name = name;
            	}
            
            	public String getGender() {
            		return gender;
            	}
            
            	public void setGender(String gender) {
            		this.gender = gender;
            	}
            
            	public int getAge() {
            		return age;
            	}
            
            	public void setAge(int age) {
            		this.age = age;
            	}
            
            	public float getHeight() {
            		return height;
            	}
            
            	public void setHeight(float height) {
            		this.height = height;
            	}
            }

子类:Male类

        import java.io.Serializable;
        
        public class Male extends Person implements Serializable{
        	/**
        	 * 
        	 */
        	private static final long serialVersionUID = -7361904256653535728L;
        	
        	public boolean beard;
        	
        	protected String weight;
        	
        
        	public boolean havaBeard(int age){
        		boolean flag = false;
        		
        		if(age>=18){
        			flag = true;
        		}
        		return flag;
        	}
        
        
        	public boolean isBeard() {
        		return beard;
        	}
        
        
        	public void setBeard(boolean beard) {
        		this.beard = beard;
        	}
        
        
        	public String getWeight() {
        		return weight;
        	}
        
        
        	public void setWeight(String weight) {
        		this.weight = weight;
        	}
        	
        }

测试类:

        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.io.ObjectInputStream;
        import java.io.ObjectOutputStream;

        public class SubTypeSerializable {
        
        	public static void main(String[] args) throws IOException, ClassNotFoundException{
        		
        		/**Male继承父类Person,自身实现序列化接口,其父类Person没有实现序列化接口*/
        		FileOutputStream fos = new Fi
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值