将数据结构或对象转换成二进制串的过程。
将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
不同的计算机语言中,数据结构,对象以及二进制串的表示方式并不相同。
数据结构和对象:对于类似 Java 这种完全面向对象的语言,工程师所操作的一切都是对象(Object),来自于类的实例化。在 Java 语言中最接近数据结构的概念,就是 POJO(Plain Old Java Object)或者 Javabean--那些只有 setter/getter 方法的类。而在 C 二进制串:序列化所生成的二进制串指的是存储在内存中的一块数据。C 语言的字符串可以直接被传输层使用,因为其本质上就是以’0’结尾的存储在内存中的二进制串。在 Java 语言里面,二进制串的概念容易和 String 混淆。实际上String 是 Java 的一等公民,是一种特殊对象(Object)。对于跨语言间的通讯,序列化后的数据当然不能是某种语言的特殊数据类型。二进制串在 Java 里面所指的是 byte[],byte 是 Java 的 8 中原生数据类型之一(Primitive data types)。
简单的概括:
具体的讲:
性能包括两个方面,时间复杂度和空间复杂度。
移动互联时代,业务系统需求的更新周期变得更快,新的需求不断涌现,而老的系统还是需要继续维
护。如果序列化协议具有良好的可扩展性,支持自动增加新的业务字段,而不影响老的服务,这将大大
提供系统的灵活度。
在序列化选型的过程中,安全性的考虑往往发生在跨局域网访问的场景。当通讯发生在公司之间或者跨
机房的时候,出于安全的考虑,对于跨局域网的访问往往被限制为基于 HTTP/HTTPS 的 80 和 443 端
口。如果使用的序列化协议没有兼容而成熟的 HTTP 传输层框架支持,可能会导致以下三种结果之一:
注意点:Android的Parcelable也有安全漏洞
最近几个月,Android安全公告公布了一系列系统框架层的高危提权漏洞,如下表所示。
https://www.anquanke.com/post/id/103570
XML 是一种常用的序列化和反序列化协议,具有跨机器,跨语言等优点,SOAP(Simple Object
Access protocol) 是一种被广泛应用的,基于 XML 为序列化和反序列化协议的结构化消息传递协议
JSON 起源于弱类型语言 Javascript, 它的产生来自于一种称之为"Associative array"的概念,其本质是
就是采用"Attribute-value"的方式来描述对象。实际上在 Javascript 和 PHP 等弱类型语言中,类的描
述方式就是 Associative array。JSON 的如下优点,使得它快速成为最广泛使用的序列化协议之一。
Protobuf 具备了优秀的序列化协议的所需的众多典型特征。
是 Java 提供的序列化接口,它是一个空接口:
public interface Serializable {
}
Serializable 用来标识当前类可以被 ObjectOutputStream 序列化,以及被 ObjectInputStream 反序列
化。
public class Student implements Serializable {
//serialVersionUID唯一标识了一个可序列化的类
private static final long serialVersionUID = -2100492893943893602L;
private String name;
private String sax;
private Integer age;
//Course也需要实现Serializable接口
private List<Course> courses;
//用transient关键字标记的成员变量不参与序列化(在被反序列化后,transient 变量的值被 设为初始值,如 int 型的是 0,对象型的是 null)
private transient Date createTime;
//静态成员变量属于类不属于对象,所以不会参与序列化(对象序列化保存的是对象的“状态”,也 就是它的成员变量,因此序列化不会关注静态变量)
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
public Student() {
System.out.println("Student: empty");
}
public Student(String name, String sax, Integer age) {
System.out.println("Student: " + name + " " + sax + " " + age);
this.name = name;
this.sax = sax;
this.age = age;
courses = new ArrayList<>();
createTime = new Date();
}
...
}
////Course也需要实现Serializable接口
public class Course implements Serializable {
private static final long serialVersionUID = 667279791530738499L;
private String name;
private float score;
...
}
Serializable 有以下几个特点:
Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。不修改这个变量值的序列化实体都可以相互进行序列化和反序列化。
设置方式
在JDK中,可以利用JDK的bin目录下的serialver.exe工具产生这个serialVersionUID,对于Test.class,执行命令:serialver Test
兼容性问题
为了在反序列化时,确保类版本的兼容性,最好在每个要序列化的类中加入 private static final
long serialVersionUID这个属性,具体数值自己定义。这样,即使某个类在与之对应的对象已经
序列化出去后做了修改,该对象依然可以被正确反序列化。否则,如果不显式定义该属性,这个属
性值将由JVM根据类的相关信息计算,而修改后的类的计算结果与修改前的类的计算结果往往不
同,从而造成对象的反序列化因为类版本不兼容而失败。
不显式定义这个属性值的另一个坏处是,不利于程序在不同的JVM之间的移植。因为不同的编译器
实现该属性值的计算策略可能不同,从而造成虽然类没有改变,但是因为JVM不同,出现因类版本
不兼容而无法正确反序列化的现象出现
因此 JVM 规范强烈 建议我们手动声明一个版本号,这个数字可以是随机的,只要固定不变就可以。同
时最好是 private 和 final 的,尽量保证不变。
public interface Externalizable extends Serializable {
void writeExternal(ObjectOutput var1) throws IOException;
void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException;
}
简单使用
public class Course1 implements Externalizable {
private static final long serialVersionUID = 667279791530738499L;
private String name;
private float score;
public Course1() {
System.out.println("Course: empty");
}
public Course1(String name, float score) {
System.out.println("Course: " + name + " " + score);
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
@Override
public void writeExternal(ObjectOutput objectOutput) throws IOException {
System.out.println("writeExternal");
objectOutput.writeObject(name);
objectOutput.writeFloat(score);
}
@Override
public void readExternal(ObjectInput objectInput) throws IOException, ClassNotFoundException {
System.out.println("readExternal");
name = (String)objectInput.readObject();
score = objectInput.readFloat();
}
@Override
public String toString() {
return "Course{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
public static void main(String... args) throws Exception {
//TODO:
Course1 course = new Course1("英语", 12f);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(course);
byte[] bs = out.toByteArray();
oos.close();
System.out.println("============反序列化=============");
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bs));
Course1 course1 = (Course1) ois.readObject();
System.out.println("course1: " + course1);
}
}
Serializable 的序列化与反序列化分别通过 ObjectOutputStream 和 ObjectInputStream 进行
/**
* 序列化对象
*
* @param obj
* @param path
* @return
*/
synchronized public static boolean saveObject(Object obj, String path) {
if (obj == null) {
return false;
}
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(path));// 创建序列化流对象
oos.writeObject(obj);//序列化
oos.close();
return true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close(); // 释放资源
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
/**
* 反序列化对象
*
* @param path
* @param
* @return
*/
@SuppressWarnings("unchecked ")
synchronized public static <T> T readObject(String path) {
ObjectInputStream ojs = null;
try {
ojs = new ObjectInputStream(new FileInputStream(path));// 创建反序列化对象
return (T) ojs.readObject();// 还原对象
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ojs!=null){
try {
ojs.close();// 释放资源
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
序列化算法一般会按步骤做如下事情:
格式化后以二进制打开
aced 0005 7372 002e 636f 6d2e 7a65 726f
2e73 6572 6961 6c69 7a61 626c 6564 656d
6f2e 7365 7269 616c 697a 6162 6c65 2e53
7475 6465 6e74 e2d9 8cd7 833d f19e 0200
044c 0003 6167 6574 0013 4c6a 6176 612f
6c61 6e67 2f49 6e74 6567 6572 3b4c 0007
636f 7572 7365 7374 0010 4c6a 6176 612f
7574 696c 2f4c 6973 743b 4c00 046e 616d
6574 0012 4c6a 6176 612f 6c61 6e67 2f53
7472 696e 673b 4c00 0373 6178 7100 7e00
0378 7073 7200 116a 6176 612e 6c61 6e67
2e49 6e74 6567 6572 12e2 a0a4 f781 8738
0200 0149 0005 7661 6c75 6578 7200 106a
6176 612e 6c61 6e67 2e4e 756d 6265 7286
ac95 1d0b 94e0 8b02 0000 7870 0000 0012
7372 0013 6a61 7661 2e75 7469 6c2e 4172
7261 794c 6973 7478 81d2 1d99 c761 9d03
0001 4900 0473 697a 6578 7000 0000 0277
0400 0000 0273 7200 2d63 6f6d 2e7a 6572
6f2e 7365 7269 616c 697a 6162 6c65 6465
6d6f 2e73 6572 6961 6c69 7a61 626c 652e
436f 7572 7365 0942 a76f 5bfc 8343 0200
0246 0005 7363 6f72 654c 0004 6e61 6d65
7100 7e00 0378 7042 b466 6674 0006 e8af
ade6 9687 7371 007e 000a 42b2 999a 7400
06e6 95b0 e5ad a678 7400 045a 6572 6f74
0003 e794 b7
以 oos.writeObject(obj) 为例分析:
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
...
}
public final void writeObject(Object obj) throws IOException {
//enableOverride=false,不走这里
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {//一般情况都走这里
writeObject0(obj, false);
...
}
/**
* Underlying writeObject/writeUnshared implementation.
*/
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
...
// remaining cases
// BEGIN Android-changed: Make Class and ObjectStreamClass replaceable.
if (obj instanceof Class) {
writeClass((Class) obj, unshared);
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
// END Android-changed: Make Class and ObjectStreamClass replaceable.
} else if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
//看这里
writeOrdinaryObject(obj, desc, unshared);
} else { //如果没有实现Serializable接口,会报 NotSerializableException
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
...
}
/**
* Writes representation of a "ordinary" (i.e., not a String, Class,
* ObjectStreamClass, array, or enum constant) serializable object to the
* stream.
*/
private void writeOrdinaryObject(Object obj,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
...
if (desc.isExternalizable() && !desc.isProxy()) {
//如果对象实现了Externalizable接口,那么执行 writeExternalData((Externalizable) obj)方法
writeExternalData((Externalizable) obj);
} else {
//如果对象实现的是Serializable接口,那么执行的是 writeSerialData(obj, desc)
writeSerialData(obj, desc);
}
...
}
/**
* Writes instance data for each serializable class of given object, from
* superclass to subclass.
*/
private void writeSerialData(Object obj, ObjectStreamClass desc)
throws IOException
{
...
if (slotDesc.hasWriteObjectMethod()) {
...
//如果writeObjectMethod != null(目标类中定义了私有的writeObject 方法),那么将调用目标类中的writeObject方法
slotDesc.invokeWriteObject(obj, this);
...
} else {
//如果如果writeObjectMethod == null, 那么将调用默认的 defaultWriteFields方法来读取目标类中的属性
defaultWriteFields(obj, slotDesc);
}
}
}
//ObjectStreamClass.java
/**
* Creates local class descriptor representing given class.
*/
private ObjectStreamClass(final Class<?> cl) {
...
if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[] { ObjectOutputStream.class },
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[] { ObjectInputStream.class },
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
writeReplaceMethod = getInheritableMethod(cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(cl, "readResolve", null, Object.class);
return null;
...
}
/**
* Returns true if represented class is serializable (but not
* externalizable) and defines a conformant writeObject method. Otherwise,
* returns false.
*/
boolean hasWriteObjectMethod() {
requireInitialized();
return (writeObjectMethod != null);
}
//多引用写入问题
fun test4() {
var course = Course("英语", 12f)
val out = ByteArrayOutputStream()
val oos = ObjectOutputStream(out)
oos.writeObject(course)
course.score = 78f //更改score值后重新序列化
//错误方案
fun error() {
oos.writeObject(course)//这样子写有问题
//结果 反序列化course1: Course(name=英语, score=12.0)
//反序列化course2: Course(name=英语, score=12.0)
}
//方案一:Ok
fun resolver1() {
oos.writeUnshared(course)
}
//或者 方案二
fun resolver2() {
oos.reset()
oos.writeObject(course)
}
// error()
resolver1()
val bs = out.toByteArray()
oos.close()
val ois = ObjectInputStream(ByteArrayInputStream(bs))
course = ois.readObject() as Course
println("反序列化course1: $course")
course = ois.readObject() as Course
println("反序列化course2: $course")
}
public class Person {
private static final long serialVersionUID = 1865725274925268509L;
private String name;
private String sax;
// public Person() {
// }
public Person(String name, String sax) {
this.name = name;
this.sax = sax;
}
}
public class Student1 extends Person implements Serializable {
private static final long serialVersionUID = -2100492893943893602L;
private Integer age;
private List<Course> courses;
...
public Student1(String name, String sax, Integer age) {
super(name,sax);
System.out.println("Student: " + name + " " + sax + " " + age);
this.age = age;
...
}
public static void main(String ... args) throws Exception{
//TODO:
Student1 student = new Student1("Zero", "男", 18);
student.addCourse(new Course("语文", 90.2f));
student.addCourse(new Course("数学", 89.3f));
//序列化
byte[] bytes = SerializeableUtils.serialize(student);
System.out.println(Arrays.toString(bytes));
//反序列化
//在readObject时抛出java.io.NotSerializableException异常。
//需要Person添加一个无参数构造器
Student1 student1 = SerializeableUtils.deserialize(bytes);
System.out.println("Student: " + student1);
}
}
在readObject时抛出java.io.NotSerializableException异常。
在类中增加 readObject 和 writeObject 方法可以实现自定义序列化策略,虽然这俩方法不是被显示调用,但是因为在使用ObjectOutputStream的writeObject方法和ObjectInputStream的readObject方法时,会通过反射的方式调用到它们。
“只有当你自行设计的自定义序列化形式与默认的序列化形式基本相同时,才能接受默认的序列化形式”.“当一个对象的物理表示方法与它的逻辑数据内容有实质性差别时,使用默认序列化形式有N种缺陷”.
其实从effective java的角度来讲,是强烈建议我们重写的,这样有助于我们更好地把控序列化过程,防范未知风险。
/**
* 默认方法readResolve
*/
public class Course3 implements Serializable {
private static final long serialVersionUID = 667279791530738499L;
private String name;
private float score;
public Course3() {
System.out.println("Course: empty");
}
public Course3(String name, float score) {
System.out.println("Course: " + name + " " + score);
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException, IOException {
System.out.println("readObject");
inputStream.defaultReadObject();
name = (String)inputStream.readObject();
score = inputStream.readFloat();
}
private void writeObject(ObjectOutputStream outputStream) throws IOException {
System.out.println("writeObject");
outputStream.defaultWriteObject();
outputStream.writeObject(name);
outputStream.writeFloat(score);
}
private Object readResolve() {
System.out.println("readResolve");
return new Course3(name, 85f);
}
private Object writeReplace(){
System.out.println("writeReplace");
return new Course3(name +"replace",score);
}
@Override
public String toString() {
return "Course{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
public static void main(String... args) throws Exception {
//TODO:
Course3 course = new Course3("英语", 12f);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(course);
byte[] bs = out.toByteArray();
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bs));
Course3 course1 = (Course3) ois.readObject();
System.out.println("course1: " + course1);
}
}
执行结果:
Course: 英语 12.0
writeReplace Course: 英语replace 12.0
writeObject
readObject
readResolve
Course: 英语replace 85.0
course1: Course{name='英语replace', score=85.0}
writeReplace 先于writeObject
readResolve后于readObject
public class SingleTest {
static final String CurPath = System.getProperty("user.dir");
public static void main(String ... args) throws Exception {
//TODO:
Single instance = Single.getInstance();
System.out.println(instance.hashCode());
System.out.println(copyInstance(instance).hashCode());
System.out.println("=================反射======================");
//使用反射方式直接调用私有构造器
Class<Single> clazz = (Class<Single>)Class.forName("com.zero.serializabledemo.serializable.Single");
Constructor<Single> con = clazz.getDeclaredConstructor(null);
con.setAccessible(true);//绕过权限管理,即在true的情况下,可以通过构造函数新建对象
Single instance1 = con.newInstance();
Single instance2 = con.newInstance();
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
private static Single copyInstance(Single instance) throws Exception{
//序列化会导致单例失效
FileOutputStream fos = new FileOutputStream(CurPath+"/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(CurPath+"/a.txt"));
Single single2 = (Single)ois.readObject();
oos.close();
ois.close();
return single2;
}
}
class Single implements Serializable {
private static final long serialVersionUID = 1L;
private static boolean flag = false;
private Single(){
synchronized (Single.class) {
if (!flag) {
flag = true;
} else {
throw new RuntimeException("单例模式被侵犯!");
}
}
}
private static Single single;
public static Single getInstance(){
if ( single == null ) {
synchronized (Single.class) {
if ( single == null ) {
single = new Single();
}
}
}
return single;
}
//如果不重写readResolve,会导致单例模式在序列化->反序列化后失败
private Object readResolve() {
return single;
}
}
介绍Parcelable不得不先提一下Serializable接口,Serializable是Java为我们提供的一个标准化的序列化
接口,那什么是序列化呢? ---- 简单来说就是将对象转换为可以传输的二进制流(二进制序列)的过程,这样
我们就可以通过序列化,转化为可以在网络传输或者保存到本地的流(序列),从而进行传输数据 ,那反序列
化就是从二进制流(序列)转化为对象的过程.
Parcelable是Android为我们提供的序列化的接口,Parcelable相对于Serializable的使用相对复杂一些,但
Parcelable的效率相对Serializable也高很多,这一直是Google工程师引以为傲的,有时间的可以看一下
Parcelable和Serializable的效率对比 Parcelable vs Serializable 号称快10倍的效率
Parcelable是Android SDK提供的,它是基于内存的,由于内存读写速度高于硬盘,因此Android中的
跨进程对象的传递一般使用Parcelable
package com.zero.xuliehuademo.parcelable;
import android.os.Parcel;
import android.os.Parcelable;
public class Course implements Parcelable {
private String name;
private float score;
public Course() {
System.out.println("_Course: empty");
}
public Course(String name, float score) {
System.out.println("_Course: " + name + " " + score);
this.name = name;
this.score = score;
}
...
/**
* 描述当前 Parcelable 实例的对象类型
* 比如说,如果对象中有文件描述符,这个方法就会返回上面的 CONTENTS_FILE_DESCRIPTOR
* 其他情况会返回一个位掩码
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 将对象转换成一个 Parcel 对象
* @param dest 表示要写入的 Parcel 对象
* @param flags 示这个对象将如何写入
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeFloat(this.score);
}
protected Course(Parcel in) {
this.name = in.readString();
this.score = in.readFloat();
}
/**
* 实现类必须有一个 Creator 属性,用于反序列化,将 Parcel 对象转换为 Parcelable
* @param
*/
public static final Parcelable.Creator<Course> CREATOR = new Parcelable.Creator<Course>() {
//反序列化的方法,将Parcel还原成Java对象
@Override
public Course createFromParcel(Parcel source) {
return new Course(source);
}
//提供给外部类反序列化这个数组使用。
@Override
public Course[] newArray(int size) {
return new Course[size];
}
};
}
在介绍之前我们需要先了解Parcel是什么?Parcel翻译过来是打包的意思,其实就是包装了我们需要传输
的数据,然后在Binder中传输,也就是用于跨进程传输数据
简单来说,Parcel提供了一套机制,可以将序列化之后的数据写入到一个共享内存中,其他进程通过
Parcel可以从这块共享内存中读出字节流,并反序列化成对象,下图是这个过程的模型。
Parcel可以包含原始数据类型(用各种对应的方法写入,比如writeInt(),writeFloat()等),可以包含
Parcelable对象,它还包含了一个活动的IBinder对象的引用,这个引用导致另一端接收到一个指向这个
IBinder的代理IBinder。
Parcelable通过Parcel实现了read和write的方法,从而实现序列化和反序列化,
Serializable是Java中的序列化接口,其使用起来简单但开销较大(因为Serializable在序列化过程中使
用了反射机制,故而会产生大量的临时变量,从而导致频繁的GC),并且在读写数据过程中,它是通
过IO流的形式将数据写入到硬盘或者传输到网络上。
Parcelable则是以IBinder作为信息载体,在内存上开销比较小,因此在内存之间进行数据传递时,推荐
使用Parcelable,而Parcelable对数据进行持久化或者网络传输时操作复杂,一般这个时候推荐使用
Serializable。
首先Parcelable的性能要强于Serializable的原因我需要简单的阐述一下
//Parcel.java
/**
* Returns the raw bytes of the parcel.
*
* The data you retrieve here must not
* be placed in any kind of persistent storage (on local disk, across
* a network, etc). For that, you should use standard serialization
* or another kind of general serialization mechanism. The Parcel
* marshalled representation is highly optimized for local IPC, and as
* such does not attempt to maintain compatibility with data created
* in different versions of the platform.
*/
public final byte[] marshall() {
return nativeMarshall(mNativePtr);
}
因此在内存中传递对象(比如跨进程通信)时推荐使用Parcelable,在数据库,文件,网络中推荐使用Serializable
反序列化后的对象,需要调用构造函数重新构造吗
不需要调用构造函数
序列化前的对象与序列化后的对象是什么关系?(是"=="还是equal?是浅复制还是深复制?)
序列化后的对象是一个新的对象,与序列化前的对象的内存地址是不一样的,所以equal方法比较两个对象是相等的。
是深复制。
序列化与持久化的关系和区别是什么?
序列化主要是在跨进程通信和网络传输中使用,因为跨进程通信和网络传输不能传输一个对象,所以要把对象进行序列化。
持久化关注的重点是把数据进行永久存储。
SerialVersionID的作用是什么?
serialVersionUID 用来表明类的不同版本间的兼容性。
Android里面为什么要设计出Bundle而不是直接用Map结构
Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数
据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删
除、查找数据的时候,都会使用二分法查找,只适合于小数据量操作,如果在数据量比较大的情况
下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,
HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据
量,我没见过在两个Activity之间传递10个以上数据的场景,所以相比之下,在这种情况下使用
ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保
证更快的速度和更少的内存占用。
另外一个原因,则是在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可
序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。
而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为
了更加快速的进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输。
Android中Intent/Bundle的通信原理及大小限制
Bundle 是实现Parcelable接口的(实现了writeToParcel()和readFromParcel()方法),Bundle对象里面的所有数据最终会通过Bundle的writeToParcel()方法被写成一个Parcel对象,Parcel用于在 Binder 跨进程通信中进行数据传送。Binder跨进程通信时会将Parcel对象进行序列化后(Parcel的marshall方法)的数据写入到一个共享内存中,读取时也是Binder从共享内存中读出字节流,然后反序列化(Parcel的unmarshall方法)为Parcel对象进行使用。能使用的 Binder 的缓冲区是有大小限制的,一次Binder通信最大可以传输是1MB-8KB(有些手机是 2 M),而一个进程默认有 16 个 Binder 线程,所以一个线程能占用的缓冲区就更小了( 有人以前做过测试,大约一个线程可以占用 128 KB)。所以当你看到 The Binder transaction failed because it was too large 这类 TransactionTooLargeException 异常时,你应该知道怎么解决了
//ProcessState.cpp
//binder虚拟内存大小
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define DEFAULT_MAX_BINDER_THREADS 15
那么如何传递大数据?可以把数据存储在文件或者数据库中,Intent只传递数据的路径。
参考:
http://gityuan.com/2016/03/12/start-activity/
Java筑基——Serializable序列化接口
(源码解读)java.io.Serializable可序列化接口
Android之序列化详解
Android序列化总结
Android 序列化-Serializable接口和Parcelable接口的使用
参考源码:
https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/native/libs/binder/ProcessState.cpp