一文搞懂标识接口/空接口和标记注解

标识接口和标记注解本质意义一样,只是因为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());
    }
}

保证只有实现了该接口的方法才能进行序列化与反序列化

在进行序列化操作时,会判断要被序列化的类是否是EnumArraySerializable类型,如果都不是则直接抛出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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Apple_Web

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值