Java 注释
Java面向对象设计 - Java注释
什么是注释?
Java中的注释允许我们将元数据与程序元素相关联。
程序元素可以是包,类,接口,类的字段,局部变量,方法,方法的参数,枚举,注释,泛型类型/方法声明中的类型参数,等等。
我们可以在Java程序中注释任何声明或类型。注释在任何其他修饰符(public,private,final,static等)的程序元素的声明中用作修饰符。
与修饰符不同,注释不会修改程序元素的含义。它是程序元素的注释。
声明注释类型
声明注释类型类似于声明接口类型。
注释类型是一种特殊类型的接口类型。
我们使用前面带有@符号的interface关键字来声明注释类型。
以下是声明注释类型的一般语法:
<modifiers> @interface <annotation-type-name> { // Annotation type body }
注释声明的<modifiers>与接口声明的相同。
我们可以将注解类型声明为public或package级别。
@符号和interface关键字可以用空格分隔,也可以放在一起。
按照惯例,它们被放在一起作为 @interface
。
interface关键字后面是注释类型名称。它应该是有效的Java标识符。
注释类型正文放在大括号中。
以下代码创建注释以使用版本信息注释程序元素。
public @interface Version { int major(); int minor(); }
我们在Version注释类型中声明了两个抽象方法:major()和minor()。
注释类型中的抽象方法称为元素。
注释可以声明零个或多个元素,它们被声明为抽象方法。抽象方法名称是注释类型的元素的名称。
在上面的代码中,我们为Version注释类型声明了两个元素major和minor。两个元素的数据类型都是int。
我们需要导入注释类型,方式与导入任何其他类型的方式相同。
注释实例
要创建注释类型的实例并使用它来注释程序元素,我们使用以下语法:
@annotationType(name1=value1, name2=value2, names3=values3...)
注释类型前面带有一个@符号,后面跟着用括号括起来的逗号分隔的name = value对列表。
name=value对中的名称是在注释类型中声明的元素的名称,值由用户提供。
name=value对不必按照在注释类型中声明的顺序显示。
以下代码使用Version类型的注释,主要元素值设置为1,次要元素值设置为0。
@Version(major=1, minor=0)
我们可以使用注释类型的完全限定名
@com.w3cschool.annotation.Version(major=0, minor=1)
我们可以将类声明注释为
@Version(major=1, minor=0) public class VersionTest { }
我们可以将程序元素的注释与其他修饰符混合使用。
@Version(major=1, minor=0) public class VersionTest { } public @Version(major=1, minor=0) class VersionTest { }
完整的源代码
@interface Version { int major(); int minor(); } @Version(major = 1, minor = 0) public class Main { @Version(major = 1, minor = 1) private int xyz = 110; @Version(major = 1, minor = 0) public Main() { } @Version(major = 1, minor = 1) public Main(int xyz) { this.xyz = xyz; } @Version(major = 1, minor = 0) public void printData() { } @Version(major = 1, minor = 1) public void setXyz(int xyz) { @Version(major = 1, minor = 2) int newValue = xyz; this.xyz = xyz; } }
Java 对注释类型的限制
Java面向对象设计 - Java对注释类型的限制
注释类型的限制
注释类型不能从另一个注释类型继承。
每个注释类型都隐式地继承java.lang.annotation.Annotation接口,其声明如下:
package java.lang.annotation; public interface Annotation { boolean equals(Object obj); int hashCode(); String toString(); Class<? extends Annotation> annotationType(); }
注释类型中的方法声明不能指定任何参数。
注释类型中的方法声明不能有throws子句。
在注释类型中声明的方法的返回类型必须是以下类型之一:
- 任何原始类型:byte,short,int,long,float,double,boolean和char
- java.lang.String
- java.lang.Class
- 枚举类型
- 注释类型
- 上述任何类型的数组,例如String[],int[]等。
返回类型不能是嵌套数组。例如,您不能具有String[][]或int[][]的返回类型。
您可以如下所示声明注释方法:
public @interface MyAnnotation { Class element1(); // Any Class type Class<Test> element2(); // Only Test class type Class<? extends Test> element3(); // Test or its subclass type }
注释类型不能是泛型的。
Java 注释默认值
Java面向对象设计 - Java注释默认值
我们可以为注释中的元素定义默认值。我们不需要为带有默认值的注释元素提供值。
默认值可以使用以下一般语法定义:
<modifiers> @interface <annotation type name> { <data-type> <element-name>() default <default-value>; }
关键字 default
指定默认值。
默认值必须是与元素的数据类型兼容的类型。
以下代码通过将其minor元素的默认值指定为零来创建Version注释类型,如下所示:
public @interface Version { int major(); int minor() default 0; // zero as default value for minor }
例子
以下代码显示如何使用默认值使用注释。
@Version(major=1) // minor is zero, which is its default value @Version(major=2, minor=1) // minor is 1, which is the specified value
以下代码显示如何为数组和其他数据类型指定默认值:
public @interface Version { double d() default 1.89; int num() default 1; int[] x() default { 1, 2 }; String s() default "Hello"; String[] s2() default { "abc", "xyz" }; Class c() default Exception.class; Class[] c2() default { Exception.class, java.io.IOException.class }; }
Java 注释用法
Java面向对象设计 - Java注释用法
注释元素的提供值必须是编译时常量表达式,我们不能使用null作为注释中任何类型元素的值。
原始类型
注释类型中的元素的数据类型可以是任何基本数据类型:byte,short,int,long,float,double,boolean和char。
Version注释类型声明两个元素,major和minor,并且都是int数据类型。
以下代码声明了注释类型:
public @interface MyAnnotation { byte a(); short b(); int c(); long d(); float e(); double f(); boolean g(); char h(); }
@MyAnnotation(a=1, b=2, c=3, d=4, e=12.34F, f=1.89, g=true, h="Y")
我们可以使用编译时常量表达式来指定注释元素的值。
以下两个版本注释实例有效:
@Version(major=2+1, minor=(int)13.2) @Version(major=3, minor=13)
String类型
我们可以在注释类型中使用String类型的元素。
以下代码定义了名为Name的注释类型。它有两个元素,first和last,它们是String类型。
public @interface Name { String first(); String last(); } @Name(first="Tom", last="Smith") public class NameTest { @Name(first="Jack", last="Iaan") public void aMethod() { } }
在String类型的元素的值表达式中使用字符串连接运算符+是有效的。
@Name(first="Ja" + "ck", last="Ia" + "an")
Class类型
以下代码显示如何使用类类型作为注释值。
import java.io.IOException; @interface MyAnnotation { Class<? extends Throwable> willThrow() default java.lang.Throwable.class; } public class Main { @MyAnnotation(willThrow = IOException.class) public static void testCase1() { // Code goes here } @MyAnnotation() public static void testCase2() { } }
枚举类型
注释可以具有枚举类型的元素。
enum Level { PENDING, FAILED, PASSED; } @interface Review { Level status() default Level.PENDING; String comments() default ""; } @Review(status = Level.PASSED) public class Main { }
注释类型
我们可以使用注释类型作为另一个注释类型的声明内的元素的类型。
要为注释类型的元素提供值,请使用用于创建注释类型实例的语法。
@interface Name { String first(); String last(); } @interface Version { int major(); int minor() default 0; // zero as default value for minor } @interface Description { Name name(); Version version(); String comments() default ""; } @Description(name = @Name(first = "Tom", last = "Smith"), version = @Version(major = 1, minor = 2), comments = "Just a test class") public class Main { }
数组类型注释元素
注释可以具有数组类型的元素。数组类型可以是以下类型之一:
- 原始类型
- java.lang.String type
- java.lang.Class type
- 枚举类型
- 注释类型
我们需要在大括号中指定数组元素的值。
数组的元素由逗号分隔。
@interface ItemList { String[] items(); } @ItemList(items = { "A", "B" }) public class Main { }
如果数组中只有一个元素,则允许省略括号。
@ToDo(items={"A"}) @ToDo(items="A")
传递一个空数组
@ToDo(items={})
速记注释语法
假设我们有一个注释类型如下。
public @interface Enabled { boolean status() default true; }
要使用带有默认值的Enabled注释类型注释程序元素,我们可以使用@Enabled()语法。
我们不需要为status元素指定值,因为它具有默认值。
我们可以进一步省略括号。
@Enabled public class Main { } @Enabled() public class Main { }
只有一个元素的注释类型具有速记语法。
如果注释类型只有一个具有命名值的元素,我们可以省略name = value对中的名称。
以下代码声明了公司注释类型,它只有一个名为value的元素:
public @interface Company { String value(); }
当使用公司注释时,我们可以省略name = value对的名称。
@Company(value="Inc.") public class Test { }
变成
@Company("Inc.") public class Test { }
以下代码显示了如果元素数据类型是数组,如何使用此缩写。
public @interface Item { String[] value(); } @Item({"A", "B"}) public class Test { }
如果我们在数组注释类型中只指定一个元素,我们可以进一步省略大括号。
@Item("A") public class Test { }
如果我们在数组注释类型中只指定一个元素,我们可以进一步省略大括号。
Java 注释类型
Java面向对象设计 - Java注释类型
标记注释类型
标记注释类型是没有元素的注释类型,甚至没有默认值。
标记注释由注释处理工具使用。
public @interface Marker { } @Marker public class Main{ }
元注释类型
元注释类型是注释类型,用于注释其他注释类型。
元注释类型是Java类库的一部分。它们在包java.lang.annotation中声明。
以下注释类型是元注释类型:
- Target
- Retention
- Inherited
- Documented
- Repeatable
- Native
Target注释类型
目标注释类型注释注释类型以设置上下文以使用注释类型。
它只有一个名为value的元素。其值元素是java.lang.annotation.ElementType枚举类型的数组。
下表列出了ElementType枚举中的所有常量。
常量名称 | 描述 |
---|---|
ANNOTATION_TYPE | 注释另一个注释类型声明。 这使得注释类型为元注释。 |
CONSTRUCTOR | 注释构造函数。 |
FIELD | 注释字段和枚举常量。 |
LOCAL_VARIABLE | 注释局部变量。 |
METHOD | 注释方法。 |
PACKAGE | 注释包声明。 |
PARAMETER | 注释参数。 |
TYPE | 注释类,接口(包括注释类型)或枚举声明。 |
TYPE_PARAMETER | 在通用类,接口,方法等中注释类型参数。 |
TYPE_USE | 注释所有类型的使用。 |
以下版本注释类型具有目标元注释,其指定Version注释类型可以与仅有三种类型的程序元素一起使用:任何类型(类,接口,枚举和注释类型),构造函数和方法。
import java.lang.annotation.Target; import java.lang.annotation.ElementType; @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD }) public @interface Version { int major(); int minor(); }
版本注释不能用于除其Target注释中指定的三种类型之外的任何程序元素。
import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target({ ElementType.TYPE_USE }) @interface MyAnno { } @Target({ ElementType.TYPE_USE }) @interface MyAnno2 { } public class Main { public void processData() throws @MyAnno Exception { int roundedValue = (@MyAnno2 int) .02; Main t = new @MyAnno Main(); } }
如果我们不与注释目标注释类型注释类型,注释类型可以用作任何声明修饰符,除了一个类型参数声明。
保留注释
保留注释设置注释类型的保留策略。
注释可以在三个级别保留。
- 仅源代码
- 仅类文件。默认行为。
- 类文件和运行时
保留元注释类型指定Java应如何保留注释。
如果注释类型具有“仅限源代码”保留策略,则在编译到类文件中时将删除其实例。
如果保留策略是“仅类文件”,则其实例将保留在类文件中,但不能在运行时读取。
如果保留策略为“类文件和运行时”,则注释实例保留在类文件中,并且它们可在运行时读取。
保留元注释类型声明一个名为value的元素,它是java.lang的。 annotation.RetentionPolicy枚举类型。
RetentionPolicy枚举有三个常量SOURCE,CLASS和RUNTIME,它们分别用于指定仅源,仅类和类和运行时的保留策略。
以下代码在版本注释类型上使用保留元注释。它指定版本注释应该在运行时可用。
import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @interface Version { int major(); int minor(); }
如果我们不对注释类型使用保留元注释,则其保留策略仅默认为类文件。我们将无法在运行时读取这些注释。
在类文件中或在运行时,局部变量声明上的注释永远不可用,而与注释类型的保留策略无关。
继承注释类型
继承的注释类型是标记元注释类型。
如果注释类型使用Inherited元注释注释,则其实例将由子类声明继承。
如果使用注释类型注释除类声明以外的任何元素,它没有任何效果。
以下代码显示了@Inherited元注释类型的效果。
import java.lang.annotation.Inherited; @interface Ann2 { int id(); } @Inherited @interface Ann3 { int id(); } @Ann2(id = 1) @Ann3(id = 2) class A { } // Class B inherits Ann3(id=2) annotation from the class A class B extends A { }
文档化注释
文档化注释类型是标记元注释类型。
如果注释类型用Documented注释注释,Javadoc工具将为其所有实例生成文档。
import java.lang.annotation.Documented; @Documented @interface Version { int major(); int minor(); } @Version(major = 1, minor = 0) public class Main { }
当使用Javadoc工具为Main类生成文档时,Main类声明上的版本注释也会作为文档的一部分生成。
可重复注释
Java 8添加了可重复元注释类型。
注释类型声明必须用@Repeatable注释注释,如果我们在一个单独的代码元素上重复使用它。
Repeatable注释类型只有一个名为value的元素,其类型是另一个注释类型的类类型。
import java.lang.annotation.Repeatable; @interface LogHistory { Log[] value(); } @Repeatable(LogHistory.class) @interface Log { String date(); String comments(); } @Log(date = "01/01/2014", comments = "B") @Log(date = "01/21/2014", comments = "A") public class Main { public static void process() { } }
本机注释
Native注释类型是元注释,用于注释可以从本机代码引用的字段。它是一个标记注释。
Java 标准注释
Java面向对象设计 - Java标准注释
Java API定义了许多标准注释类型。
以下部分讨论了在java.lang包中定义的四种最常用的标准注释。
- Deprecated
- Override
- SuppressWarnings
- FunctionalInterface
已弃用的注释类型
弃用的注释类型是标记注释类型。它标记了已弃用的代码元素。
使用已弃用的程序元素将导致编译器生成警告。
@Deprecate class MyClass { private MyClass() { } public static MyClass getInstance() { MyClass dt = new MyClass(); return dt; } } public class Main { public static void main(String[] args) { MyClass dt; // Generates a compile-time note } }
重写注释类型
重写注释类型是只能在方法上使用的标记注释类型。
它表示方法重写在其超类中声明的方法。
如果我们要覆盖超类中的一个方法,建议使用@Override注释注释覆盖的方法。
编译器将确保注释的方法真正覆盖超类中的一个方法。
如果注释的方法不覆盖超类中的方法,编译器将生成错误。
class A { public void m1() { } } class B extends A { @Override public void m1() { } }
SuppressWarnings注释类型
SuppressWarnings用于抑制命名的编译器警告。它声明一个名为value的元素,其数据类型是一个String数组。
import java.util.ArrayList; public class Main { @SuppressWarnings("unchecked") public void test() { @SuppressWarnings("rawtypes") ArrayList list = new ArrayList(); list.add("Hello"); // The compiler issues an unchecked warning } }
FunctionalInterface注释类型
具有一个抽象方法声明的接口被称为功能接口。
如果使用此注释注释的接口不是函数接口,则会生成编译时错误。
@FunctionalInterface interface MyThread{ void run(); }
只有一个抽象方法的接口是一个功能接口,不管它是否用@FunctionalInterface注释注释。
注释Java包
要注释一个Java包,创建一个名为package-info.java的文件,并将注释包声明放在其中。
以下代码显示了package-info.java文件的内容。
// package-info.java @Version(major=1, minor=0) package com.w3cschool.annotation;
Java 注释反射
Java面向对象设计 - Java注释反射
程序元素上的注释是Java对象。
允许您访问其注释的程序元素实现java.lang.reflect.AnnotatedElement接口。
以下类实现了AnnotatedElement接口:
AnnotatedElement接口的方法用于访问以下列出的对象类型的注释。
java.lang.Class java.lang.reflect.Executable java.lang.reflect.Constructor java.lang.reflect.Field java.lang.reflect.Method java.lang.reflect.Parameter java.lang.Package java.lang.reflect.AccessibleObject
注释类型必须使用运行时的保留策略通过保留元注释注释,以便在运行时访问它。
例子
假设你有一个Test类,并且你想打印它的所有注释。以下代码片段将打印Test类的类声明上的所有注释:
import java.lang.annotation.Annotation; @SuppressWarnings("unchecked") @Deprecated public class Main { public static void main(String[] argv) { // Get the class object reference Class<Main> c = Main.class; // Get all annotations on the class declaration Annotation[] allAnns = c.getAnnotations(); System.out.println("Annotation count: " + allAnns.length); // Print all annotations for (Annotation ann : allAnns) { System.out.println(ann); } } }
Annotation接口的toString()方法返回注释的字符串表示形式。
例2
以下代码显示了如何获取特定注释。
import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @Documented @interface Version { int major(); int minor(); } @Version(major=1,minor=2) public class Main { public static void main(String[] argv) { Class<Main> c = Main.class; Version v = c.getAnnotation(Version.class); if (v == null) { System.out.println("Version annotation is not present."); } else { int major = v.major(); int minor = v.minor(); System.out.println("Version: major=" + major + ", minor=" + minor); } } }
上面的代码生成以下结果。
例3
以下代码显示了如何访问方法的注释。
import java.lang.annotation.Annotation; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; @Retention(RetentionPolicy.RUNTIME) @interface Version { int major(); int minor(); } @Version(major = 1, minor = 0) class AccessAnnotation { @Version(major = 1, minor = 1) public void testMethod1() { } @Version(major = 1, minor = 2) @Deprecated public void testMethod2() { } } public class Main { public static void main(String[] args) { Class<AccessAnnotation> c = AccessAnnotation.class; System.out.println("Annotations for class:" + c.getName()); printAnnotations(c); System.out.println("Method annotations:"); Method[] m = c.getDeclaredMethods(); for (int i = 0; i < m.length; i++) { System.out.println("Annotations for method:" + m[i].getName()); printAnnotations(m[i]); } } public static void printAnnotations(AnnotatedElement programElement) { Annotation[] annList = programElement.getAnnotations(); for (int i = 0; i < annList.length; i++) { System.out.println(annList[i]); if (annList[i] instanceof Version) { Version v = (Version) annList[i]; int major = v.major(); int minor = v.minor(); System.out.println("Found Version annotation: " + "major =" + major + ", minor=" + minor); } } } }
上面的代码生成以下结果。
例4
以下代码显示了如何在运行时访问可重复注释的实例。
import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @interface LogHistory { Log[] value(); } @Repeatable(LogHistory.class) @interface Log { String date(); String comments(); } @Log(date = "02/01/2014", comments = "A") @Log(date = "01/22/2014", comments = "B") public class Main { public static void main(String[] args) { Class<Main> mainClass = Main.class; Log[] annList = mainClass.getAnnotationsByType(Log.class); for (Log log : annList) { System.out.println("Date=" + log.date() + ", Comments=" + log.comments()); } Class<LogHistory> containingAnnClass = LogHistory.class; LogHistory logs = mainClass.getAnnotation(containingAnnClass); for (Log log : logs.value()) { System.out.println("Date=" + log.date() + ", Comments=" + log.comments()); } } }
上面的代码生成以下结果。