标识接口和标记注解本质意义一样,只是因为JDK一开始没有注解(JDK1.5才有)这个概念,所以才有了标识接口,现在一般通过注解实现标识!
基本概念
标识接口是没有任何方法和属性的接口。标识接口不对实现它的类有任何语义上的要求,它仅仅表明实现它的类属于一个特定的类型。
标识接口在Java语言中有一些很著名的应用,例如我们常用的ArrayList类,它的类实现接口如下:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
仔细看 RandomAccess、RandomAccess、Serializable的接口可以看到,内部都是一些空接口,没有定义接口方法
public interface RandomAccess {
}
public interface Cloneable {
}
public interface Serializable {
}
标识接口,当一个类实现了一个标识接口之后就像是给自己打了个标签。
自定义标识接口
// 这是一个标记接口,不含有任何方法
public interface MyMark {
}
// 类 ClassA 实现了 MyMark 接口
public class ClassA implements MyMark {
private String aa;
}
// 类 ClassB 没有实现 MyMark 接口
public class ClassB {
private String bb;
}
// 利用 instanceof 判断 类的实例是否 持有标记接口的标签
public class MyTest {
public static void main(String[] args) {
ClassA classA = new ClassA();
ClassB classB = new ClassB();
if(classA instanceof MyMark){
System.out.println("类 ClassA 实现了接口 MyMark,可以进行其他操作");
}else{
System.out.println("类 ClassA 未实现接口 MyMark");
}
if(classB instanceof MyMark){
System.out.println("类 ClassB 实现了接口 MyMark,可以进行其他操作");
}else{
System.out.println("类 ClassB 未实现接口 MyMark");
}
}
}
控制台输出:
类 ClassA 实现了接口 MyMark,可以进行其他操作
类 ClassB 未实现接口 MyMark
JDK源码实现
以Serializable为例:
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法进行序列化或反序列化。 可序列化类的所有子类型本身都是可序列化的。
查阅Serializable的源码,就会发现,他只是一个空的接口,里面什么东西都没有。Serializable接口没有方法或字段,仅用于标识可序列化的语义。 但是,如果一个类没有实现这个接口,想要被序列化的话,就会抛出java.io.NotSerializableException异常。
Serializable是在执行序列化的过程中,会执行到以下代码:
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());
}
}
保证只有实现了该接口的方法才能进行序列化与反序列化
在进行序列化操作时,会判断要被序列化的类是否是Enum
、Array
和Serializable
类型,如果都不是则直接抛出NotSerializableException
。
大家也许会问,在Spring里满天飞的注解(Annotation)不是最好的用来维护元数据的方式么?确实,Annotation能声明在Java包、类、字段、方法、局部变量、方法参数等的前面用于维护元数据的目的,既灵活又方便。然而这么好的东西,只有在JDK1.5之后才能用。JDK1.5之前维护元数据的重任就落在标记接口上了。
大家看另一个标记接口,Cloneable。下图第51行清晰标注了该接口从JDK1.0起就有了。
/**
* A class implements the <code>Cloneable</code> interface to
* indicate to the {@link java.lang.Object#clone()} method that it
* is legal for that method to make a
* field-for-field copy of instances of that class.
* <p>
* Invoking Object's clone method on an instance that does not implement the
* <code>Cloneable</code> interface results in the exception
* <code>CloneNotSupportedException</code> being thrown.
* <p>
* By convention, classes that implement this interface should override
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.
* <p>
* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface. Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.
*
* @author unascribed
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}
JDK源代码里的Clone方法的注释也清晰注明了,如果一个类没有实现Cloneable接口,在执行clone方法时会抛出CloneNotSupportedException异常。
/**
*.....
* The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface {@code Cloneable}, then a
* {@code CloneNotSupportedException} is thrown. Note that all arrays
* are considered to implement the interface {@code Cloneable} and that
* the return type of the {@code clone} method of an array type {@code T[]}
* is {@code T[]} where T is any reference or primitive type.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p>
* The class {@code Object} does not itself implement the interface
* {@code Cloneable}, so calling the {@code clone} method on an object
* whose class is {@code Object} will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
自定义标记注解
标记注解是特殊类型的注解,其中不包含成员。标记注解的唯一目的就是标记声明,因此,这种注解作为注解而存在的理由是充分的。确定标记注解是否存在的最好方式是使用isAnnotationPresent()
方法,该方法是由AnnotatedElement
接口定义的。
下面是一个使用标记注解的例子。因为标记注解不包含成员,所以只需要简单地判断其是否存在即可。
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
}
public class MyTest {
public static void main(String args[]) {
myMeth();
}
/**
*用注解标记一个方法
*/
@MyAnnotation
public static void myMeth() {
MyAnnotation ob = new MyAnnotation();
try {
Method m = ob.getClass().getMethod("myMeth");
// 判断该方法是否有自定义注解,做对应的操作
if(m.isAnnotationPresent(MyAnnotation.class))
System.out.println("MyAnnotation is present.");
} catch (NoSuchMethodException exc) {
System.out.println("Method Not Found.");
}
}
}
输出如下所示,@MyMarker确实是存在的:
MyMarker is present.
在这个程序中,应用@MyMarker时后面不需要有圆括号。因此,只通过名称即可应用@MyMarker,如下所示:@MyMarker
提供空的圆括号虽然不是错误,但不是必需的。
参考文章:
https://www.cnblogs.com/myseries/p/10876314.html