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方法,即方式二执行过程。