java中序列化和transient关键字使用小结

一、背景:Java中的对象序列化

1.对象序列化的类是有要求的,这个序列化的类必须实现一个接口Serializable,这个
接口没有任何方法声明,它是一个标识接口。

2.对象流可以将Java对象转换成二进制写入磁盘,这个过程通常叫做序列化。
并且还可以从磁盘读出完整的Java对象,而这个过程叫做反序列化。

3.序列化 ObjectOutputStream 把对象转成文件
反序列化 ObjectInputStream 把文件转成对象

4.序列化的意义
Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输,一般当我们使用缓存cache(内存空间不够有可能会本地存储到硬盘)或远程调用rpc(网络传输)的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化。

当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象,要不然序列化后干嘛呢,所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。

二、关于transient关键字

1.原理:
一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个的所有属性和方法都会自动序列化。

但是有种情况是有些属性是不需要序列化的,所以就用到transient这个关键字。只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

2.添加关键字transient,就是为了不被序列化。为什么要不被序列化呢?
(1)为了数据安全,避免序列化和反序列化。在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。

(2)节省存储空间。类中的字段值可以根据其它字段推导出来,如一个长方形类有三个属性:长度、宽度、面积。那么在序列化的时候,面积这个属性就没必要被序列化了,因为没有多大意义。

三、transient关键字使用实例

//import java.io.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class TransientTest{

public static void main(String args[]) throws Exception {

    //序列化之前的数据显示
    Rectangle rectangle = new Rectangle(3,4);
    System.out.println("1.原始对象\n"+rectangle);

    //开始序列化
    try{
        ObjectOutputStream o = new ObjectOutputStream(new        
                           FileOutputStream("rectangle"));

        // 往流写入对象,将Rectangle对象写入rectangle文件
        o.writeObject(rectangle);
        o.flush();
        o.close();
    }
    catch(FileNotFoundException e){
        //打印异常的堆栈信息到控制台
        e.printStackTrace();
    }
    catch(IOException e){
        e.printStackTrace();
    }

    //开始反序列化
    try{
        // 从流中读取对象
        ObjectInputStream in = new ObjectInputStream(new 
                           FileInputStream("rectangle"));

        //从流中读取Rectangle的数据
        Rectangle rectangle1 = (Rectangle)in.readObject();
        //输出反序列化的结果
        System.out.println("2.反序列化后的对象\n"+rectangle1);

        rectangle1.setArea();
        System.out.println("3.恢复成原始对象\n"+rectangle1);
        in.close();
    }
    catch(ClassNotFoundException e){
        e.printStackTrace();
    }
    catch(FileNotFoundException e){
        e.printStackTrace();
    }
    catch(IOException e){
        e.printStackTrace();
    }
}

}

/**
*建立矩形类
*/
class Rectangle implements Serializable{

private static final long serialVersionUID = 1710022455003682613L;
private Integer width;
private Integer height;
//在反序列化时,将无法恢复原来值  
private transient Integer area;

//矩形函数Rectangle()
public Rectangle (Integer width, Integer height){
    this.width = width;
    this.height = height;
    this.area = width * height;
}

//计算矩形面积
public void setArea(){
    this.area = this.width * this.height;
}

@Override
public String toString(){

//StringBuffer是动态字符串数组 ,它的对象是可以扩充和修改
//Stringbuffer有append()方法 
StringBuffer sb = new StringBuffer(40);
//append()追加信息,往动态字符串数组添加,类似于“+”
sb.append("width : ");
sb.append(this.width);
sb.append("\nheight : ");
sb.append(this.height);
sb.append("\narea : ");
sb.append(this.area);
return sb.toString();
}

}

输出:
1.原始对象
width : 3
height : 4
area : 12
2.反序列化后的对象
width : 3
height : 4
area : null
3.恢复成原始对象
width : 3
height : 4
area : 12

四、序列化与反序列化常用语句

1.开始序列化
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(“rectangle”));
其实就是:FileOutputStream f = new FileOutputStream(“rectangle”);
ObjectOutputStream o = new ObjectOutputStream(f)

2.开始反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream(“rectangle”));
其实就是:FileInputStream fi = new FileInputStream(“rectangle”);
ObjectInputStream in = new ObjectInputStream(fi)

五、小结

1.反序列化area为null,说明反序列化时根本没有从文件中获取到信息。

2.关于private static final long serialVersionUID = 1710022455003682613L;
serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。

3.当对象被序列化时,被transient关键字修饰的变量不会被序列化到目标文件。
当对象从序列化文件重构对象时(反序列化过程),被transient字段修饰的变量不会被恢复原来值。一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

4.transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。

5.被transient关键字修饰的变量不再能被序列化。一个静态变量不管是否被transient修饰,均不能被序列化,这里与该变量被static修饰后,transient就失效了无关。

6.类变量加不加transient关键字都不会参与序列化和反序列化。
如:private transient static int minorVer;
private static transient final int sf = 110;

7.对于private transient InputStream is; 这个字段如果用做序列化时,必须要加transient的,不然就会报错(java.io.NotSerializableException),因为InputStream没有实现可序列化的接口。加了后,这个字段就不会进行序列化,就与InputStream没有实现可序列化的接口不矛盾。

本篇博文是小白我在大神的肩膀上,吸收整理出来的。衷心感谢大神们的无私奉献、厚薄积发的精神,这种品质给我们小白在技术上的成长带来了很大的帮助。
参考:
https://www.cnblogs.com/chenpi/p/6185773.html
http://www.importnew.com/21517.html

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值