注解
注解是一个接口
基本注解
- @Override 指定方法覆载的,强制一个子类必须覆盖父类的方法。
如果负载的名字不对,则会报错:
Method does not override method from its superclass
@Deprecated:表示某个元素或者某个方法已经过时,当其他程序引用该注解注释的
类或方法时,会展示编译告警'getXXX()' is deprecated@SuppressWarnings:抑制编译告警
@SafeVarargs:
public void test5() {
List ints = new ArrayList<>();
ints.add(20);
List<String> ss = ints;
System.out.println(ss.get(0));
}
代码由于泛型擦除,会导致运行时异常。引发这种错误的原因为“堆污染”,档把一个不带泛型
的对象赋值给一个带泛型的变量是,往往会发生这种“堆污染”。
元注解
-
@Retention:只用于修饰注解定义,用于指定被修饰的注解可以保留多长时间,他包含一个RetentionPolicy类型的变量
使用该注解的时候必须为变量赋值RetentionPolicy.CLASS 在class文件中 默认值 JVM无法获取注解信息
RetentionPolicy.RUNTIME 在class文件中 JVM运行时也可以获取注解信息,可以通过烦死获取该注解信息
RetentionPolicy.SOURCE 在源文件中,编译器直接丢弃这种注解 -
@Targer 只能修饰注解 用于指定被修饰的注解能用于修饰那些程序单元。
ElementType.ANNOTATION_TYPE:指定该策略的注解只能修饰注解
ElementType.CONSTRUCTOR: 修饰构造器
ElementType.FIELD: 修饰成员变量
ElementType.LOCAL_VARIABLE: 修饰局部变量
ElementType.METHOD: 修饰方法
ElementType.PACKAGE 修饰包定义
ElementType.TYPE: 修饰类,接口或枚举定义 @Documented 用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。它是一个标记注解,没有成员。
@Inherited 用于表示某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
自定义注解
1 使用@interfacce关键字
public @interface Crystal {
}
注解还可以带成员变量
public @interface Crystal {
// 定义带成员变量的注解
// 注解中的成员变量以方法的形式定义
String name();
int age();
}
一旦注解定义了成员变量以后,在使用注解的时候,要为成员变量赋值
@Crystal(name = "car", age = 10)
public class Car {
}
修饰方法
public class Car {
int wheel = 4;
static int door = 4;
@Crystal(name = "car", age = 10)
String name() {
return "Car";
}
可以为注解指定初始值
public @interface Crystal {
// 定义带成员变量的注解
// 注解中的成员变量以方法的形式定义
String name() default "crystal";
int age() default 18;
}
可以根据注解是否包含成员变量,将其分为两类
- 标记注解,没有成员变量,仅利用自身的存在与否提供信息
- 元数据注解:包含成员变量
提取注解信息
使用注解注释类,方法,成员变量等成员后,这些注解不会自己生效,必须由开发者提供相应的工具来
提取并处理注解信息。
仅仅使用注解来标记程序元素,对程序是没有任何影响的,为了让注解起作用,我们要为注解提供一个
注解处理工具。
java5在java.lang.reflect包下新增了AnnotatedElement接口,该接口表示程序中可以
接受注解的程序元素。该接口主要有一下几个实现类
- Class:类定义
- Constructor:构造器定义
- Field 类的成员变量定义
- Method 类的方法定义
- Package:包定义
反射包提供了取读运行时注解的能力,只有当定义注解时使用了
@Retention(RetentionPolicy.RUNTIME)
修饰,该注解才会在运行时可见
JVM才会在装在.class文件时取读保存在class文件中注解信息
程序通过反射获取了某个类的AnnotatedElement对象(Class、Method、Constructor)之后,就可以
通过调用对象的如下几个方法来访问注解信息
<A extends Annotation> A getAnnotation(Class<A> annotationClass)
返回该程序元素上存在的、指定类型的注解。如果该类型的注解不存在,则返回null
<A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)
返回直接修饰该程序元素指定类型的注解。如果该类型的注解不存在,则返回null
Annotation[] getAnnotations()
返回该程序元素上所有注解
Annotation[] getDeclaredAnnotations()
返回直接修饰该程序元素上所有注解
boolean isAnnotationPresend(Class<? extends Annotation> annotationClass)
判定改程序元素上是否存在指定类型的注解,如果存在则返回true,否则返回false
<A extends Annotation>A[] getAnnotationByType(Class<A> annotationClass)
与getAnnotations()方法基本类似
<A extends Annotation>A[] getDeclaredAnnotationByType(Class<A> annotationClass)
与getDeclaredAnnotations()方法基本类似
例子
public class MyTest {
public static void m1() {
}
// 使用@Testable注解表示该方法可测试
@Testable
public static void m2() {
}
public static void m3() {
}
@Testable
public static void m4() {
throw new IllegalArgumentException("参数异常");
}
public static void m5() {
}
@Testable
public static void m6() {
}
public static void m7() {
}
@Testable
public static void m8() {
throw new RuntimeException("业务异常");
}
}
public class ProcessorTest {
public static void process(String clazz) throws ClassNotFoundException {
int passed = 0;
int failed = 0;
// 遍历clazz类里面对应的所有方法
for (Method m : Class.forName(clazz).getMethods()) {
if (m.isAnnotationPresent(Testable.class)) {
try {
m.invoke(null);
passed++;
} catch (Exception e) {
System.out.println("方法" + m + "运行失败,异常:" + e.getMessage());
failed++;
}
}
}
System.out.println("共运行了" + (passed + failed) + "个方法,其中:\n" + "失败了:" + failed + "个,\n" + "成功了:" + passed + "个");
}
}
public class RunTests {
public static void main(String[] args) throws Exception {
ProcessorTest.process("com.huawei.kemuer.MyTest");
}
}
输出
方法public static void com.huawei.kemuer.MyTest.m4()运行失败,异常:null
方法public static void com.huawei.kemuer.MyTest.m8()运行失败,异常:null
共运行了4个方法,其中:
失败了:2个,
成功了:2个