注释: 给人看的,便于阅读代码, 对代码的描述
注解(Annotation): 对代码的描述, 作为代码形式保留下来,
Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
注释: 类似超市商品下面的标签, 描述商品 方便给顾客查看的
注解: 类似商品的条形码, 描述商品, 方便后期商品结算
注解的本质: 特殊的接口
声明注解: 创建了一个特殊接口
使用注解: @注解名(创建注解的一个对象)
1.注解的作用
1、生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等 2、跟踪代码依赖性,实现替代配置文件功能。比如Spring的注入,未来java开发,将大量注解配置,具有很大用处; 后期学习框架大量使用, 基于注解的开发 3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出
1.内置注解: jdk定义好这个注解的声明, 开发者直接使用, 语法检查
@Override 检测重写
@Deprecated 已过时, 只是一个标志, 还是能够使用
@SuppressWarnings("all") 抑制编译器生成警告信息
@SuppressWarnings("all")
public class Demo1 {
@SuppressWarnings("all")
public void fun1(){
System.out.println("dddd");
}
public static void main(String[] args) {
Demo2 demo2 = new Demo2();
demo2.fun1(10);
Date d = new Date();
//2022 - 1970 = 52 1900 ~ 1999
System.out.println(d.getYear()); //2022 122
}
}
@SuppressWarnings("all")
class Demo2 extends Demo1{
//重写Demo1的
//@Override
/**
*
* @param a 声明了方法参数
* @return 声明方法返回值
*/
@Deprecated
public int fun1(int a){
return 1;
}
}
2.元注解: jdk定义好这个注解的声明, 在注解上使用,
1.@Documented-注解是否将包含在JavaDoc中
一个简单的Annotations标记注解,表示是否将注解信息添加在javadoc文档中
2.@Retention –什么时候使用该注解
Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间
它的取值如下:
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
自定义注解: 保留期一定设置为runtime
3.@Target–注解用于什么地方
默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括 ● ElementType.CONSTRUCTOR:用于描述构造器 ● ElementType.FIELD:成员变量、对象、属性(包括enum实例) ● ElementType.LOCAL_VARIABLE:用于描述局部变量 ● ElementType.METHOD:用于描述方法 ● ElementType.PACKAGE:用于描述包 ● ElementType.PARAMETER:用于描述参数 ● ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明
4.@Inherited – 定义该注释和子类的关系
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解
/**
* 定义一个注解接口
* 使用元注解对自定义的注解进行一些声明,说明
*/
@Documented //表示该MyAnnotation1注解将会在生成doc文档上出现
//跟注解的参数赋值: 如果数组类型
@Target(value={ElementType.TYPE,ElementType.METHOD,ElementType.FIELD}) //目标: 表示这个MyAnnotation1注解在那些地方使用
// 如果没有写, 表示这个注解在任意的地方使用
@Retention(value= RetentionPolicy.RUNTIME) //自定义注解,保留期一定是Runtime
@Inherited // 表示这个MyAnnotation1这个注解标记的类, 这个类的子类是否继承父类该注解
public @interface MyAnnotation1 {
}
@MyAnnotation1 //创建了MyAnnotation1的一个对象
public class Student {
@MyAnnotation1
private int stuno;
private String name;
//@MyAnnotation1
public Student( int stuno, String name) {
this.stuno = stuno;
this.name = name;
}
public Student() {
}
@MyAnnotation1
public int getStuno() {
return stuno;
}
public void setStuno(int stuno) {
this.stuno = stuno;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class SubStudent extends Student{
}
自定义注解: 关键字@interface , 默认继承Annotation接口, 本质就是一个接口
参数成员访问修饰符: public 或者是缺省的(还是public), 参数名后面必须是()
参数本质就是一个抽象方法
但是我们可以使用注解的时候,给参数赋值, 在声明的时候, 设置默认值
参数名() default 值;
如果一个参数没有设置default默认值, 使用这个注解的时候, 一定要给参数赋值,
如果使用default, 使用注解的时候, 可以给参数赋值,也可以不赋值(使用默认值)
参数的数据类型: 八大基本数据类型,String, 枚举,Class,注解类型,或者12种的数组类型
自定义的注解,可以有参数,也可以没有参数, 如果没有参数,这个注解没有意义
自定义注解,它的功能, 必须写代码解析注解,并给它赋予功能,自定义注解保留期: 一定为RUNTIME, 获取该注解的对象, 只能使用反射来获取
自定义注解:
声明注解的语法: @interface
public @interface 注解名{ //成员 }
使用注解:
在方法,类型,包,构造方法,属性,参数... 使用注解
@注解名
使用注解注意事项:
如果注解定义的参数是数组类型, 给数组类型赋值
a. 赋一个值: 参数名=值 或者: 参数名={值}
b. 赋多个值: 参数名={值1,值2,值3....}
/**
* 自定义注解
*/
@Documented
@Target(value={ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Annotation1 {
//成员参数
// value参数有一个默认值, 这个方法默认返回值
//给这个参数赋值, 就是设置这个方法的返回值
String value();
SexEnum sex() default SexEnum.MAN;
}
public enum SexEnum {
MAN,WOMAN
}
@Annotation1(value="lisi",sex=SexEnum.WOMAN)
public class Demo3 {
}
商品类: 上架方法, 下架方法, 购买方法, 浏览方法,
/**
* 商品类
*/
public class Product {
@Role("管理员")
public void shangJia(){
System.out.println("商品正在上架...");
}
@Role("管理员")
public void xiaJia(){
System.out.println("商品正在下架...");
}
@Role("顾客")
public void buy(){
System.out.println("您正在购买商品...");
}
public void look(){
System.out.println("您正在查看商品...");
}
}
/**
* 角色的注解
*/
//如果这个参数名为value, 单独给这个value参数赋值, 省略 value=
// 如果给多个参数赋值, value= 一定不能省略
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Role {
String value();
}
要求: 上架方法, 下架方法,必须要求用户的角色是"管理员"
购买方法: 必须要求用户的角色是"顾客"
浏览方法: 不需要角色
Scanner input = new Scanner(System.in);
System.out.println("请输入您的角色:");
String role = input.next();
System.out.println("请输入您的调用的方法:");
String methodName = input.next();
//调用Product的方法
Product product = new Product();
//调用方法之前,判断你的角色与方法上定义的角色是否匹配
//如果不匹配, 禁止访问, 如果匹配,允许访问
//通过反射
//1.获取Product类的Class对象
Class clazz = Product.class;
//2.获取指定方法名的Method对象
Method method = clazz.getDeclaredMethod(methodName);
//判断您的角色是否满足
//3.获取这个方法上的需要角色: 使用注解
//判断是否有@Role注解 isAnnotationPresent(注解的Class类型) true表示有, false:没有
if(method.isAnnotationPresent(Role.class)){ //true
//判断角色是否匹配
//获取这个方法上@Role的value参数
//获取方法上的注解对象 getAnnotation(类 annotationClass) 获取指定类型的注解对象
//Annotation[] getAnnotations() 获取所有的注解对象
Role obj = method.getAnnotation(Role.class);
//获取它的参数值
String value = obj.value();
if(role.equals(value)){ //角色是否匹配
System.out.println("您有权限访问...");
method.invoke(product);
}else{
System.out.println("您没有权限访问...");
}
}else{//没有,直接执行
method.invoke(product);
}
//3.调用方法
//method.invoke(product);
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池
这个运行时生成的动态代理对象是可以导出到文件的,方法有两种
在代码中加入System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
在运行时加入jvm 参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
public static void main(String[] args) throws NoSuchMethodException {
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Class clazz = Test.class;
Method method = clazz.getMethod("fun");
Role annotation = method.getAnnotation(Role.class);
System.out.println(annotation);
}
@Role(value="xxx")
public static void fun(){
System.out.println("....");
}