本文共 12686 字,大约阅读时间需要 42 分钟。
说java的序列化一般都是Serializable接口,今天不仅是这部分内容。
第一部分 Serializable接口
首先说一下Serializable接口,类需要实现这个接口,但是需要注意几点
1.如果这个类是可序列化的,那么他的子类也是可以序列化的。
2.如果这个类是可序列化的,他的父类如果没有实现Serializable接口,那么父类的属性不会被序列化,除非父类也实现了Serializable接口。
3.关于类的静态变量,序列化和反序列化不会影响类静态变量的值。
4.Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
5.关于类的serialVersionUID 的问题,在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;而在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
/** * @Title: SerializableTest.java * @Package com.digital.base.serializable * @Description: TODO(用一句话描述该文件做什么) * @date 2018年7月9日 * @version V1.0 */ package com.digital.base.serializable;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;/** * @ClassName: SerializableTest * @Description: TODO(这里用一句话描述这个类的作用) * @author liucgc * @date 2018年7月9日 * */public class SerializableTest { public static void main(String[] args) throws Exception { FileOutputStream fos = new FileOutputStream("temp.out"); ObjectOutputStream oos = new ObjectOutputStream(fos); TestObject testObject = new TestObject(); testObject.staticValue=500; testObject.parentValue=200;// TestObject.staticValue=5000; System.out.println(testObject.staticValue); System.out.println("parentValue is "+testObject.parentValue); oos.writeObject(testObject); oos.flush(); oos.close(); TestObject.staticValue=5000; FileInputStream fis = new FileInputStream("temp.out"); ObjectInputStream ois = new ObjectInputStream(fis); TestObject deTest = (TestObject) ois.readObject(); System.out.println(deTest.testValue); System.out.println("parentValue is "+deTest.parentValue); System.out.println(deTest.innerObject.innerValue); System.out.println(deTest.staticValue); } } class Parent //implements Serializable { private static final long serialVersionUID = -4963266899668807475L; public int parentValue = 100; } class InnerObject implements Serializable { private static final long serialVersionUID = 5704957411985783570L; public int innerValue = 200; } class TestObject extends Parent implements Serializable { private static final long serialVersionUID = -3186721026267206914L; public int testValue = 300; public static int staticValue=50; public InnerObject innerObject = new InnerObject(); }
第二部分 自定义序列化之Serializable
自定义序列化是由ObjectInput/OutputStream在序列化/反序列化时候通过反射检查该类是否存在以下方法(0个或多个):执行顺序从上往下,序列化调用1和2,反序列调用3和4;transient关键字当某个字段被声明为transient后,默认序列化机制就会忽略该字段。
1.Object writeReplace() throws ObjectStreamException;可以通过此方法修改序列化的对象2.void writeObject(java.io.ObjectOutputStream out) throws IOException; 方法中调用defaultWriteObject() 使用writeObject的默认的序列化方式,除此之外可以加上一些其他的操作,如添加额外的序列化对象到输出:out.writeObject("XX")3.void readObject(java.io.ObjectInputStream in) throws Exception; 方法中调用defaultReadObject()使用readObject默认的反序列化方式,除此之外可以加上一些其他的操作,如读入额外的序列化对象到输入:in.readObject()4.Object readResolve() throws ObjectStreamException;可以通过此方法修改返回的对象。
自定义的 writeReplace、writeObject、readResolve 和 readObject 方法可以允许用户控制序列化的过程,下面的例子中模拟了对password字段加密。
/** * @Title: Personser.java * @Package com.digital.base.serializable * @Description: TODO(用一句话描述该文件做什么) * @date 2018年7月9日 * @version V1.0 */ package com.digital.base.serializable;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectInputStream.GetField;import java.io.ObjectOutputStream;import java.io.ObjectOutputStream.PutField;import java.io.Serializable;import java.util.Date;/** * @ClassName: Personser * @Description: TODO(这里用一句话描述这个类的作用) * @author liucgc * @date 2018年7月9日 * */public class Personser implements Serializable { private String code; private String password="pass"; private Date createDate; /** * @Title: main * @Description: TODO(这里用一句话描述这个方法的作用) * @param @param args 参数 * @return void 返回类型 * @throws */ public static void main(String[] args) { try { ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("result.obj")); Personser per=new Personser(); per.setPassword("password123"); out.writeObject(per); out.close(); ObjectInputStream oin = new ObjectInputStream(new FileInputStream( "result.obj")); Personser t = (Personser) oin.readObject(); System.out.println("解密后的字符串:" + t.getPassword()); oin.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * @return the code */ public String getCode() { return code; } /** * @param code the code to set */ public void setCode(String code) { this.code = code; } /** * @return the password */ public String getPassword() { return password; } /** * @param password the password to set */ public void setPassword(String password) { this.password = password; } /** * @return the createDate */ public Date getCreateDate() { return createDate; } /** * @param createDate the createDate to set */ public void setCreateDate(Date createDate) { this.createDate = createDate; } private void writeObject(ObjectOutputStream out) { try { PutField putFields = out.putFields(); System.out.println("原密码:" + password); password = enCode(password);//模拟加密 putFields.put("password", password); System.out.println("加密后的密码" + password); out.writeFields(); } catch (IOException e) { e.printStackTrace(); } } private void readObject(ObjectInputStream in) { try { GetField readFields = in.readFields(); Object object = readFields.get("password", "encryption"); System.out.println("要解密的字符串:" + object.toString()); password=object.toString(); password=deCode(password);//模拟解密,需要获得本地的密钥 } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public String enCode(String pass) { if("password123".equals(pass)) { return "encryption"; } return "error"; } public String deCode(String pass) { if("encryption".equals(pass)) { return "password123"; } return "error"; }}
单例模式的类实现序列化接口,若使用默认的序列化策略,则在反序列化返回的对象创建了新的对象,破坏了单例模式,面试扩展:如果一个类是单例的,那么如何能再创建该类一个新的对象。
private Object readResolve() throws ObjectStreamException { System.out.println("4 read resolve start"); return PersonSingleton.getInstance();//不管序列化的操作是什么,返回的都是本地的单例对象 }
第三部分 自定义之 Externalizable接口
此种实现方法需要注意的两点,
1.类必须存在一个pulic的无参数构造方法。
2.反序列化时的字段属性需要与序列化时一致,否则值顺序错乱。
/** * @Title: Personextern.java * @Package com.digital.base.serializable * @Description: TODO(用一句话描述该文件做什么) * @author liucgc * @date 2018年7月9日 * @version V1.0 */ package com.digital.base.serializable;import java.io.Externalizable;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInput;import java.io.ObjectInputStream;import java.io.ObjectOutput;import java.io.ObjectOutputStream;import java.util.Date;/** * @ClassName: Personextern * @Description: TODO(这里用一句话描述这个类的作用) * @author liucgc * @date 2018年7月9日 * */public class Personextern implements Externalizable { private String code; private String password="pass"; private Date createDate; public Personextern() { System.out.println("Personextern constructor1"); } public Personextern(String code,String password,Date createDate) { this.code=code; this.password=password; this.createDate=createDate; System.out.println("Personextern constructor2"); } public void writeExternal(ObjectOutput out) throws IOException { // TODO Auto-generated method stub out.writeObject(code); out.writeObject(password); out.writeObject(createDate); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { code=(String) in.readObject(); password=(String) in.readObject(); createDate=(Date)in.readObject(); } /** * @throws IOException * @Title: main * @Description: TODO(这里用一句话描述这个方法的作用) * @param @param args 参数 * @return void 返回类型 * @throws */ public static void main(String[] args) throws IOException { FileOutputStream fos=null; ObjectOutputStream oos=null; FileInputStream fis=null; ObjectInputStream ois=null; try { Personextern per=new Personextern(); per.setPassword("password123"); per.setCode("123123"); fos=new FileOutputStream(new File("E://javanewlskd.txt")); oos=new ObjectOutputStream(fos); oos.writeObject(per); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if(oos!=null) { oos.close(); } } try { fis=new FileInputStream(new File("E://javanewlskd.txt")); ois=new ObjectInputStream(fis); System.out.println("反序列化之后,readObject"); Personextern per=(Personextern)ois.readObject(); System.out.println(per); } catch(Exception e) { e.printStackTrace(); } finally { if(ois!=null) { ois.close(); } } } @Override public String toString() { return "Personextern [code=" + code + ", password=" + password + ", createDate=" + createDate + "]"; } /** * @return the code */ public String getCode() { return code; } /** * @param code the code to set */ public void setCode(String code) { this.code = code; } /** * @return the password */ public String getPassword() { return password; } /** * @param password the password to set */ public void setPassword(String password) { this.password = password; } /** * @return the createDate */ public Date getCreateDate() { return createDate; } /** * @param createDate the createDate to set */ public void setCreateDate(Date createDate) { this.createDate = createDate; }}
第四部分 性能对比
/** * @Title: TestPerformance.java * @Package com.digital.base.serializable * @Description: TODO(用一句话描述该文件做什么) * @author liucgc * @date 2018年7月9日 * @version V1.0 */ package com.digital.base.serializable;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;/** * @ClassName: TestPerformance * @Description: TODO(这里用一句话描述这个类的作用) * @author liucgc * @date 2018年7月9日 * */public class TestPerformance { /** * @throws IOException * @Title: main * @Description: TODO(这里用一句话描述这个方法的作用) * @param @param args 参数 * @return void 返回类型 * @throws */ public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); int count=10000; start = System.currentTimeMillis(); setSerializableObject(count); System.out.println("java原生序列化时间:" + (System.currentTimeMillis() - start) + " ms" ); start = System.currentTimeMillis(); getSerializableObject(count); System.out.println("java原生反序列化时间:" + (System.currentTimeMillis() - start) + " ms"); start = System.currentTimeMillis(); setSerializableObjectExt(count); System.out.println("java Externalizable序列化时间:" + (System.currentTimeMillis() - start) + " ms" ); start = System.currentTimeMillis(); getSerializableObjectExt(count); System.out.println("java Externalizable反序列化时间:" + (System.currentTimeMillis() - start) + " ms"); } /** * @Title: getSerializableObject * @Description: TODO(这里用一句话描述这个方法的作用) * @param @param count 参数 * @return void 返回类型 * @throws */ private static void getSerializableObject(int count) { try { ObjectInputStream oin = new ObjectInputStream(new FileInputStream( "result.obj")); for(int i=0;i
自定义序列化效率更低一些。
java Externalizable序列化时间:214 msjava Externalizable反序列化时间:255 msjava原生序列化时间:85 msjava原生反序列化时间:166 msjava原生序列化时间:106 msjava原生反序列化时间:164 msjava Externalizable序列化时间:196 msjava Externalizable反序列化时间:229 ms
参考网页相关地址
https://www.cnblogs.com/yoohot/p/6019767.htmlhttps://www.cnblogs.com/chenfei0801/archive/2013/04/06/3002146.html
http://www.cnblogs.com/dukc/p/4817822.html