在本教程中,我们将借助示例学习不同类型的Java注解。
Java注解是我们程序源代码的元数据(有关数据的数据)。Java SE提供了几个预定义的注解。此外,我们还可以根据需要创建自定义注解。
如果您不知道什么是注解,请访问Java注解教程。
这些注解可以分类为:
1. 预定义的注解
@Deprecated
@Override
@SuppressWarnings
@SafeVarargs
@FunctionalInterface
2. 自定义注解
3. 元注解
@Retention
@Documented
@Target
@Inherited
@Repeatable
@Deprecated注解是一个标记注解指示元素(类,方法,字段等)已过时,并已被更新的元素取代。
其语法为:
@Deprecated accessModifier returnType deprecatedMethodName() { ... }
当程序使用已声明为已弃用的元素时,编译器将生成警告。
我们使用Javadoc @deprecated标记来记录已弃用的元素。
/** * @deprecated * 为什么它被弃用 */ @Deprecated accessModifier returnType deprecatedMethodName() { ... }
class Main { /** * @deprecated * 此方法已弃用,并已由newMethod()代替 */ @Deprecated public static void deprecatedMethod() { System.out.println("Deprecated method"); } public static void main(String args[]) { deprecatedMethod(); } }
输出结果
Deprecated method
@Override 注解指定子类的方法用相同的方法名、返回类型和参数列表覆盖父类的方法。
@Override在重写方法时,并非必须使用。 但是,如果使用它,则在覆盖方法时,如果出现错误(例如错误的参数类型),则编译器将给出错误。
class Animal { //重写方法 public void display(){ System.out.println("I am an animal"); } } class Dog extends Animal { //重写方法 @Override public void display(){ System.out.println("I am a dog"); } public void printMessage(){ display(); } } class Main { public static void main(String[] args) { Dog dog1 = new Dog(); dog1.printMessage(); } }
输出结果
I am a dog
在本示例中,通过创建Dog类的对象dog1,我们可以调用它的方法printMessage(),然后该方法执行display()语句。
由于display()在两个类中均已定义,因此Dog子类的display()方法将覆盖超类Animal的display()方法。因此,子类的display()被调用。
顾名思义,@SuppressWarnings注解指示编译器禁止执行程序时生成的警告。
我们可以指定要取消的警告类型。可以禁止的警告是特定于编译器的,但警告分为两类:弃用 和 未检查。
为了禁止显示特定类别的警告,我们刻意使用:
@SuppressWarnings("warningCategory")
例如,
@SuppressWarnings("deprecated")
为了禁止显示多个类别警告,我们刻意使用:
@SuppressWarnings({"warningCategory1", "warningCategory2"})
例如,
@SuppressWarnings({"deprecated", "unchecked"})
当我们使用不推荐使用的元素时,deprecated类别指示编译器禁止显示警告。
当我们使用原始类型时,unchecked类别指示编译器禁止显示警告。
并且,未定义的警告将被忽略。例如,
@SuppressWarnings("someundefinedwarning")
class Main { @Deprecated public static void deprecatedMethod() { System.out.println("Deprecated method"); } @SuppressWarnings("deprecated") public static void main(String args[]) { Main depObj = new Main(); depObj. deprecatedMethod(); } }
输出结果
Deprecated method
在这里,deprecatedMethod()已被标记为已弃用,使用时会发出编译器警告。通过使用@SuppressWarnings("deprecated")注解,我们可以避免编译器警告。
@SafeVarargs注解断言,带注解的方法或构造不执行它的可变参数不安全的操作(可变的参数数)。
我们只能在不能被重写的方法或构造函数上使用此注解。这是因为重写它们的方法可能会执行不安全的操作。
在Java 9之前,我们只能在final或static方法上使用此注解,因为它们不能被重写。现在,我们也可以将此注解用于私有方法。
import java.util.*; class Main { private void displayList(List<String>... lists) { for (List<String> list : lists) { System.out.println(list); } } public static void main(String args[]) { Main obj = new Main(); List<String> universityList = Arrays.asList("Tribhuvan University", "Kathmandu University"); obj.displayList(universityList); List<String> programmingLanguages = Arrays.asList("Java", "C"); obj.displayList(universityList, programmingLanguages); } }
警告事项
Type safety: Potential heap pollution via varargs parameter lists Type safety: A generic array of List<String> is created for a varargs parameter
输出结果
Note: Main.java uses unchecked or unsafe operations. [Tribhuvan University, Kathmandu University] [Tribhuvan University, Kathmandu University] [Java, C]
在此,List ... list指定类型为List的变长参数。 这意味着方法displayList()可以具有零个或多个参数。
上面的程序编译没有错误,但是在不使用@SafeVarargs 注解时会发出警告。
在上面的示例中使用@SafeVarargs注解时,
@SafeVarargs private void displayList(List<String>... lists) { ... }
我们得到相同的输出,但没有任何警告。当使用此注解时,未经检查的警告也会被删除。
Java 8首先引入了此@FunctionalInterface注解。此注解表明使用它的类型声明是一个功能接口。一个功能接口只能有一个抽象方法。
@FunctionalInterface public interface MyFuncInterface{ public void firstMethod(); //这是一个抽象的方法 }
如果我们添加另一个抽象方法,那么
@FunctionalInterface public interface MyFuncInterface{ public void firstMethod(); // 这是一个抽象的方法 public void secondMethod(); //这会引发编译错误 }
现在,当我们运行程序时,我们将收到以下警告:
Unexpected @FunctionalInterface annotation @FunctionalInterface ^ MyFuncInterface is not a functional interface multiple non-overriding abstract methods found in interface MyFuncInterface
使用@FunctionalInterface注解不是强制性的。编译器会将满足功能接口定义的任何接口视为功能接口。
我们使用此注解的目的,是确保功能接口只有一种抽象方法。
但是,它可以有任意数量的默认方法和静态方法,因为它们都有实现。
@FunctionalInterface public interface MyFuncInterface{ public void firstMethod(); //这是一种抽象方法 default void secondMethod() { ... } default void thirdMethod() { ... } }
也可以创建我们自己的自定义注解。
其语法为:
[Access Specifier] @interface<AnnotationName> { DataType <Method Name>() [default value]; }
这是您需要了解的有关自定义注解的信息:
注解可以通过使用@interface后面跟注解名称来创建。
注解可以具有看起来像方法的元素,但是它们没有实现。
默认值是可选的。参数不能为空值。
方法的返回类型可以是原始,枚举,字符串,类名或这些类型的数组。
@interface MyCustomAnnotation { String value() default "default value"; } class Main { @MyCustomAnnotation(value = "nhooo") public void method1() { System.out.println("测试方法1"); } public static void main(String[] args) throws Exception { Main obj = new Main(); obj.method1(); } }
输出结果
测试方法1
元注解是应用于其他注解的注解。
@Retention注解指定了该注解可用的最高级别。
其语法为:
@Retention(RetentionPolicy)
有三种类型:
RetentionPolicy.SOURCE - 注解仅在源级别可用,并且被编译器忽略。
RetentionPolicy.CLASS - 注解在编译时可供编译器使用,但Java虚拟机(JVM)会忽略它。
RetentionPolicy.RUNTIME - 注解可用于JVM。
例如,
@Retention(RetentionPolicy.RUNTIME) public @interface MyCustomAnnotation{ ... }
默认情况下,自定义注解不包含在官方Java文档中。要将注解包含在Javadoc文档中,请使用@Documented注解。
例如,
@Documented public @interface MyCustomAnnotation{ ... }
我们可以使用@Target注解将注解限制为应用于特定目标。
其语法为:
@Target(ElementType)
ElementType可有以下几种类型之一:
元素类型 | Target |
---|---|
ElementType.ANNOTATION_TYPE | 注解类型 |
ElementType.CONSTRUCTOR | 构造函数 |
ElementType.FIELD | 字段 |
ElementType.LOCAL_VARIABLE | 局部变量 |
ElementType.METHOD | 方法 |
ElementType.PACKAGE | 包 |
ElementType.PARAMETER | 参数 |
ElementType.TYPE | 用于描述类、接口(包括注解类型) 或enum声明 |
例如,
@Target(ElementType.METHOD) public @interface MyCustomAnnotation{ ... }
在此示例中,我们仅将此注解的使用限制为方法。
注意:如果未定义目标类型,则注解可用于任何元素。
默认情况下,注解类型不能从超类继承。但是,如果需要将注解从超类继承到子类,则可以使用@Inherited注解。
其语法为:
@Inherited
例如,
@Inherited public @interface MyCustomAnnotation { ... } @MyCustomAnnotation public class ParentClass{ ... } public class ChildClass extends ParentClass { ... }
带有@Repeatable标记的注解可以多次应用于同一声明。
@Repeatable(Universities.class) public @interface University { String name(); }
@Repeatable注解中定义的值是容器注解。容器注解具有上述可重复注解数组类型的变量值(value)。在这里,Universities是包含注解类型的。
public @interface Universities { University[] value(); }
现在,@University注解可以在同一声明上多次使用。
@University(name = "TU") @University(name = "KU") private String uniName;
如果需要检索注解数据,可以使用反射。
要检索注解值,我们使用反射API中定义的getAnnotationsByType()或getAnnotations()方法。