首先我们创建一个Person类:
class Person(name: String, id: Int) extends Serializale
这里的name和id更像是java里的构造函数参数,无法通过Person的对象之间访问name和id,实际上通过javap反编译后的代码类似于:
public class Person implements Serializale { public Person(String name, int id) { } }
所以当我们对Person对象进行序列化时实际上name和id不会被写到OutputStream中。
但是当Person内有方法需要访问到name或者id时则会为name和id生产final的field
class Person(name: String, id: Int) extends Serializale {
def func(): Unit = {
println(s"${name} ${id}")
}
}
这时反编译后代码为:
public class Person implements Serializale { private final String name; private final int id; public Person(String name, int id) { this.name = name; thid.id = id; } public void func() { ... } }
我们对Person的field做点改变:
class Person(val name: String, val id: Int) extends Serializable
这时反编译后的代码类似于:
public class Person implements Serializale { private final String _name; private final int _id; public Person(String name, int id) { this._name = name; this._id = id; } public String name() { return _name; } public int id() { return _id; } }
所以不同的是增加了两个访问方法,序列化时会写出两个field。
再做点改变:
class Person(var name: String, var id: Int) extends Serializable
反编译:
public class Person implements Serializale { private String _name; private int _id; public Person(String name, int id) { this._name = name; this._id = id; } public String name() { return _name; } public int id() { return _id; } public void name_=(String name) { this._name = name; } public void id_=(int id) { this._id = id; } }
这次两个field不再是final修饰了,并且增加了两个set方法。现在更像是一个java的POJO了。
那对于一个lazy对象会怎么样呢?继续改造下:
class Person(name: String, id: Int) extends Serializable { lazy val NAME = name lazy val ID = id }
在这里我们新加了两个lazy修饰的field 并且transient。
val p = new Person("test", 1) val bytes = // serialize p val p1 = // deserialize bytes into Person object println(p1.NAME) // "test" println(p1.ID) // 1
我们对Person对象进行序列化和反序列化,神奇的是即使两个field用transient修饰了,我们依然可以在反序列化后得到。我们看下反编译后的代码类似于如下:
public class Person implements Serializale { private final String _name; private final int _id; private transient String NAME; private transient int ID; private volatile boolean NAMEInitialized = false; private volatile boolean IDInitialized = false; public Person(String name, int id) { this._name = name; this._id = id; } public String NAME() { synchronized(this) { if (!NAMEInitialized) { NAME = _name; NAMEInitialized = true; } } return NAME; } public String ID() { synchronized(this) { if (!IDInitialized) { ID = _id; IDInitialized = true; } } return ID; } }
实际上的代码和上述代码有点区别。我们可以看到lazy修饰的field被转换成了一个方法,而我们的transient修饰于内部的字段,但是我们依然可以通过方法访问到。所以我们只要把Person改造成下面这样就访问不到了:
class Person( name: String, id: Int) extends Serializable { lazy val NAME = name lazy val ID = id }