Comparable可认为是内比较器(可比较的),是接口类,类参数为泛型对象T,通常对比的类本身需实现继承Comparable接口类的唯一方法compareTo(T o),对比指标为类的一个或多个属性,对比类与Comparable接口类耦合性强,Comparable接口类源代码如下:
public interface Comparable<T> {
public int compareTo(T o);
}
类对象通常在实现继承Comparable接口类后,配合java.util.Collections.sort(T[] arr)或java.util.Arrays.sort(List<T> list)来实现排序。
JDK常见使用:如基本数据类型String,Integer implements java.io.Serializable, Comparable<T>以及File implements java.io.Serializable, Comparable<File>
Comparator可认为是是外比专用比较器,是接口类,类参数为泛型对象T,通过编写独立的排序算法类继承Comparator接口类,而对比类本身不做任何继承,保证对比类与Comparator接口类无耦合,接口类部分源代码如下:
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
.......
}
类对象通常在独立的排序算法类实现继承Comparator接口类后,配合java.util.Collections.sort(T[] arr, Comparator<? super T> c)或java.util.Arrays.sort(List<T> list, Comparator<? super T> c)来实现排序。常见使用习惯(代码片段):
StudentComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Studento2) {
if (o1.getScore() > o2.getScore()) return -1;
else if (o1.getScore() < o2.getScore()) return 1;
else {
if (o1.getAge() > o2.getAge()) return 1;
else if (o1.getAge() < o2.getAge()) return -1;
else return 0;
}
}
}
java.util.Arrays.sort(Student[] stuArrs,new StudentComparator());
java.util.Collections.sort(List<Student> sList,new StudentComparator());
序列化Serializable 和反序列化Deserializable
概念:把对象(内存中)转换为字节序列的过程称为对象的序列化;把字节(码)序列恢复为对象的过程称为对象的反序列化。
主要用途:1)把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中 2)在网络上传送对象的字节序列
**磁盘数据存储格式或网络间数据存储传输格式都基于
字节
码**
序列化 ID
序列化 ID 是否一致确定了类反序列化是否正确(默认long serialVersionUID = 1L),序列化保存的是对象的状态,不能保存类的状态,故序列化时静态变量不保存,直接从内存中取数据验证片断代码:
public class TestSerializable implements Serializable {
private static final long serialVersionUID = 1L;
public static String staticVar = "static";
public String name = "cj";
public static void main(String[] args) {
TestSerializable m = new TestSerializable();
m.setName("cm");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
out.writeObject(m);
out.close();
TestSerializable mupdage = new TestSerializable();
mupdage.staticVar = "staticUpdate";
mupdage.setName("cmUpdate");
ObjectInputStream oin = new ObjectInputStream(new FileInputStream( "result.obj"));
TestSerializable t = (TestSerializable) oin.readObject();
oin.close();
System.out.println(t.staticVar +" "+ t.getName());
}
}
输出:staticUpdate cm
原因:类普通属性值通过反序列化转化后的中对象获取值,而静态变量属性值从内存中获取。
Transient[临时的] 关键字:特殊定义变量,阻止该变量被序列化,在被反序列化后,transient 变量的值被设为初始值
对敏感字段加密
情境:服务器端给客户端发送序列化对象数据,对象中敏感数据在序列化时需要进行加密,比如密码字符串等,客户端在拥有解密的密钥,且进行反序列化时,才可以对密码进行读取,这样可一定程度保证序列化对象的数据安全。
解决:在序列化过程中,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化。如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。验证片断代码:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String password = "pass";//省略GET、SET
private void writeObject(ObjectOutputStream out) {
try {
PutField putFields = out.putFields();
System.out.println("原密码:" + password);
password = "encryption";//模拟加密
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", "");
System.out.println("要解密的字符串:" + object.toString());
password = "pass";//模拟解密,需要获得本地的密钥
} catch (IOException e) { e.printStackTrace(); }
catch (ClassNotFoundException e) { e.printStackTrace(); }
}
public static void main(String[] args) {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
out.writeObject(new User()); out.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream( "result.obj"));
User t = (User) oin.readObject();
System.out.println("解密后的字符串:" + t.getPassword()); oin.close();
}
}
使用案例
:RMI技术是完全基于 Java序列化技术的,服务器端接口调用所需要的参数对象来至于客户端,它们通过网络相互传输。这就涉及RMI 的安全传输的问题。一些敏感的字段,如用户名密码(用户登录时需要对密码进行传输),我们希望对其进行加密,这时,就可以采用本节介绍的方法在客户端对密 码进行加密,服务器端进行解密,确保数据传输的安全性
序列化存储规则
Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,增加 5 字节的存储空间就是新增引用和一些控制信息的空间。反序列化时,恢复引用关系,使得清单 3 中的 t1 和 t2 指向唯一的对象,二者相等,输出 true。该存储规则极大的节省了存储空间,如下代码片断:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result2.obj"));
TestSerializable testS = new TestSerializable();
testS.setName("1");
out.writeObject(testS);
out.flush();
System.out.print(new File("result2.obj").length() +" ");
testS.setName("2");
out.writeObject(testS);
System.out.print(new File("result2.obj").length()+" ");
out.close();
oin2 = new ObjectInputStream(new FileInputStream("result2.obj"));
TestSerializable t1 = (TestSerializable) oin2.readObject();
TestSerializable t2 = (TestSerializable) oin2.readObject();
System.out.print(t1 == t2);
System.out.println(" " +t1.getName() +" " + t2.getName());
输出:72 77 true 1 1
原理分析
序列化方式一:调用ObjectOutputStream类writeObject方法序列化对象,将其写入磁盘,再次调用readObject时,根据wirteObject方法从磁盘文件重新恢复对象。
序列化方式二:Externalizable接口扩展Serializable,并增添了两个方法:writeExternal()和readExternal()。在序列化和重新装配的过程中,会自动调用此两个方法。
方式一执行的详细如下:
1)ObjectOutputStream的构造函数设置enableOverride = false:
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
writeStreamHeader();
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
2)外部调用ObjectOutputStream.writeObject(序列化类对象)方法执行writeObject0(obj, false);
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
writeObject0(obj, false)重要代码片断:
// remaining cases
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
从上可以看出,如果对象没有实现Serializable接口,在序列化的时候会抛出NotSerializableException异常。
跟踪writeOrdinaryObject(obj, desc,unshared)方法代码片断:
desc.checkSerialize();
bout.writeByte(TC_OBJECT);
writeClassDesc(desc, false);
handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
在检查Serialize后,如果对象实现Externalizable接口,执行writeExternalData((Externalizable) obj)方法如果实现的是Serializable接口,那么执行的是writeSerialData(obj, desc);
首先看writeSerialData方法,主要执行方法:defaultWriteFields(obj, slotDesc);
private void writeSerialData(Object obj, ObjectStreamClass desc)
throws IOException
{
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
for (int i = 0; i < slots.length; i++) {
ObjectStreamClass slotDesc = slots[i].desc;
if (slotDesc.hasWriteObjectMethod()) {
PutFieldImpl oldPut = curPut;
curPut = null;
SerialCallbackContext oldContext = curContext;
if (extendedDebugInfo) {
debugInfoStack.push(
"custom writeObject data (class \"" +
slotDesc.getName() + "\")");
}
try {
curContext = new SerialCallbackContext(obj, slotDesc);
bout.setBlockDataMode(true);
slotDesc.invokeWriteObject(obj, this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
} finally {
curContext.setUsed();
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
curPut = oldPut;
} else {
defaultWriteFields(obj, slotDesc);
}
}
}
slotDesc.hasWriteObjectMethod()检查序列化类是否存在自定义的writeObject(ObjectOutputStream outputStream),存在则执行 slotDesc.invokeWriteObject(obj,this);通过反射去执行自定义的writeObject(ObjectOutputStream outputStream)方法,否则执行默认的defaultWriteFields(obj, slotDesc),若执行默认的defaultWriteFields(obj, slotDesc),通过writeObject0循环将类属性写入文件中。代码片断:
private void defaultWriteFields(Object obj, ObjectStreamClass desc){
desc.checkDefaultSerialize();
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
desc.getObjFieldValues(obj, objVals);
for (int i = 0; i < objVals.length; i++) {
writeObject0(objVals[i], fields[numPrimFields + i].isUnshared());
....
}
}
再次看一下writeExternalData的方法,重要代码如下:
private void writeExternalData(Externalizable obj) throws IOException {
try {
curContext = null;
if (protocol == PROTOCOL_VERSION_1) {
obj.writeExternal(this);
} else {
bout.setBlockDataMode(true);
obj.writeExternal(this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
}}
obj.writeExternal(this)为序列化接口类(interfaceExternalizable)对象writeExternal方法,故必须在自定义的序列化类中重载实现writeExternal方法,即方式二执行过程。
反序列化
1) objectInputStream.readObject()方法执行readObject0(false)方法:主要代码:
switch (tc) {
case TC_NULL:
return readNull();
.....
case TC_STRING:
case TC_LONGSTRING:
return checkResolve(readString(unshared));
case TC_ARRAY:
return checkResolve(readArray(unshared));
case TC_ENUM:
return checkResolve(readEnum(unshared));
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));
case TC_EXCEPTION:
IOException ex = readFatalException();
throw new WriteAbortedException("writing aborted", ex);
case TC_BLOCKDATA:
.....
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
根据不同的对象类型做相应的处理,这里我们关注的是TC_OBJECT,执行的方法是:checkResolve(readOrdinaryObject(unshared));接着看readOrdinaryObject(unshared),执行了以下代码:
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
......
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else { readSerialData(obj, desc); }
ObjectStreamClass默认构造函数:
private ObjectStreamClass(final Class<?> cl) {
if (serializable) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
.....
suid = getDeclaredSUID(cl);//获取UID
try {
fields = getSerialFields(cl);//获取SerialFields
computeFieldOffsets();
} ......
if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
//定义系列ObjectMethod
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[] { ObjectOutputStream.class },
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[] { ObjectInputStream.class },
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
......
});
}
}
序列化类实现Externalizable 接口执行readExternalData((Externalizable)obj, desc);,实现Serializable接口则执行readSerialData(obj,desc);首先先看readSerialData(obj,desc);代码片断:
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
for (int i = 0; i < slots.length; i++) {
ObjectStreamClass slotDesc = slots[i].desc;
if (slots[i].hasData) {
if (obj == null || handles.lookupException(passHandle) != null) {
defaultReadFields(null, slotDesc); // skip field values
} else if (slotDesc.hasReadObjectMethod()) {
......
slotDesc.invokeReadObject(obj, this);
} ......
} else { defaultReadFields(obj, slotDesc); }
}
if (obj != null && slotDesc.hasReadObjectNoDataMethod() && handles.lookupException(passHandle) == null)
{slotDesc.invokeReadObjectNoData(obj);}
}
序列化类中若存在ReadObject方法,则执行类中ReadObject方法,否则obj为空或默认情况下执行defaultReadFields方法。
其次看readExternalData((Externalizable)obj, desc);跟踪如以下主要代码片断:
private void readExternalData(Externalizable obj, ObjectStreamClass desc){...obj.readExternal(this);...}
obj.readExternal(this)为序列化接口类(interfaceExternalizable)对象readExternal方法,故必须在自定义的序列化类中重载实现readExternal方法,即方式二执行过程。