Java-序列化-反序列化

Thanks

Java基础学习总结——Java对象的序列化和反序列化
java序列化反序列化原理
Java 序列化的高级认识
Java中的关键字 transient

Java中的序列化

对象是存储在内存中,但如果我们想把对象持久化存到硬盘上该怎么做呢?在Java中,可以使用序列化:Serialization(序列化)是一种将对象以一连串的字节描述的过程;反序列化deserialization是一种将这些字节重建成一个对象的过程。

简单例子

Java中必须实现接口Serializable才能够被序列化,而Serializable接口没有方法,更像是个标记。 有了这个标记的Class就能被序列化机制处理。

简单定义一个需要序列化的类:

public class Person implements Serializable{
    public int height = 180;
    private int age = 21;
}

然后进行序列化

public class Main {
    public static void main(String args[]) throws IOException {
        FileOutputStream fos = new FileOutputStream("temp.out");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        Person ts = new Person();
        oos.writeObject(ts);
        oos.flush();
        oos.close();
    }
}

序列化后保存到一份文件里面,文件内容如下,这些内容是有特定含义,

aced 0005 7372 0022 736f 7572 6365 436f
6465 2e74 6573 7453 6572 6961 6c69 7a61
626c 652e 5065 7273 6f6e e400 6d6d fbe4
589a 0200 0249 0003 6167 6549 0006 6865
6967 6874 7870 0000 0015 0000 00b4

一堆的16进制,保存的就是对象的属性了,如果我们再反序列话出来,就得到原来的对象。

public static void main(String args[]) throws IOException, ClassNotFoundException {
    FileInputStream fis = new FileInputStream("temp.out");
    ObjectInputStream oin = new ObjectInputStream(fis);
    Person person = (Person) oin.readObject();
    System.out.println("height="+person.height);
}

SerialVersionUID

对于每一个序列化的类,虚拟机会维护其一个对应的UID,相当于一个标识。如果没有显式地去声明UID,则在修改了类后,其UID会改变,会导致修改前的类反序列化失败,抛出ClassNotFoundException的错误。

例如,上面序列化的类:

public class Person implements Serializable{
    public int height = 180;
    private int age = 21;
}

及其对应的序列化文件:

aced 0005 7372 0022 736f 7572 6365 436f
6465 2e74 6573 7453 6572 6961 6c69 7a61
626c 652e 5065 7273 6f6e e400 6d6d fbe4
589a 0200 0249 0003 6167 6549 0006 6865
6967 6874 7870 0000 0015 0000 00b4

我们稍微修改一下类:

public class Person implements Serializable{
    public int height = 180;
    private int age = 21;

    @Override
    public String toString() {
        return super.toString();
    }
}

然后反序列化,发现报错了:

Exception in thread "main" java.io.InvalidClassException: sourceCode.testSerializable.Person; local class incompatible: stream classdesc serialVersionUID = -2017492313917073254, local class serialVersionUID = 9042608728172191849
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1630)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)

UID不一致,虚拟机认为这两个类是不一样的,抛出异常。序列化 ID 在 Eclipse 下提供了两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。

静态变量序列化

增加一个静态的成员:

public class Person implements Serializable{

    private static final long serialVersionUID = 1L;
    public static int skin = 1;
    public int height = 180;
    public int age = 21;

    @Override
    public String toString() {
        return "Person{" +
                "height=" + height +
                ", age=" + age +
                ", skin=" + skin +
                '}';
    }
}

序列化前修改一下:

FileOutputStream fos = new FileOutputStream("temp.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Person person = new Person();
person.height = 170;
person.age = 25;
person.skin = 3;
oos.writeObject(person);
oos.flush();
oos.close();

反序列化:

FileInputStream fis = new FileInputStream("temp.out");
ObjectInputStream oin = new ObjectInputStream(fis);
Person person = (Person) oin.readObject();
System.out.println(person.toString());
//输出:
Person{height=170, age=25, skin=1}

可见,反序列化出来的静态成员不是序列化前的那个值。序列化时,并不保存静态变量,我们这样去理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

Transient关键字

Java中transient关键字的作用,简单地说,就是让某些被修饰的成员属性变量不被序列化,这一看好像很好理解,就是不被序列化,那么什么情况下,一个对象的某些字段不需要被序列化呢?如果有如下情况,可以考虑使用关键字transient修饰:

1、类中的字段值可以根据其它字段推导出来,如一个长方形类有三个属性:长度、宽度、面积(示例而已,一般不会这样设计),那么在序列化的时候,面积这个属性就没必要被序列化了;

2、其它,看具体业务需求吧,哪些字段不想被序列化

序列化加密

序列化和反序列化的时候,如果用户有重写对象类里面的 writeObjectreadObject 方法,则虚拟机直接调用它,反之默认调用默认调用是 ObjectOutputStreamdefaultWriteObject 方法以及 ObjectInputStreamdefaultReadObject 方法。

那我们可以定义自己的规则来达到一个序列化时候的加密和反序列化时候解密。来测试一下:

public class Person implements Serializable{
    private static final long serialVersionUID = 1L;
    private transient final String psdStr = "psdStr";
    public String name;
    public int height = 180;
    public int age = 21;
    @Override
    public String toString() {
        return "Person{" +
                "name=" + name +
                ", height=" + height +
                ", age=" + age +
                '}';
    }
    private void writeObject(ObjectOutputStream out) {
        try {
            ObjectOutputStream.PutField putFields = out.putFields();
            putFields.put("name", EncoderUtils.AESEncode(psdStr,String.valueOf(name)));
            putFields.put("height", height*1234);
            putFields.put("age", age*1234);
            out.writeFields();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void readObject(ObjectInputStream in) {
        try {
            ObjectInputStream.GetField readFields = in.readFields();
            name = EncoderUtils.AESDecode(psdStr, (String) readFields.get("name", ""));
            height = (readFields.get("height", 0))/1234;
            age = (readFields.get("age", 0))/1234;
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

加密:

FileOutputStream fos = new FileOutputStream("temp.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Person person = new Person();
person.name = "王尼玛";
person.height = 190;
person.age = 28;
oos.writeObject(person);
oos.flush();
oos.close();

解密:

FileInputStream fis = new FileInputStream("temp.out");
ObjectInputStream oin = new ObjectInputStream(fis);
Person person = (Person) oin.readObject();
System.out.println(person.toString());
//
Person{name=王尼玛, height=190, age=28}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容

  • JAVA序列化机制的深入研究 对象序列化的最主要的用处就是在传递,和保存对象(object)的时候,保证对象的完整...
    时待吾阅读 10,868评论 0 24
  • 一、Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计...
    子非鱼_t_阅读 4,195评论 1 44
  • 一、序列化 1、序列化的作用 Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于...
    慕凌峰阅读 4,060评论 0 8
  • 人永远是一个奇怪的动物,他不是追求最好的,而是寻找生命里所缺的。敬老院社工开出的工资和福利根本不好,可我为...
    隔水望伊人阅读 544评论 1 50
  • 美人一旦跟政治沾上边,就免不了要成为祸水。 明朝末年,一个朝代即使在走向终点时,散发出的腐朽气息已随处可闻,可依旧...
    何潇湘阅读 1,416评论 6 13