Tag Interface:标记接口
- 什么是标记接口:就是为实现类打上某个标签,是的实现类具有什么样的功能,这种功能的实现是JDK底层帮我们实现的。其和注解类似,注解的功能,JDK自带的是由JDK实现的。我们也可以自定义注解并且解释器功能。
目前标记接口,只有四个:Cloneable、RandomAccess、Serializable、Remote
Cloneable
Cloneable:可克隆的接口,针对于Java对象的克隆。何为克隆,就是克隆出一样的对象使用,但是相互之间不受影响的对象。
- 如下所示:cloneA将会抛出CloneNotSupportedException异常,因为其没有实现Cloneable接口,所以其就算重写了clone方法,也是会抛出异常的。
```java
public class CloneableTest {
public static void main(String[] args) throws Exception {
} }CloneA cloneA = new CloneA();CloneB cloneB = new CloneB();System.out.println(cloneA.clone()); // 抛出 CloneNotSupportedException 异常System.out.println(cloneB.clone());
class CloneA { private String name;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class CloneB implements Cloneable { private String name;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- Object类clone()的修饰符为protected产生的影响。由于Object的clone()方法是protected的,所以一个类从Object继承的clone方法默认只有java.lang包可见,以及子类可以继承,但子类也只能调用受保护的clone()方法来克隆它自己的对象(注意调用super.clone()不会克隆父类的对象,还是克隆的这个类的对象)。一个类必须重新定义clone()为public方法才能允许"所有方法"调用这个类的clone()方法来克隆这个类的实例。
- 克隆对象和原对象有什么联系?**克隆出来的对象与原来的对象属性是相同的,但不是同一个对象【和克隆羊一样,完全一样的身体,完全不一样的灵魂】**,他们的内存地址是不一样的,所以改变克隆出来的对象的属性一般不会影响到原来的对象。但是从Object继承的clone方法是一个浅拷贝,如果原对象包含子对象的引用,拷贝对象和原对象会拥有对同一个子对象的引用,这个情况下克隆对象对这个子对象的更改会改变原对象中这个子对象的属性。所以在原对象引用的子对象不都是String这种不可变的对象的话,必须重新定义clone方法来建立一个深拷贝。
<a name="Ke4A7"></a>
# RandomAccess
- RandomAccess:标识支持随机访问的接口。
- ArrayList基于数组实现,天然带下标,可以实现常量级的随机访问,复杂度为O(1)
- LinkedList基于链表实现,随机访问需要依靠遍历实现,复杂度为O(n)
- ArrayList实现了RandomAccess接口,而LinkedList没有
- 那么到底这个接口有什么用呢
- 当一个List拥有快速访问功能时,其遍历方法采用for循环最快速。而没有快速访问功能的List,遍历的时候采用Iterator迭代器最快速。其时间复杂度为O(1)
- 当我们不明确获取到的是Arraylist,还是LinkedList的时候,我们可以通过RandomAccess来判断其是否支持快速随机访问,若支持则采用for循环遍历,否则采用迭代器遍历,如下方式
```java
public class RandomAccessTest {
private List<String> list = null;
public RandomAccessTest(List<String> list) {
this.list = list;
}
public void loop() {
if (list instanceof RandomAccess) {
// for循环
System.out.println("采用for循环遍历");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
} else {
// 迭代器
System.out.println("采用迭代器遍历");
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
public static void main(String[] args) {
List<String> list = Arrays.asList("123", "456", "789", "1110");
List<String> list1 = new LinkedList<>();
list1.add("aaa");
list1.add("bbb");
list1.add("ccc");
new RandomAccessTest(list).loop();
new RandomAccessTest(list1).loop();
}
}
- 实现随机访问的接口
- ArrayList
- Vector
- CopyOnWriteArrayList
- RandomAccessSubList
- UnmodifiableArrayList
测试速度 ```java public class RandomAccessTimeTest {
//使用for循环遍历 public static long traverseByLoop(List list) {
long startTime = System.currentTimeMillis(); for (int i = 0; i < list.size(); i++) { list.get(i); } long endTime = System.currentTimeMillis(); return endTime - startTime;}
//使用迭代器遍历 public static long traverseByIterator(List list) {
Iterator iterator = list.iterator(); long startTime = System.currentTimeMillis(); while (iterator.hasNext()) { iterator.next(); } long endTime = System.currentTimeMillis(); return endTime - startTime;}
public static void main(String[] args) {
//加入数据 List<String> arrayList = new ArrayList<>(); for (int i = 0; i < 30000; i++) { arrayList.add("" + i); } long loopTime = RandomAccessTimeTest.traverseByLoop(arrayList); long iteratorTime = RandomAccessTimeTest.traverseByIterator(arrayList); System.out.println("ArrayList:"); System.out.println("for循环遍历时间:" + loopTime + " ms"); System.out.println("迭代器遍历时间:" + iteratorTime + " ms"); List<String> linkedList = new LinkedList<>(); //加入数据 for (int i = 0; i < 30000; i++) { linkedList.add("" + i); } loopTime = RandomAccessTimeTest.traverseByLoop(linkedList); iteratorTime = RandomAccessTimeTest.traverseByIterator(linkedList); System.out.println("LinkedList:"); System.out.println("for循环遍历时间:" + loopTime + " ms"); System.out.println("迭代器遍历时间:" + iteratorTime + " ms");} }
// ArrayList: // for循环遍历时间:2 ms // 迭代器遍历时间:1 ms
// LinkedList: // for循环遍历时间:1413 ms // 迭代器遍历时间:1 ms
<a name="KTxui"></a>
# Serializable
- 该接口用来标记类的对象是否能够可以进行序列化,或者说串行化。将对象序列化之后,可以进行持久化的储存以及在网络中进行传输。如把对象变成字节流写入到一个文件中,就是一个序列化的过程,实现了对象的持久化储存,然后你的程序可以从这个文件中读取序列化的对象并且把它还原成原来的对象,进行反序列化。如果进行序列化的类的对象没有实现Serializable接口,则会抛出NotSerializableException
- **对象序列化有哪些特点?**在对象序列化时,该对象引用的实例变量也会被序列化,如果这个实例变量是一个对象,这个对象也会被序列化。被transient修饰的实例变量也不参与序列化。在对象反序列化时transient修饰的变量将重新初始化,对象初始为null,基本数据类型被初始化为0、false等值。而static修饰的静态变量也不参与序列化的过程,在反序列化时,它的值是运行时虚拟机中该变量的值,并不是对象序列化时"储存"的值。
- **serialVersionUID是什么,有什么用?**在对象序列化时,这个对象都会有一个的serialVersionUID,它是根据类的结构信息计算出来的。在反序列化时,如果对象有了不同的serialVersionUID,则虚拟机将会抛出异常,还原操作将会失败。如在ArrayList类的源码(JDK8)中就可以发现如下属性,所以JDK8中ArrayList类的对象都有一个一致的serialVersionUID值。
```java
public class SerializableTest {
public static void main(String[] args) {
Baby mybaby = new Baby(2, "小官");
Mother mother = new Mother(25, mybaby, "官妈");
// 此时静态变量name引用的字符串为"小红"
mother.setName("小红");
ObjectOutputStream oos = null;
try (FileOutputStream fos = new FileOutputStream("MyMom.ser");) {
// "MyMom.ser"如果不存在会自动创建
oos = new ObjectOutputStream(fos);
// 将变量引用的对象序列化并写入MyMom.ser这个文件
oos.writeObject(mother);
} catch (Exception e) {
e.printStackTrace();
}
ObjectInputStream ois = null;
Mother reMother = null;
// 静态变量name引用的字符串修改为"不是小红"
mother.setName("不是小红");
try (FileInputStream fis = new FileInputStream("MyMom.ser");) {
ois = new ObjectInputStream(fis);
reMother = (Mother) ois.readObject();
// 将写入MyMom.ser的对象反序列化,由于返回的是Object对象,所以需要进行强转
} catch (Exception e) {
e.printStackTrace();
}
// 将反序列化得到的对象reMother打印出来
System.out.println(reMother);
System.out.println(reMother.name);
}
}
class Baby implements Serializable {
private int age;
private String name;
public Baby(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return new StringJoiner(",").add("age=" + age).add("name=" + name).toString();
}
}
class Mother implements Serializable {
private int i;
// 一个实例变量,也是一个实现了Serializable的类的对象
private Baby mybaby;
public static String name = "LDL";
private transient String nickname;
public Mother(int i, Baby mybaby, String nickname) {
this.i = i;
this.mybaby = mybaby;
this.nickname = nickname;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return new StringJoiner(",").add("i=" + i).add("mybaby=" + mybaby).add("nickname=" + nickname).toString();
}
}
可以发现mother对象中,Baby类型的实例变量mybaby所引用的对象也进行了序列化,String类型静态变量name未参与序列化,以及transient修饰的String实例变量nickname也被初始化为null。
Remote
java.rmi.Remote标记接口主要用来通知JAVA虚拟机该类所包含的方法可以从非本地虚拟机上调用。如果不声明该接口,进行远程调用的时候,JVM抛出异常。
反序列化相关
Externalizable 是一个手动序列化接口,就是自己来决定序列化的内容
- 序列化源码在:ObjectInputStream#readOrdinaryObject、ObjectInputStream#newInstance ```java private Object readOrdinaryObject(boolean unshared) throws IOException{ //… obj = desc.isInstantiable() ? desc.newInstance() : null; //… return obj; }
Object newInstance() throws InstantiationException, InvocationTargetException, UnsupportedOperationException{ requireInitialized(); if (cons != null) { try { return cons.newInstance();//走到这里 cons是对象obj所属类父类class java.lang.Object的无参构造函数 } catch (IllegalAccessException ex) { // should not occur, as access checks have been suppressed throw new InternalError(ex); } } else { throw new UnsupportedOperationException(); } }
private Object readOrdinaryObject(boolean unshared) throws IOException{ ObjectStreamClass desc = readClassDesc(false);//这里 obj = desc.isInstantiable() ? desc.newInstance() : null; return obj; }
private ObjectStreamClass readClassDesc(boolean unshared) throws IOException { byte tc = bin.peekByte(); ObjectStreamClass descriptor; switch (tc) { case TC_NULL: descriptor = (ObjectStreamClass) readNull(); break; … case TC_CLASSDESC: descriptor = readNonProxyDesc(unshared);// 走到这里 break; default: throw new StreamCorruptedException( String.format(“invalid type code: %02X”, tc)); } if (descriptor != null) { validateDescriptor(descriptor); } return descriptor; }
private ObjectStreamClass readNonProxyDesc(boolean unshared) throws IOException{ desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false)); return desc; }
void initNonProxy(ObjectStreamClass model, Class<?> cl, ClassNotFoundException resolveEx, ObjectStreamClass superDesc) throws InvalidClassException { if (cl != null) { osc = lookup(cl, true); }
static ObjectStreamClass lookup(Class<?> cl, boolean all) { if (entry == null) { entry = new ObjectStreamClass(cl); } }
private ObjectStreamClass(final Class<?> cl) { this.cl = cl; name = cl.getName(); isProxy = Proxy.isProxyClass(cl); isEnum = Enum.class.isAssignableFrom(cl); serializable = Serializable.class.isAssignableFrom(cl); externalizable = Externalizable.class.isAssignableFrom(cl);
Class<?> superCl = cl.getSuperclass();
superDesc = (superCl != null) ? lookup(superCl, false) : null;
localDesc = this;
if (serializable) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
if (isEnum) {
suid = Long.valueOf(0);
fields = NO_FIELDS;
return null;
}
if (cl.isArray()) {
fields = NO_FIELDS;
return null;
}
suid = getDeclaredSUID(cl);
try {
fields = getSerialFields(cl);
computeFieldOffsets();
} catch (InvalidClassException e) {
serializeEx = deserializeEx =
new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS;
}
if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);//这里是重点,真正的获取构造函数的地方
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);
}
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
}
});
} else {
suid = Long.valueOf(0);
fields = NO_FIELDS;
}
try {
fieldRefl = getReflector(fields, this);
} catch (InvalidClassException ex) {
// field mismatches impossible when matching local fields vs. self
throw new InternalError(ex);
}
if (deserializeEx == null) {
if (isEnum) {
deserializeEx = new ExceptionInfo(name, "enum type");
} else if (cons == null) {
deserializeEx = new ExceptionInfo(name, "no valid constructor");
}
}
for (int i = 0; i < fields.length; i++) {
if (fields[i].getField() == null) {
defaultSerializeEx = new ExceptionInfo(
name, "unmatched serializable field(s) declared");
}
}
initialized = true;
}
//获取构造函数 private static Constructor<?> getSerializableConstructor(Class<?> cl) { Class<?> initCl = cl; while (Serializable.class.isAssignableFrom(initCl)) { if ((initCl = initCl.getSuperclass()) == null) { // 看这里,获取的是基类的构造函数 return null; } } try { Constructor<?> cons = initCl.getDeclaredConstructor((Class<?>[]) null); int mods = cons.getModifiers(); if ((mods & Modifier.PRIVATE) != 0 || ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 && !packageEquals(cl, initCl))) { return null; } cons = reflFactory.newConstructorForSerialization(cl, cons); cons.setAccessible(true); return cons; } catch (NoSuchMethodException ex) { return null; } } } ```
推荐文章
- Java反序列化是否使用默认构造函数初始化对象:https://www.cnblogs.com/kendoziyu/p/how-create-java-bean-when-java-serialization.html
