序列化与反序列化
什么是序列化?什么是反序列化?
序列化: 将对象写入对象输出流中
反序列化:从对象输入流中读出对象
为什么需要序列化?
对象传输数据复杂,类与类之间有关联,比如继承或者引用,序列化机制可以通过对象图机制读写对象,以满足复杂对象的关联,如单链表。实际上,这是动态保存了对象的状态。
怎么实现序列化?
java类库提供很多IO类支持不同需求的IO操作。首先介绍自动序列化,自动序列化需要被传输的类实Serializable接口,此类中引用到的类也需要实现这个接口,接口虽然是空实现,但是必须实现才能被jvm认可可序列化。反序列化时会调用无参构造
自动序列化
Parent类
public class Parent implements Serializable {
private Son son;
// private transient Son son;
private String name;
public Parent(Son son, String name) {
this.son = son;
this.name = name;
}
public Parent() {
}
public Son getSon() {
return son;
}
public void setSon(Son son) {
this.son = son;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Parent{" +
"son=" + son +
", name='" + name + '\'' +
'}';
}
}
Son类
public class Son implements Serializable {
private int age;
private String name;
public Son(int age, String name) {
this.age = age;
this.name = name;
}
public Son() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Son{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
Client类
public class Client {
public static void main(String[] args) {
Son son = new Son(11,"小小");
Parent parent = new Parent(son,"大大");
ObjectOutputStream out = null;
ObjectInputStream in = null;
try {
out = new ObjectOutputStream(new FileOutputStream("data.dat"));
in = new ObjectInputStream(new FileInputStream("data.dat"));
out.writeObject(parent);
Parent p = (Parent)in.readObject();
System.out.println(p);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//打印 : Parent{son=Son{age=11, name='小小'}, name='大大'}
那么,如果我们不希望Son也被序列化,即只需要parent的其他成员有状态,我们可以添加关键字transient,如同Person类的注释,此时Son类也可以不实现序列化接口,打印如
Parent{son=null, name='大大'}
自定义序列化
自动序列化有些需求无法完成,我们可以使用自定义序列化。可以扩展序列化时的处理,比如对密码加密、解密···
class MD6{
public static String md(String s){
return "加密操作"+s;
}
public static String emd(String s){
return s.substring(4,s.length());
}
}
class Parent1 implements Externalizable {
private String username;
private String pwd;
public Parent1(String username, String pwd) {
this.username = username;
this.pwd = pwd;
}
public Parent1(){}
@Override
public String toString() {
return "Parent1{" +
"username='" + username + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(username);
String md = MD6.md(pwd);
out.writeObject(md);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
username = (String)in.readObject();
String s = (String)in.readObject();
pwd = MD6.emd(s);
}
}
class App11{
public static void main(String[] args) {
Parent1 p = new Parent1("小王","123456");
ObjectOutputStream out = null;
ObjectInputStream in = null;
try {
out = new ObjectOutputStream(new FileOutputStream("data1.dat"));
in = new ObjectInputStream(new FileInputStream("data1.dat"));
out.writeObject(p);
Parent1 pp = (Parent1)in.readObject();
System.out.println(pp);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
实际上,我们不要局限于加密解密,我这里写得很简单,实际上这些“加解密”操作我们都是可以扩展延伸的。
半自动自定义序列化
使用transient关键字修饰需要特殊处理的成员,序列化特殊处理实现两个私有方法,writeObject,readObject,JVM调用对象流默认调用类中相应的私有方法,在这两个方法中调用默认的序列化操作,和特殊处理手动序列化的值。毫无疑问这更加方便。至于为什么JVM为什么会这样调用方法,之后学习到更深层再解答。
class MD6{
public static String md(String s){
return "加密操作"+s;
}
public static String emd(String s){
return s.substring(4,s.length());
}
}
class Parent2 implements Serializable {
private String username;
private transient String pwd;
public Parent2(String username, String pwd) {
this.username = username;
this.pwd = pwd;
}
public Parent2(){}
@Override
public String toString() {
return "Parent2{" +
"username='" + username + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
private void writeObject(ObjectOutputStream out) throws IOException{
out.defaultWriteObject();
String md = MD6.md(pwd);
out.writeObject(md);
}
private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
in.defaultReadObject();
String s= (String)in.readObject();
pwd = MD6.emd(s);
}
}
class App21{
public static void main(String[] args) {
Parent2 p = new Parent2("小王","1234256");
ObjectOutputStream out = null;
ObjectInputStream in = null;
try {
out = new ObjectOutputStream(new FileOutputStream("data1.dat"));
in = new ObjectInputStream(new FileInputStream("data1.dat"));
out.writeObject(p);
Parent2 pp = (Parent2)in.readObject();
System.out.println(pp);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}