概述
注解:说明程序的,给计算机看的
注释:描述程序的,给程序员看的
注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件
注解怎么使用?
1、注解使用时的语法格式:@ 注解类型名
2、注解可以出现在类上、属性上、方法上、变量上等。。。注解还可以出现在注解类型上
注重学@Override@Deprecated@Target@Retention这四个注解
JDK内置注解
java提供了5个基本的注解,分别是
1.@Override:确保子类确实重写了父类的方法
@Override这个注解只能注解方法
2.@Deprecated:用于表示某个程序元素类,方法等已过时,当其他程序使用已过时的类,方法时编译器会给出警告
3.@SuppressWarnings:被该注解修饰的元素以及该元素的所有子元素取消显示编译器警告
自定义注解
格式&本质
元注解
public @interface 名称 {
属性列表;
}
本质:就是一个接口,该接口默认继承Annotation接口
public interface myAnno extends java.lang.annotation.Annotation {
}
属性定义
我们通常在注解中定义属性,以下这个是MyAnno的name属性
看着像一个方法,但实际上我们称之为属性name
如果一个注解中有属性,那么必须给属性赋值
public @interface MyAnno {
public String show();
int age() default 18;
}
public class MyAnnotationTest {
//注解里age可以不写了,因为有默认值
@MyAnno(name="aaa")
public void doSome() {
}
}
要求
1、属性的返回值类型有下列取值:基本数据类型,String,枚举,注解,以上类型的数组(不能是Integer,Double )
2、定义了属性,在使用时需要给属性赋值
1、如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值
2、如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可
3、数组赋值时,值使用{}包裹,如果数组中只有一个值,则{}可以省略
元注解
用于描述注解的注解
@Target:描述注解能够作用的位置
@Target(ElementType.TYPE):可以作用在类上
@Target(ElementType.METHOD):可以作用在方法上
@Target(ElementType.FIELD):可以作用在成员变量上
@Retention:描述注解被保留的阶段
@Retention(RetentionPolicy.CLASS):当前被描述的注解会保留到class字节码文件中,但不被JVM读到
@Retention(RetentionPolicy.RUNTIME):当前被描述的注解会保留到class字节码文件中,并被JVM读到(被反射机制读取)
@Retention(RetentionPolicy.SOURCE):只会保存在java源文件中,不会出现在class文件里,因为只给编译器看,所以编译后就没有用了
@Documented:描述注解是否被抽取到api文档中
@Inherited:描述注解是否被子类继承
解析(使用)注解
获取注解中定义的属性值
1、获取注解定义的位置的对象(Class,Method,Field)
2、获取指定的注解
getAnnotation(Class)
//其实就是在内存中生成了一个该注解接口的子类实现对象
/*
public class ProImpl implements Pro {
public String className() {
return "annotation.Demo1";
}
public String methodName() {
return "show";
}
}
*/
3、调用注解中的抽象方法获取配置的属性值
package annotation;
public class Demo1 {
public void show() {
System.out.println("Demo1...show...");
}
}
package annotation;
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();
}
package annotation;
import java.lang.reflect.Method;
@Pro(className = "annotation.Demo1",methodName = "show")
public class ReflectTest {
public static void main(String[] args) throws Exception {
//不改变该类的任何代码,可以任意类的对象,可以执行任意方法
//1、解析注解
//1。1、获取该类的字节码对象
Class<ReflectTest> reflectTestClass = ReflectTest.class;
//2、获取上边的注释对象
//其实就是在内存中生成了一个该注解接口的子类实现对象
/*
public class ProImpl implements Pro {
public String className() {
return "annotation.Demo1";
}
public String methodName() {
return "show";
}
}
*/
Pro an = reflectTestClass.getAnnotation(Pro.class);
//3、调用注解对象中定义的重新方法,获取返回值
String className = an.className();
String methodName = an.methodName();
//4、加载对象进内存
Class cls = Class.forName(className);
Object obj = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(obj);
}
}
反射注解
package annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "abc";
}
package annotation;
@MyAnnotation()
public class MyAnnotationTest {
}
package annotation;
public class ReflectAnnotationTest {
public static void main(String[] args) throws ClassNotFoundException {
Class c = Class.forName("annotation.MyAnnotationTest");
System.out.println(c.isAnnotationPresent(MyAnnotation.class));
Class stringClass = Class.forName("java.lang.String");
System.out.println(stringClass.isAnnotationPresent(MyAnnotation.class));
if(c.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation myAnnotation = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
System.out.println("类上面的注解对象");
//获取对象上的属性
String value = myAnnotation.value();
System.out.println(value);
}
}
}
案例
package annotation.demo;
/**
* 小明定义的计算器类
*/
public class Calculator {
@Check
public void add() {
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 annotation.demo;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}
package annotation.demo;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* 简单的测试框架
* 当主方法执行后,会自动检测加了 @Check 的方法,判断方法是否有异常,记录到文件中
*/
public class TestCheck {
public static void main(String[] args) throws IOException {
//1.创建计算器对象
Calculator c = new Calculator();
//2.获取字节码文件对象
Class cls = c.getClass();
//3.获取所有方法
Method[] methods = cls.getMethods();
int num = 0;//异常出现的次数
BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
for (Method method : methods) {
//4.判断方法上是否有Check注释
if(method.isAnnotationPresent(Check.class)) {
//5.执行
try {
method.invoke(c);
} catch (Exception e) {
//6.捕获异常
num++;
bw.write(method.getName()+"方法出异常了");
bw.newLine();
bw.write("异常的名称 :"+e.getCause().getClass().getSimpleName());
bw.newLine();
bw.write("异常的原因"+e.getCause().getMessage());
bw.newLine();
bw.write("------------------------");
bw.newLine();
}
}
}
bw.write("本次测试一共出现"+num+"次异常");
bw.flush();
bw.close();
}
}
注解在开发中有什么用?
需求:假设有这样一个注解,叫做;@id
这个注解只能出现在类上面,当这个类上有这个注解的时候,要求这个类必须 有一个int类型 id属性。如果没有这个属性就报异常。如果有这个属性就正常执行
package annotation.annotation1;
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 Id {
}
package annotation.annotation1;
@Id
public class User {
int id;
String name;
String password;
}
package annotation.annotation1;
public class HasNotIdPropertyException extends RuntimeException{
public HasNotIdPropertyException() {
}
public HasNotIdPropertyException(String s) {
super(s);
}
}
package annotation.annotation1;
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception{
Class c = Class.forName("annotation.annotation1.User");
boolean isRigth = false;
if(c.isAnnotationPresent(Id.class)) {
Field[] fields = c.getDeclaredFields();
for(Field field:fields) {
if("id".equals(field.getName())&&"int".equals(field.getType().getSimpleName())){
isRigth = true;
break;
}
}
if(!isRigth) {
throw new HasNotIdPropertyException("被@id注解标注的类中必须要有int类型的id属性");
}
}
}
}
小结
1、以后大多数时候,我们会使用注解,而不是自定义注解
2、注解给谁用?编译器和解析程序用
3、注解不是程序的一部分,可以理解为注解就是一个标签