Java批注是我们程序源代码的元数据(有关数据的数据)。 Java SE提供了几个预定义的注释。此外,我们还可以根据需要创建自定义注释。
如果您不知道什么是注释,请访问Java注释教程。
这些注释可以分类为:
1. 预定义的注释
-
@Deprecated
-
@Override
-
@SuppressWarnings
-
@SafeVarargs
-
@FunctionalInterface
2. 自定义注释
3. 元注释
-
@Retention
-
@Documented
-
@Target
-
@Inherited
-
@Repeatable
预定义的注释类型
1. @已弃用
@Deprecated
批注是一个标记批注,它指示不赞成使用的元素(类,方法,字段等),并已由较新的元素替换。
其语法为:
@Deprecated
accessModifier returnType deprecatedMethodName() { ... }
当程序使用已声明为已弃用的元素时,编译器将生成警告。
我们使用Javadoc @deprecated
标记来记录已弃用的元素。
/**
* @deprecated
* why it was deprecated
*/
@Deprecated
accessModifier returnType deprecatedMethodName() { ... }
示例1:@Deprecated注释示例
class Main {
/**
* @deprecated
* This method is deprecated and has been replaced by newMethod()
*/
@Deprecated
public static void deprecatedMethod() {
System.out.println("Deprecated method");
}
public static void main(String args[]) {
deprecatedMethod();
}
}
输出
Deprecated method
2. @Override
@Override
批注指定子类的方法使用相同的方法名称,返回类型和参数列表覆盖超类的方法。
重写方法时,并非必须使用@Override
。但是,如果使用它,则在覆盖方法时,如果出现错误(例如错误的参数类型),则编译器将给出错误。
示例2:@Override注释示例
class Animal {
// overridden method
public void display(){
System.out.println("I am an animal");
}
}
class Dog extends Animal {
// overriding method
@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
在此示例中,通过使对象Dog1为Dog类,我们可以调用其方法printMessage() ,然后执行display()
语句。
由于在两个类中都定义了display()
,所以Dog子类的方法将覆盖Animal的超类方法。因此,将调用子类的display()
。
3. @SuppressWarnings
顾名思义, @SuppressWarnings
批注指示编译器禁止在程序执行时生成警告。
我们可以指定要禁止的警告类型。可以禁止的警告是特定于编译器的,但警告分为两类: 弃用和未检查 。
为了禁止显示特定类别的警告,我们使用:
@SuppressWarnings("warningCategory")
例如,
@SuppressWarnings("deprecated")
为了禁止显示多种警告,我们使用:
@SuppressWarnings({"warningCategory1", "warningCategory2"})
例如,
@SuppressWarnings({"deprecated", "unchecked"})
deprecated
类别将指示编译器在使用不推荐使用的元素时禁止显示警告。
unchecked
类别会指示编译器在使用原始类型时禁止显示警告。
并且,未定义的警告将被忽略。例如,
@SuppressWarnings("someundefinedwarning")
示例3:@SuppressWarnings注释示例
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")
批注,我们可以避免编译器警告。
4. @SafeVarargs
@SafeVarargs
批注断言,带注释的方法或构造函数不会对其varargs(可变数量的参数)执行不安全的操作。
我们只能在无法覆盖的方法或构造函数上使用此注释。这是因为覆盖它们的方法可能会执行不安全的操作。
在Java 9之前,我们只能在final或static方法上使用此注释,因为它们不能被覆盖。现在,我们也可以将此注释用于私有方法。
示例4:@SafeVarargs注释示例
import java.util.*;
class Main {
private void displayList(List... lists) {
for (List list : lists) {
System.out.println(list);
}
}
public static void main(String args[]) {
Main obj = new Main();
List universityList = Arrays.asList("Tribhuvan University", "Kathmandu University");
obj.displayList(universityList);
List 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 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
... lists
指定List
类型的变长参数displayList()
可以具有零个或多个参数。
上面的程序编译没有错误,但是当不使用@SafeVarargs
批注时会发出警告。
当在上面的示例中使用@SafeVarargs
批注时,
@SafeVarargs
private void displayList(List... lists) { ... }
我们得到相同的输出,但没有任何警告。当使用此批注时,未经检查的警告也会被删除。
5. @FunctionalInterface
Java 8首先引入了此@FunctionalInterface
批注。此注释指示使用它的类型声明是一个功能接口。一个功能接口只能有一个抽象方法。
示例5:@FunctionalInterface注释示例
@FunctionalInterface
public interface MyFuncInterface{
public void firstMethod(); // this is an abstract method
}
如果我们添加另一个抽象方法,那么说
@FunctionalInterface
public interface MyFuncInterface{
public void firstMethod(); // this is an abstract method
public void secondMethod(); // this throws compile error
}
现在,当我们运行程序时,我们将收到以下警告:
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(); // this is an abstract method
default void secondMethod() { ... }
default void thirdMethod() { ... }
}
自定义注释
也可以创建我们自己的自定义注释。
其语法为:
[Access Specifier] @interface {
DataType () [default value];
}
这是您需要了解的有关自定义注释的信息:
- 可以使用
@interface
加上注释名称来创建注释。 - 批注可以具有看起来像方法的元素,但是它们没有实现。
- 默认值为可选。参数不能为空值。
- 方法的返回类型可以是原始,枚举, 字符串,类名或这些类型的数组。
示例6:自定义注释示例
@interface MyCustomAnnotation {
String value() default "default value";
}
class Main {
@MyCustomAnnotation(value = "programiz")
public void method1() {
System.out.println("Test method 1");
}
public static void main(String[] args) throws Exception {
Main obj = new Main();
obj.method1();
}
}
输出
Test method 1
元注释
元注释是应用于其他注释的注释。
1. @保留
@Retention
注释指定注释可用的级别。
其语法为:
@Retention(RetentionPolicy)
保留策略有3种类型:
- RetentionPolicy.SOURCE-注释仅在源级别可用,并且被编译器忽略。
- RetentionPolicy.CLASS-注释在编译时可供编译器使用,但被Java虚拟机(JVM)忽略。
- RetentionPolicy.RUNTIME-注释可用于JVM。
例如,
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotation{ ... }
2. @记录
默认情况下,自定义注释不包含在官方Java文档中。为了将注释包含在Javadoc文档中,我们使用@Documented
注释。
例如,
@Documented
public @interface MyCustomAnnotation{ ... }
3. @Target
我们可以使用@Target
注释将注释限制为应用于特定目标。
其语法为:
@Target(ElementType)
ElementType
可以具有以下类型之一:
Element Type | Target |
---|---|
ElementType.ANNOTATION_TYPE |
Annotation type |
ElementType.CONSTRUCTOR |
Constructors |
ElementType.FIELD |
Fields |
ElementType.LOCAL_VARIABLE |
Local variables |
ElementType.METHOD |
Methods |
ElementType.PACKAGE |
Package |
ElementType.PARAMETER |
Parameter |
ElementType.TYPE |
Any element of class |
例如,
@Target(ElementType.METHOD)
public @interface MyCustomAnnotation{ ... }
在此示例中,我们仅将此注释的使用限制为方法。
注意:如果未定义目标类型,则注释可用于任何元素。
4. @继承
默认情况下,注释类型不能从超类继承。但是,如果我们需要将注释从超类继承到子类,则可以使用@Inherited
注释。
其语法为:
@Inherited
例如,
@Inherited
public @interface MyCustomAnnotation { ... }
@MyCustomAnnotation
public class ParentClass{ ... }
public class ChildClass extends ParentClass { ... }
5. @可重复
由@Repeatable
标记的@Repeatable
可以多次应用于同一声明。
@Repeatable(Universities.class)
public @interface University {
String name();
}
@Repeatable
批注中定义的值是容器批注。容器注释具有上述可重复注释的数组类型的变量值 。在这里, Universities
是包含的注释类型。
public @interface Universities {
University[] value();
}
现在, @University
批注可以在同一声明上多次使用。
@University(name = "TU")
@University(name = "KU")
private String uniName;
如果需要检索注释数据,则可以使用Reflection API。
要检索注释值,我们使用在反射API中定义的getAnnotationsByType()
或getAnnotations()
方法。