之前看过一遍《Java编程思想》,把书中的代码理解了一遍,也都敲过一遍。但是很多比较深入的知识在实际的工作中并未使用到,因此都忘记了,为了给自己做一些巩固,在以后的时间里,我会努力地做一些关于这些内容的读书笔记,以加强自己的理解
Java的序列化是指将Java对象转化为可在磁盘或者其它存储介质中保存的字节序列,以便在后续的操作中将这个字节序列重新复原为Java对象,存储的字节序列可以写入文件亦可以通过网络进行传输。那么什么对象可以被序列化呢?被序列化的对象必须实现Serializable接口,请看下例:
package com.fan;
import java.io.*;
import java.util.Random;
/**
* Created by Alpha on 17/4/16.
*/
class Data implements Serializable {
private int n;
public Data(int n){this.n = n;}
public String toString(){return Integer.toString(n);}
}
public class Worm implements Serializable{
private static Random rand = new Random(47);
private Data d[] = {
new Data(rand.nextInt(10)),
new Data(rand.nextInt(10)),
new Data(rand.nextInt(10))
};
private Worm next;
private char c;
public Worm(int i,char x){
System.out.println("Worm constructor:" + i);
c = x;
if(--i > 0){
next = new Worm(i,(char)(x + 1));
}
}
public Worm(){
System.out.println("Default constructor");
}
public String toString(){
StringBuilder result = new StringBuilder(":");
result.append(c);
result.append("(");
for(Data dat : d)
result.append(dat);
result.append(")");
if(next != null)
result.append(next);
return result.toString();
}
public static void test() throws IOException, ClassNotFoundException {
Worm w = new Worm(6,'a');
System.out.println("w = " + w);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("worm.out"));
out.writeObject("Worm storage");
out.writeObject(w);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("worm.out"));
String s = (String) in.readObject();
Worm w2 = (Worm) in.readObject();
System.out.println(s + "w2 = " + w2);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out2 = new ObjectOutputStream(bout);
out2.writeObject("worm storage");
out2.writeObject(w);
out2.flush();
ObjectInputStream in2 = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));
s = (String) in2.readObject();
Worm w3 = (Worm) in2.readObject();
System.out.println(s + "w3 = " + w3);
}
}
输出如下:
在上面的例子中我们可以看到,我们定义了两个类,Data
和Worm
,它们都实现了Serializable
接口,在测试方法中我们创建一个包含6个Worm类对象并将这些对象写到了文件worm.out
中,看上面的读取步骤,与写入的步骤相同,我们也是先读取一个字符串对象再读取一个Worm对象
如上例,假如我们将写的对象的序列化文件通过网络传输给其它的计算机,那么其它的计算机是不是同样也能解析出这个对象呢,答案是不一定,假如另一台计算机中的classpath
中没有上述的Data
类和Worm
类,那么就会抛出一个ClassNotFoundException
的异常
Externalizable
假如我们需要序列化一个对象,但是又不希望将这个对象的所有成员都序列化,比如登录表单,我们只希望提供给用户用户名
而不是密码
,那么我们可以将包含用户名
和密码
的类通过实现Externalizable
接口来实现,请看下面的实例
package com.fan;
import java.io.*;
/**
* Created by Alpha on 17/4/16.
*/
public class Blips3 implements Externalizable {
private int i;
private String s;
public Blips3(){
System.out.println("Blips3 constructor");
}
public Blips3(String x,int a){
s = x;
i = a;
}
public String toString(){return s + i;}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Blips3.writeExternal");
out.writeObject(s);
out.writeInt(i);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("Blips3.readExternal");
s = (String) in.readObject();
i = in.readInt();
}
public static void test() throws IOException, ClassNotFoundException {
System.out.println("Constructing objects:");
Blips3 b3 = new Blips3("A String " , 47);
System.out.println(b3);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("Blip3.out"));
System.out.println("Saving object:");
out.writeObject(b3);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("Blip3.out"));
System.out.println("Recovering b3:");
b3 = (Blips3) in.readObject();
System.out.println(b3);
}
}
输出如下
如上,我们在使用
Externalizable
接口来进行对象的初始化时,我们必须实现Externalizable
类的两个接口writeExternal
和readExternal
,这个过程和Serializable
接口的不太一样,Serializable
是以二进制为基础来对对象进行存储,而Externalizable
则需要调用构造器,写入时调用writeExternal
,读取时调用readExternal
,因此我们假如我们不想某个成员的内容被序列化,我们可以不在writeExternal
方法里将其写入输出文件。注意在使用序列化写入或读取时,写入的方法和读取的对应的方法要一致
transient关键字
利用Serializable也可以实现对象的部分成员序列化,那就是对不需要序列化的成员使用transient关键字,如下例所示
package com.fan;
import java.io.*;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* Created by fanwenlong on 17/4/16.
*/
public class Logon implements Serializable {
private Date date = new Date();
private String username;
private transient String password;
public Logon(String username,String password){
this.username = username;
this.password = password;
}
public String toString(){
return "logon info:\n username:" + username + "\n date:" + date + "\n password:" + password;
}
public static void test() throws IOException, InterruptedException, ClassNotFoundException {
Logon logon = new Logon("Aplha","hello");
System.out.println("Logon logon = " + logon);
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Logon.out"));
o.writeObject(logon);
o.close();
TimeUnit.SECONDS.sleep(1);
ObjectInputStream in = new ObjectInputStream(new FileInputStream("Logon.out"));
System.out.println("Recoving object at:" + new Date());
logon = (Logon) in.readObject();
System.out.println("logon logon = " + logon);
}
}
结果如下
可以看到,上面的
password
写入序列化后再次的值为空,说明其没有被写入保存