Java系列 - 序列化

一、为什么需要序列化

人类语言内容太丰富了(各种数据格式:图像、声音、文本、json/xml、java对象等),计算机要想去存储,肯定是要转化成它所能理解的某种方式。


image.png

视频、图像、声音这些内容我们一般使用编码的形式成为二进制流进行传输,比如通过H264/H265、JPEG、AAC等编码算法把这些让人类理解的视频图像声音内容转换成计算机可以理解的二进制数据,也方便计算机的存储和网络传输。
所以做翻译主要有两个目标:
1、本地保存:我们经常将一些信息保存在计算机内部;
2、网络传输:我们还会把一些信息传给网线另一侧的计算机。


image.png
为了完成让代码中的对象信息实现 本地保存 和 网络传输 两个目标,所以就需要序列化。也可以这么理解,凡是离开内存的对象信息都需要进行序列化。

二、什么是序列化和反序列化

广义上来说:

1、序列化:将内存中易于处理和阅读的数据格式转换称为二进制数据流或者文本流的过程。(序列化后的数据方便在网络上传输和在硬盘上存储)
2、反序列化:与序列化相反,是将二进制数据流或者文本流转换称为易于处理和阅读的数据格式的过程。

狭义点来说:

1、Java序列化:把Java对象(java bean)转换为字节序列(byte[])。
2、Java反序列化:把字节序列(byte[])恢复为原先的Java对象(java bean)。

三、序列化的基础使用

在Java中,如果一个对象要想实现序列化,必须要实现下面两个接口之一:
Serializable 接口
Externalizable 接口

1、 Serializable 接口

一个对象想要被序列化,那么它的类就要实现此接口或者它的子接口。
这个对象的所有属性(包括private属性、包括其引用的对象)都可以被序列化和反序列化来保存、传递。不想序列化的字段可以使用transient(临时的)修饰,static修饰的字段也不会被序列化。

在需要序列化的对象的Java类里加入writeObject()方法与readObject()方法可以控制如何序列化各属性,甚至完全不序列化某些属性或者加密序列化某些属性。

2、Externalizable 接口

它是Serializable接口的子类,用户要实现的writeExternal()和readExternal() 方法,用来决定如何序列化和反序列化。因为序列化和反序列化方法需要自己实现,因此可以指定序列化哪些属性,而transient在这里无效。

对Externalizable对象反序列化时,会先调用类的无参构造方法,这是有别于默认反序列方式。因此Externalizable对象必须有默认构造函数,而且必需是public的。

3、Serializable 和 Externalizable 对比

  • readExternal(),writeExternal()两个方法,这两个方法除了方法签名和readObject(),writeObject()两个方法的方法签名不同之外,其方法体完全一样。
  • 当使用Externalizable机制反序列化该对象时,程序会使用public的无参构造器创建实例,然后才执行readExternal()方法进行反序列化,因此实现Externalizable的序列化类必须提供public的无参构造。
  • 虽然实现Externalizable接口能带来一定的性能提升,但由于实现ExternaLizable接口导致了编程复杂度的增加,所以大部分时候都是采用实现Serializable接口方式来实现序列化。
4、Serializable接口有何用

我们点进Serializable接口内部查看,发现它竟然是一个空接口,并没有包含任何方法!
Serializable接口也仅仅只是做一个标记用!它告诉代码只要是实现了Serializable接口的类都是可以被序列化的!然而真正的序列化动作不需要靠它完成。
真正的序列化是靠这几个API完成的:ObjectOutputStream.writeObject();ObjectInputStream.readObject()


image.jpeg
5、serialVersionUID号有何用
private static final long serialVersionUID = -4392658638228508589L;

1、serialVersionUID是序列化前后的唯一标识符
serialVersionUID序列化ID,可以看成是序列化和反序列化过程中的版本号(“暗号”),在反序列化时,JVM会把字节流中的序列号ID和被序列化类中的序列号ID做比对,只有两者一致,才能重新反序列化,否则就会报异常来终止反序列化的过程。

2、默认如果没有人为显式定义过serialVersionUID,那编译器会为它自动声明一个!
如果在定义一个可序列化的类时,没有人为显式地给它定义一个serialVersionUID的话,则Java运行时环境会根据该类的各方面信息自动地为它生成一个默认的serialVersionUID,一旦像上面一样更改了类的结构或者信息,则类的serialVersionUID也会跟着变化!

四、演示Java序列化

public class SerialDemo {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
    //序列化
        FileOutputStream fos = new FileOutputStream("object.out");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        User user1 = new User("xuliugen", "123456", "male");
        oos.writeObject(user1);
        oos.flush();
        oos.close();
    //反序列化
        FileInputStream fis = new FileInputStream("object.out");
        ObjectInputStream ois = new ObjectInputStream(fis);
        User user2 = (User) ois.readObject();
        System.out.println(user2.getUserName()+ " " + 
            user2.getPassword() + " " + user2.getSex());
        //反序列化的输出结果为:xuliugen 123456 male
    }
}

public class User implements Serializable {
    private String userName;
    private String password;
    private String sex;
    //全参构造方法、get和set方法省略
}

五、Android中的序列化Parcelable

重点1

鉴于Serializable在内存序列化上开销比较大,而内存资源属于android系统中的稀有资源(android系统分配给每个应用的内存开销都是有限的),为此android中提供了Parcelable接口来实现序列化操作。

重点2

Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如通过Intent在activity间传输数据,而Parcelable的缺点就使用起来比较麻烦。

Parcelable接口的实现案例

public class Book implements Parcelable{
 private String bookName;
 private int publishDate;
 public Book(){
   
 }
 public String getBookName(){
  return bookName;
 }
 public void setBookName(String bookName){
  this.bookName = bookName;
 }
 public int getPublishDate(){
  return publishDate;
 }
 public void setPublishDate(int publishDate){
  this.publishDate = publishDate;
 }
  
 @Override
 public int describeContents(){
  return 0;
 }
  
 @Override
 public void writeToParcel(Parcel out, int flags){
  out.writeString(bookName);
  out.writeInt(publishDate);
 }
  
 public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){
   
  @Override
  public Book[] newArray(int size){
   return new Book[size];
  }
   
  @Override
  public Book createFromParcel(Parcel in){
   return new Book(in);
  }
 };
  
 public Book(Parcel in){
  bookName = in.readString();
  publishDate = in.readInt();
 }
}
重点3:简单用一句话概括来说就是通过writeToParcel将我们的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射成我们的对象。

也可以将Parcel看成是一个类似Serliazable的读写流,通过writeToParcel把对象写到流里面,在通过createFromParcel从流里读取对象,这个过程需要我们自己来实现并且写的顺序和读的顺序必须一致。

重点4:哪里会使用到Parcelable对象

1、通过Intent传递复杂类型


image.png

2、Bundle、Bitmap,它们本身实现了Parcelable序列化,因此我们可以方便地使用它们在组件间进行数据传递,当然Bundle本身也是一个类似键值对的容器,也可存储Parcelable实现类

public class ParceBean implements Parcelable{
    private  Bitmap dw;
    private String name;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Bitmap getDw() {
        return dw;
    }

    public void setDw(Bitmap dw) {
        this.dw = dw;
    }

     public static final Parcelable.Creator<ParceBean> CREATOR = new Creator<ParceBean>() { 
            public ParceBean createFromParcel(Parcel source) { 
                ParceBean pb = new ParceBean(); 
                pb.name = source.readString(); 
                pb.dw = Bitmap.CREATOR.createFromParcel(source);
                return pb; 
            } 
            public ParceBean[] newArray(int size) { 
                return new ParceBean[size]; 
            } 
        }; 
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeString(name);
        dw.writeToParcel(parcel, 0);
    }
}

Bitmap本身实现了Parcelable接口,利用writeToParccel之后可以用createFromParcel来rebuild这个Bitmap。

重点5:Parcelable 与 Serializable 区别
(1)两者的实现差异(Serializable比Parcelabel简单)

Serializable的实现,只需要实现Serializable接口即可。这只是给对象打了一个标记(UID),系统会自动将其序列化。而Parcelabel的实现,不仅需要实现Parcelabel接口,还需要在类中添加一个静态成员变量CREATOR,这个变量需要实现 Parcelable.Creator 接口,并实现读写的抽象方法。

(2)两者的设计初衷

Serializable的设计初衷是为了序列化对象到本地文件、数据库、网络流、RMI以便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Android的Parcelable的设计初衷是由于Serializable效率过低,消耗大,而android中数据传递主要是在内存环境中(内存属于android中的稀有资源),因此Parcelable的出现为了满足数据在内存中低开销而且高效地传递问题。

(3)两者效率选择(Serializable < Parcelable)

Serializable使用IO读写存储在硬盘上。序列化过程使用了反射技术,并且期间产生临时对象,优点代码少,在将对象序列化到存储设置中或将对象序列化后通过网络传输时建议选择Serializable。
Parcelable是直接在内存中读写,我们知道内存的读写速度肯定优于硬盘读写速度,所以Parcelable序列化方式性能上要优于Serializable方式很多。

重点6:最后一点,如何选择序列化方式

1、Android应用程序在内存间数据传输时推荐使用Parcelable,如activity间传输数据和AIDL数据传递。大多数情况下使用Serializable也是没什么问题的。
2、Parcelable也可以在网络中传输,只不过实现和操作过程过于麻烦并且为了防止android版本不同而导致Parcelable可能不同的情况,因此在序列化到存储设备或者网络传输方面还是尽量选择Serializable接口。

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

推荐阅读更多精彩内容