说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<count;i++) {
Personser t = (Personser) oin.readObject();
}
oin.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* @Title: setSerializableObject
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @param count 参数
* @return void 返回类型
* @throws
*/
private static void setSerializableObject(int count) {
try {
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("result.obj"));
for(int i=0;i<count;i++) {
Personser per=new Personser();
per.setPassword("password123");
out.writeObject(per);
}
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @throws IOException
* @Title: getSerializableObject
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @param count 参数
* @return void 返回类型
* @throws
*/
private static void getSerializableObjectExt(int count) throws IOException {
FileOutputStream fos=null;
ObjectOutputStream oos=null;
FileInputStream fis=null;
ObjectInputStream ois=null;
try {
fis=new FileInputStream(new File("ext.obj"));
ois=new ObjectInputStream(fis);
for(int i=0;i<count;i++) {
Personextern per=(Personextern)ois.readObject();
}
} catch(Exception e) {
e.printStackTrace();
} finally {
if(ois!=null) {
ois.close();
}
}
}
public static void setSerializableObjectExt(int count) throws IOException{
FileOutputStream fos=null;
ObjectOutputStream oos=null;
FileInputStream fis=null;
ObjectInputStream ois=null;
try {
fos=new FileOutputStream(new File("ext.obj"));
oos=new ObjectOutputStream(fos);
for(int i=0;i<count;i++) {
Personextern per=new Personextern();
per.setPassword("password"+i);
per.setCode("code"+i);
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();
}
}
}
}
自定义序列化效率更低一些。
java Externalizable序列化时间:214 ms
java Externalizable反序列化时间:255 ms
java原生序列化时间:85 ms
java原生反序列化时间:166 ms
java原生序列化时间:106 ms
java原生反序列化时间:164 ms
java Externalizable序列化时间:196 ms
java 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