Java基础学习笔记32——注解
目录
1、注解概述
注解定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注释。
注解用来说明程序,给计算机看的。
注解小结:
- 以后大多数时候,我们会使用注解,而不是自定义注解;
- 编译给谁用?-> 给解析程序用;
- 注解不是程序的一部分,可以理解为注解就是一个标签。
注解的作用:
- 编写文档:通过代码里标识的注解生成文档【如使用javadoc命令生成doc文档:javadoc MyClass.java】
- 代码分析:通过代码里标识的注解对代码进行分析【如使用反射】
- 编译检查:通过代码里标识的注解让编译期能够实现基本的编译检查【如Override】
package AnnotationPackage;
/*
注解javadoc演示
@auther authername
@version 1.0
@since 1.5
*/
public class Test1 {
public static void main(String[] args) {
}
/**
* 计算两数的和
* @param x 整数
* @param y 整数
* @return 两数的和
*/
public int add(int x, int y) {
return x+y;
}
}
2、JDK中预定义的一些注解
2.1、@Override
@Override:检测被该注解标注的方法是否是继承自父类(接口)的。
2.2、@Deprecated
@Deprecated:该注解标注的内容,表示已过时。
2.3、@SuppressWarnings
@SuppressWarnings:压制警告。
一般传递参数all :@SuppressWarnings("all")
package AnnotationPackage;
@SuppressWarnings("all") //压制所有警告
public class Test2 {
@Override
public String toString() {
return super.toString();
}
@Deprecated //标注已过时
public void show1() {
//有缺陷
}
public void show2() {
//替代show1方法
}
}
3、自定义注解
格式:
元注解
public @interface 注解名称{
属性列表;
}
注解本质:就是一个接口,该接口默认继承Annotation接口
public interface 注解名称 extends java.lang.annotation.Annotation {}
注解的属性:接口中可以定义的抽象成员方法。要求:
- 属性的返回值类型:① 基本数据类型;② String类型;③ 枚举;④ 注解;⑤ 以上类型的数组。
- 定义了属性,在使用时要对属性赋值:① 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值;② 如果只有一个属性需要赋值,并且属性的名称是calue,则value可以省略,直接定义值即可;③ 数组赋值时,值使用{}包裹,如果数组中只有一个值,则{}可以省略。
元注解:用于描述注解的注解。元注解包含如下几个:
- @Target:描述注解能够作用的位置。ElemType 的取值:① TYPE:可以作用于类上;② METHOD:可以作用于方法上;③ FIELD:可以作用于成员变量上。
- @Retention:描述注解被保留的阶段。如:@Retention(RetentionPolicy.RUNTIME) //表示被MyAnno注解会保留到class字节码文件中,并被JVM读取到
- @Documented:描述注解是否被抽取到api文档中;
- @Inherited:描述注解是否被子类继承。
package AnnotationPackage;
//自定义注解
public @interface MyAnno {
// int showInt();
// String showString(); //抽象方法是注解的属性
// String[] showStrings();
// Person per();
// MyAnno2 anno2();
int value();
// String name() default "张三"; //默认为张三
Person per();
MyAnno2 anno2();
String[] strs();
}
----------------------------------------------
package AnnotationPackage;
public @interface MyAnno2 {
}
----------------------------------------------
package AnnotationPackage;
import java.lang.annotation.*;
/*
@Target:描述注解能够作用的位置;
@Retention:描述注解被保留的阶段;
@Documented:描述注解是否被抽取到api文档中;
@Inherited:描述注解是否被子类继承。
*/
@Target(value={ElementType.TYPE}) //表示该MyAnno只能作用于类上
//@Target(value={ElementType.METHOD}) //表示该MyAnno只能作用于成员方法上
//@Target(value={ElementType.FIELD}) //表示该MyAnno只能作用于成员变量上
@Retention(RetentionPolicy.RUNTIME) //表示被MyAnno注解会保留到class字节码文件中,并被JVM读取到
@Documented
@Inherited
public @interface MyAnno3 {
}
----------------------------------------------
package AnnotationPackage;
public enum Person {
P1,P2;
}
----------------------------------------------
package AnnotationPackage;
//@MyAnno(22) //value=22
@MyAnno(value = 18, per = Person.P1, anno2 = @MyAnno2, strs = {"aa", "bbb"})
@MyAnno3
public class Worker {
// @MyAnno3
public String name = "abc";
// @MyAnno3
public void show(){
}
}
----------------------------------------------
package AnnotationPackage;
//Teacher会自动继承Worker的注解
public class Teacher extends Worker{
}
4、在程序中使用(解析)注解
在程序中使用(解析)注解:获取注解中定义的属性值。
使用步骤:
- 获取注解定义的位置的对象(Class,Method,Field);
- 获取指定的注解:getAnnotation(Class) //其实就是在内存中生成了一个该注解接口的子类实现对象;
public class ProImpl implements Pro { public String className() { return "Annotation.ReflectPackage.Student"; } public String methodName() { return "show"; } }
- 调用注解中的抽象方法获取配置的属性值。
代码演示:
package AnnotationPackage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 描述需要执行的类型和方法名
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
String className();
String methodName();
}
/*
public class ProImpl implements Pro {
public String className() {
return "Annotation.ReflectPackage.Student";
}
public String methodName() {
return "show";
}
}
*/
----------------------------------------------------------------------
package AnnotationPackage;
public class Student {
public void show() {
System.out.println("学生");
}
}
----------------------------------------------------------------------
package AnnotationPackage;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@Pro(className = "AnnotationPackage.Student", methodName = "show")
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
InvocationTargetException, InstantiationException, IllegalAccessException {
/*
前提:不能改变该类的任何代码,可以创建任意类的对象,可以执行任意方法
*/
//解析注解
//获取该类的字节码文件对象
Class<ReflectTest> reflectTestClass = ReflectTest.class;
//获取上边的注解对象
Pro an = reflectTestClass.getAnnotation(Pro.class); //其实就是在内存去生成了一个该注解接口的子类实现对象
//调用注解对象中定义的抽象方法(属性),获取返回值
String className = an.className();
String methodName = an.methodName();
//通过反射来使用
Class<?> c = Class.forName(className);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m = c.getMethod(methodName);
m.invoke(obj);
}
}
---------------------------------------------------------------------------
结果:
学生
5、注解案例
package AnnotationPackage.demo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
-----------------------------------------------------------
package AnnotationPackage.demo;
/*
小明定义的计算机类
*/
public class Calculator {
//加法
@Check
public void add() {
String str = null;
str.toString();
System.out.println("1 + 0 = " + (1 + 0));
}
//减法
@Check
public void sub() {
System.out.println("1 - 0 = " + (1 - 0));
}
//乘法
@Check
public void mul() {
System.out.println("1 * 0 = " + (1 * 0));
}
//除法
@Check
public void div() {
System.out.println("1 / 0 = " + (1 / 0));
}
public void show() {
System.out.println("永无bug");
}
}
-----------------------------------------------------------
package AnnotationPackage.demo;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 简单的测试框架
*
* 当主方法执行后,会自动执行被检测的所有方法(加了Check注解的方法),判断方法是否有异常,并记录到文件中
*/
public class TestCheck {
public static void main(String[] args) throws IOException {
//创建计算器对象
Calculator c = new Calculator();
//获取字节码文件对象
Class<? extends Calculator> cls = c.getClass();
//获取所有方法
Method[] methods = cls.getMethods();
int number = 0; //出现异常的次数
BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
for (Method method : methods) {
//判断方法上是否有check注解
if (method.isAnnotationPresent(Check.class)) {//判断当前方式是否加上了指定注解
//有就执行
try {
method.invoke(c);
} catch (Exception e) {
//捕获异常
number ++;
bw.write(method.getName()+"方法出异常了");
bw.newLine();
bw.write("异常的名称:"+e.getCause().getClass().getSimpleName());
bw.newLine();
bw.write("异常的原因:"+e.getCause().getMessage());
bw.newLine();
bw.write("---------------------\n");
}
}
}
bw.write("本次一共出现"+number+"次异常");
bw.flush();
bw.close();
}
}
-----------------------------------------------------------
bug.txt中内容:
add方法出异常了
异常的名称:NullPointerException
异常的原因:Cannot invoke "String.toString()" because "str" is null
---------------------
div方法出异常了
异常的名称:ArithmeticException
异常的原因:/ by zero
---------------------
本次一共出现2次异常