编写高质量代码:改善Java的151个建议二:11-14(序列化相关内容)

该书籍PDF下载地址:http://download.csdn.net/download/muyeju/10001473

保存在磁盘上的对象文件包括2个部分:

  (1):类文件描述信息:包括包路径、继承关系、访问权限、变量描述、变量访问权限、方法签名、返回值、以及变量的关联类信息。

  (2):非瞬太(transient关键字)和非静态(static关键字)的实例变量值

11.养成良好习惯,显示声明UID

  当一个继承了Serializable接口,这个类就可以被序列化。继承Serializable的目的是为了可持久化,如果网络传输或本地存储,为系统的分布和异构部署提供先决支持条件,如果没有序列化,那么远程调用、对象数据库都不存在。

  序列化:就是将一个对象从内存块转换为可传输的数据流

  反序列化:将对象的数据流转换为一个实例对象

  问题:当序列化和反序列化所对于的版本不一致就会抛异常InvalidClassException,JVM不能将数据流转为实例对象。JVM是通过serialVersionUID来判断序列化和反序列化所对于的版本是否一致的,所以就需要声明serialVersionUID

  建议显示声明seriavalVersionUID,级,将鼠标指向该类,然后选则Add default serial version ID或者Add generated serial Version ID

  

  Add default serial version ID:其值是private static final long serialVersionUID = 1L;当对类进行了不兼容性修改时,需要修改UID,建议选择这种

  Add generated serial Version ID:如果修改了属性,不重新生成UID时,默认值是不会变的,也可以正常反序列化,但不推荐,毕竟UID的值与实际不符。 

  隐士声明UID:

12.在序列化的类中,不使用构造函数为final变量赋值

  序列化修饰的类中,如果final修饰的属性是一个直接量(不变量),在反序列化时就会重新计算其值。

  例:下列代码name如果重新修改了值,然后在反序列化,那么结果就是修改后的值

public class TestClass implements Serializable {
    private static final long serialVersionUID = 1L;
    private final String name = "张三" ;    
}

  但是,如果是通过构造方法来赋值,那么修改后的值是不会变的。因为反序列化时构造函数不会执行。

  例:如果修改了name的值,其值是不会改变的

public class TestClass implements Serializable {

    private static final long serialVersionUID = 1L;
    private final String name ;
    
    public TestClass(){
        this.name = "张三" ;
    }
    
}

  反序列化的过程:JVM从数据流中获取一个Object对象,然后根据数据流中的类文件描述信息,发现name是final变量,需要重新技术其值,于是就引用TestClass中的name值,但是此时JVM发现name没有赋值,不能引用,于是它就不在初始化,保存原值状态,所以name的值就不变

  规则:

    保持新旧对象的final变量相同,是序列化的基本规则之一

    反序列化时构造函数不会执行

13.反序列化时,final在以下情况不会被重新赋值

  (1):通过构造函数为final变量赋值 

  (2):通过方法返回值为final变量赋值

  (3):final修饰的属性不是基本类型

14.使用序列化的私有方法,巧妙解决部分属性持久化方法,private void writeObject(ObjectOutputStream out)和private void readObject(ObjectInputStream in)

  序列化机制:序列化回调。Java调用ObjectOutputStream类把一个对象转换成数据流时,会通过反射检查被序列化的类中是否有writeObject方法,且方法是私有、无返回的

                    如果有,就委托该方法进行序列化,没有,则由ObjectOutputSream按照默认规则继续序列化。同样,在由流数据恢复成实例对象时,也会检查是否有一个私有                             的readObject()方法,有就会通过该方法读取属性值。

public class TestClass implements Serializable {

    private static final long serialVersionUID = 1L;
    private String name ;
    private transient Salary salary ;
    
    public TestClass(String name,Salary salary){
        this.name = name ;
        this.salary = salary ;
    }
    
    private void writeObject(ObjectOutputStream out) throws IOException{
        out.defaultWriteObject(); 
        out.writeObject(salary.getBase());
    }
    
    private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException{
        in.defaultReadObject(); 
        salary = new Salary(in.readDouble(),0.0) ;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Salary getSalary() {
        return salary;
    }
    public void setSalary(Salary salary) {
        this.salary = salary;
    }
    
}

 

 

 

 

 

 

  

 

posted @ 2017-09-30 10:30  孟夏草木长  阅读(235)  评论(0编辑  收藏  举报