java序列化详解(一)

所谓对象的序列化,就是让对象可以保存。这里的保存是指保存到本地,你可以理解为文件也可以理解为网络中传输的流~

今天我们就来讲一下 java序列化的知识,弄懂这个就可以明白android中 对象序列化 ,activity之间对象的传递是怎么一回事。

新建一个person类

package com.burning.test;

import java.io.Serializable;

public class Person implements Serializable{

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", gender=" + gender
                + "]";
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    private String name;

    private int age;

    private Gender gender;

}

再新建一个gender类

package com.burning.test;

public class Gender {

    private int sex;

    @Override
    public String toString() {
        return "Gender [sex=" + sex + "]";
    }

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

}

最后看看我们的主类。

 1 package com.burning.test;
 2
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileNotFoundException;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 import java.io.ObjectInputStream;
 9 import java.io.ObjectOutputStream;
10
11 public class TestMain {
12
13     public static void main(String[] args) {
14         // TODO Auto-generated method stub
15         File file = new File("person.out");
16
17         try {
18             ObjectOutputStream oout = new ObjectOutputStream(
19                     new FileOutputStream(file));
20             Person person = new Person();
21             person.setAge(18);
22             person.setName("burning");
23             //Gender gender = new Gender();
24             ///gender.setSex(1);
25             ///person.setGender(gender);
26             oout.writeObject(person);
27             oout.close();
28
29             ObjectInputStream oin = new ObjectInputStream(new FileInputStream(
30                     file));
31             Object person2 = oin.readObject();
32             oin.close();
33             System.out.println(person2);
34
35         } catch (FileNotFoundException e) {
36             // TODO Auto-generated catch block
37             e.printStackTrace();
38         } catch (IOException e) {
39             // TODO Auto-generated catch block
40             e.printStackTrace();
41         } catch (ClassNotFoundException e) {
42             // TODO Auto-generated catch block
43             e.printStackTrace();
44         }
45
46     }
47
48 }

可以看一下 这个就是把person 这个对象保存在了一个文件中。

可以看一下23-25 这个被注释掉的语句,如果不注释掉,后果就是这段代码会抛出一个异常,告诉你要序列化的对象里面有个成员变量没有继承Serializable 这个接口。

所以如果你想把gender这个属性也序列化的话 就要让Gender这个类也继承这个Serializable 接口。

我们可以打开看一下这个保存对象的文件。

aced 0005 7400 5d50 6572 736f 6e20 5b6e
616d 653d 6275 726e 696e 672c 2061 6765
3d31 382c 2067 656e 6465 723d 6e75 6c6c
5d20 2044 3a5c 5573 6572 735c 6275 726e
696e 675c 776f 726b 7370 6163 655c 5465
7374 5072 6f6a 6563 745c 7065 7273 6f6e
2e6f 7574

实际上是二进制文件,而绝非我们设想的是文本文件。

当然了如果我们不想默认的序列化这个对象里的所有成员变量(序列化开销较大 应该选择性的序列化我们需要的变量 而不是默认全部序列化),应该怎么做?

我们只要把person类的 name声明改成

transient private String name;

即可。

这样序列化以后 这个name的值是不会进行序列化的 ,你取出来是空~~

我们甚至可以加上一个构造函数。

 1 package com.burning.test;
 2
 3 import java.io.Serializable;
 4
 5 public class Person implements Serializable {
 6
 7     /**
 8      *
 9      */
10     private static final long serialVersionUID = 1L;
11
12     public Person() {
13         // TODO Auto-generated constructor stub
14         System.out.println("person的构造函数");
15     }
16
17     public String getName() {
18         return name;
19     }
20
21     public void setName(String name) {
22         this.name = name;
23     }
24
25     public int getAge() {
26         return age;
27     }
28
29     public void setAge(int age) {
30         this.age = age;
31     }
32
33     transient private String name;
34
35     private int age;
36
37 }

我们发现 最终读取这个对象的时候 构造函数是没有走的。就好像直接从二进制-----------转成对象一样。

我们可以看一下 序列化 写入对象的源代码。

 1   /**
 2      * Underlying writeObject/writeUnshared implementation.
 3      */
 4     private void writeObject0(Object obj, boolean unshared)
 5         throws IOException
 6     {
 7         boolean oldMode = bout.setBlockDataMode(false);
 8         depth++;
 9         try {
10             // handle previously written and non-replaceable objects
11             int h;
12             if ((obj = subs.lookup(obj)) == null) {
13                 writeNull();
14                 return;
15             } else if (!unshared && (h = handles.lookup(obj)) != -1) {
16                 writeHandle(h);
17                 return;
18             } else if (obj instanceof Class) {
19                 writeClass((Class) obj, unshared);
20                 return;
21             } else if (obj instanceof ObjectStreamClass) {
22                 writeClassDesc((ObjectStreamClass) obj, unshared);
23                 return;
24             }
25
26             // check for replacement object
27             Object orig = obj;
28             Class cl = obj.getClass();
29             ObjectStreamClass desc;
30             for (;;) {
31                 // REMIND: skip this check for strings/arrays?
32                 Class repCl;
33                 desc = ObjectStreamClass.lookup(cl, true);
34                 if (!desc.hasWriteReplaceMethod() ||
35                     (obj = desc.invokeWriteReplace(obj)) == null ||
36                     (repCl = obj.getClass()) == cl)
37                 {
38                     break;
39                 }
40                 cl = repCl;
41             }
42             if (enableReplace) {
43                 Object rep = replaceObject(obj);
44                 if (rep != obj && rep != null) {
45                     cl = rep.getClass();
46                     desc = ObjectStreamClass.lookup(cl, true);
47                 }
48                 obj = rep;
49             }
50
51             // if object replaced, run through original checks a second time
52             if (obj != orig) {
53                 subs.assign(orig, obj);
54                 if (obj == null) {
55                     writeNull();
56                     return;
57                 } else if (!unshared && (h = handles.lookup(obj)) != -1) {
58                     writeHandle(h);
59                     return;
60                 } else if (obj instanceof Class) {
61                     writeClass((Class) obj, unshared);
62                     return;
63                 } else if (obj instanceof ObjectStreamClass) {
64                     writeClassDesc((ObjectStreamClass) obj, unshared);
65                     return;
66                 }
67             }
68
69             // remaining cases
70             if (obj instanceof String) {
71                 writeString((String) obj, unshared);
72             } else if (cl.isArray()) {
73                 writeArray(obj, desc, unshared);
74             } else if (obj instanceof Enum) {
75                 writeEnum((Enum) obj, desc, unshared);
76             } else if (obj instanceof Serializable) {
77                 writeOrdinaryObject(obj, desc, unshared);
78             } else {
79                 if (extendedDebugInfo) {
80                     throw new NotSerializableException(
81                         cl.getName() + "\n" + debugInfoStack.toString());
82                 } else {
83                     throw new NotSerializableException(cl.getName());
84                 }
85             }
86         } finally {
87             depth--;
88             bout.setBlockDataMode(oldMode);
89         }
90     }

注意看一下70-85行。 你就知道

 NotSerializableException  这个异常是哪来的,你也可以知道 array类型 string类型 enum 都是默认可以序列化的~~(查看这些类型的源代码你会发现都默认实现了Serializable接口)

到这里 我们如果想再次对name 进行序列化怎么办?除了 去掉那个关键字,其实还有一个办法。
 1 package com.burning.test;
 2
 3 import java.io.IOException;
 4 import java.io.ObjectInputStream;
 5 import java.io.ObjectOutputStream;
 6 import java.io.Serializable;
 7
 8 public class Person implements Serializable {
 9
10     /**
11      *
12      */
13     private static final long serialVersionUID = 1L;
14
15     public Person() {
16         // TODO Auto-generated constructor stub
17         System.out.println("person的构造函数");
18     }
19
20     @Override
21     public String toString() {
22         return "Person [name=" + name + ", age=" + age + "]";
23     }
24
25     public String getName() {
26         return name;
27     }
28
29     public void setName(String name) {
30         this.name = name;
31     }
32
33     public int getAge() {
34         return age;
35     }
36
37     public void setAge(int age) {
38         this.age = age;
39     }
40
41     transient private String name;
42
43     private int age;
44
45     private void writeObject(ObjectOutputStream os) {
46         try {
47             os.defaultWriteObject();
48         } catch (IOException e1) {
49             // TODO Auto-generated catch block
50             e1.printStackTrace();
51         }
52         try {
53             os.writeUTF(name);
54         } catch (IOException e) {
55             // TODO Auto-generated catch block
56             e.printStackTrace();
57         }
58     }
59
60     private void readObject(ObjectInputStream in) {
61         try {
62             in.defaultReadObject();
63         } catch (ClassNotFoundException e1) {
64             // TODO Auto-generated catch block
65             e1.printStackTrace();
66         } catch (IOException e1) {
67             // TODO Auto-generated catch block
68             e1.printStackTrace();
69         }
70         try {
71             name = in.readUTF();
72         } catch (IOException e) {
73             // TODO Auto-generated catch block
74             e.printStackTrace();
75         }
76     }
77
78 }
这个writeobject和readobject是私有方法,那么肯定就是在反射中进行调用的了。这个地方实际上和android里面的序列化已经是差不多的了。写法都类似。

可以看一下源代码objectoutputstream里的
 1   /**
 2      * Writes instance data for each serializable class of given object, from
 3      * superclass to subclass.
 4      */
 5     private void writeSerialData(Object obj, ObjectStreamClass desc)
 6         throws IOException
 7     {
 8         ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
 9         for (int i = 0; i < slots.length; i++) {
10             ObjectStreamClass slotDesc = slots[i].desc;
11             if (slotDesc.hasWriteObjectMethod()) {
12                 PutFieldImpl oldPut = curPut;
13                 curPut = null;
14                 SerialCallbackContext oldContext = curContext;
15
16                 if (extendedDebugInfo) {
17                     debugInfoStack.push(
18                         "custom writeObject data (class \"" +
19                         slotDesc.getName() + "\")");
20                 }
21                 try {
22                     curContext = new SerialCallbackContext(obj, slotDesc);
23                     bout.setBlockDataMode(true);
24                     slotDesc.invokeWriteObject(obj, this);
25                     bout.setBlockDataMode(false);
26                     bout.writeByte(TC_ENDBLOCKDATA);
27                 } finally {
28                     curContext.setUsed();
29                     curContext = oldContext;
30                     if (extendedDebugInfo) {
31                         debugInfoStack.pop();
32                     }
33                 }
34
35                 curPut = oldPut;
36             } else {
37                 defaultWriteFields(obj, slotDesc);
38             }
39         }
40     }
这个方法 一目了然。

当然了我们还可以用Externalizable 这个接口来序列化。这个接口序列化的话 就和android里面是完全一样的。需要你自己制定序列化的细节。
 1 package com.burning.test;
 2
 3 import java.io.Externalizable;
 4 import java.io.IOException;
 5 import java.io.ObjectInput;
 6 import java.io.ObjectInputStream;
 7 import java.io.ObjectOutput;
 8 import java.io.ObjectOutputStream;
 9 import java.io.Serializable;
10
11 public class Person implements Externalizable {
12
13     /**
14      *
15      */
16     private static final long serialVersionUID = 1L;
17
18     public Person() {
19         // TODO Auto-generated constructor stub
20         System.out.println("person的构造函数");
21     }
22
23     @Override
24     public String toString() {
25         return "Person [name=" + name + ", age=" + age + "]";
26     }
27
28     public String getName() {
29         return name;
30     }
31
32     public void setName(String name) {
33         this.name = name;
34     }
35
36     public int getAge() {
37         return age;
38     }
39
40     public void setAge(int age) {
41         this.age = age;
42     }
43
44      private String name;
45
46     private int age;
47
48
49     //这2个方法不写的话 是没有序列化的 也就是说这个序列化接口需要你自己去实现序列化的细节。
50     @Override
51     public void writeExternal(ObjectOutput out) throws IOException {
52         // TODO Auto-generated method stub
53
54     }
55
56     @Override
57     public void readExternal(ObjectInput in) throws IOException,
58             ClassNotFoundException {
59         // TODO Auto-generated method stub
60
61     }
62
63
64
65 }

这个地方要注意 用这种接口的时候在反序列化的时候 是调用了构造函数的~~可以打日志自己看一下。

在这里要额外提一下。如果单例对象也要实例化的话要注意一下 增加一个readResolve() 这样的方法。

不然你序列化的单例 再反序列化出来以后 会发现这是2个对象 并不相等。

				
时间: 2024-08-30 00:43:39

java序列化详解(一)的相关文章

java 序列化详解(二)

接着前面的文章说,当任何一个类继承Serializable 这个接口时,Eclipse经常会有黄色惊叹号提示. 提示内容如下: The serializable class Person does not declare a static final serialVersionUID field of type long 点开以后有2个选择 一个是 Adds a default serial version ID to the selected type. Use this option to

java关键字详解

Java关键字及其作用 目录 Java关键字及其作用--- 1 一.     关键字总览:2 二.     详细解释--- 3 1.访问控制--- 3 1)私有的-- 3      private 2)受保护的-- 3      protected 3)公共的-- 3      public 2.类.方法和变量修饰符--- 3 1)声明抽象-- 3      abstract 2)类-- 4      class 3)继承.扩展-- 4      extends 4)最终.不可改变-- 4   

Java集合详解7:HashSet,TreeSet与LinkedHashSet

Java集合详解7:HashSet,TreeSet与LinkedHashSet 今天我们来探索一下HashSet,TreeSet与LinkedHashSet的基本原理与源码实现,由于这三个set都是基于之前文章的三个map进行实现的,所以推荐大家先看一下前面有关map的文章,结合使用味道更佳. 具体代码在我的GitHub中可以找到 https://github.com/h2pl/MyTech 文章首发于我的个人博客: https://h2pl.github.io/2018/05/12/colle

package-info.java文件详解

package-info.java文件详解 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 一.pacakge-info.java介绍 pacakge-info.java是一个Java文件,可以添加到任何的Java源码包中.pacakge-info.java的目标是提供一个包级的文档说明或者是包级的注释. pacakge-info.java文件中,唯一要求包含的内容是包的声明语句,比如: package com.ch.service; 二.包文档 在

java 反射 详解

本文来自:blog.csdn.net/ljphhj JAVA反射机制:   通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们. 理论的东东太多也没用,下面我们看看实践 Demo - Demo: package cn.lee.demo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import

Java synchronized详解

Java synchronized详解 第一篇: 使用synchronized 在编写一个类时,如果该类中的代码可能运行于多线程环境下,那么就要考虑同步的问题.在Java中内置了语言级的同步原语--synchronized,这也大大简化了Java中多线程同步的使用.我们首先编写一个非常简单的多线程的程序,是模拟银行中的多个线程同时对同一个储蓄账户进行存款.取款操作的. 在程序中我们使用了一个简化版本的Account类,代表了一个银行账户的信息.在主程序中我们首先生成了1000个线程,然后启动它们

Java虚拟机详解——JVM常见问题总结

[正文] 声明:本文只是做一个总结,有关jvm的详细知识可以参考之前的系列文章,尤其是那篇:Java虚拟机详解04--GC算法和种类.那篇文章和本文是面试时的重点. 面试必问关键词:JVM垃圾回收.类加载机制. 先把本文的目录画一个思维导图:(图的源文件在本文末尾) 一.Java引用的四种状态: 强引用:  用的最广.我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它,这就是强引用. * 如果一个对象具有强引用,那垃圾回收器绝不会回收它*.当内存空间不足,Java虚拟机宁

Java引用类型详解

JVM  的垃圾回收器对于不同类型的引用有不同的处理方式.java中对于一个对象来说,只要有引用的存在,它就会一直存在于内存中.如果这样的对象越来越多,超出了JVM中的内存总数,JVM就会抛出OutOfMemory错误.虽然垃圾回收的具体运行是由JVM来控制的,但是开发人员仍然可以在一定程度上与垃圾回收器进行交互,其目的在于更好的帮助垃圾回收器管理好应用的内存.这种交互方式就是使用JDK1.2 引入的  java.lang.ref包. 强引用(strong reference) 在一般的 Jav

Java堆栈详解 .

1. Java中堆栈(stack)和堆(heap) (1)内存分配的策略 按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不 允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的