1. 反射:
java反射机制是在运行状态下, 对于任意一个类, 都能够知道这个类的所有属性和方法, 对于任意一个对象, 都能够调用他的任意一个方法和属性, 这种动态获取的信息, 以及动态获取的方法这种动态获取信息和动态调用方法的功能称为java语言的反射机制.
即通过在运行阶段, 对类的class文件进行解读, 获取属性和方法
//反射
//1.生成一个class对象
Class<?> class1 = Class.forName("com.atguigu.yygh.mq.thread.money.Money");
//2.1 生成一个filed对象
Field hello = class1.getField("hello");
//2.2 生成一个method对象
Method sayHello = class1.getMethod("sayHello", String.class);
//2.3 生成一个Constructor对象
Constructor<?> allConstructor = class1.getConstructor(int.class);
Constructor<?> noConstructor = class1.getConstructor();
//3. 生成对象实例的两种方式
//使用class对象通过无参构造生成Money对象实例
Money money1 = (Money) class1.newInstance();
//使用构造器实例,通过有参构造,完成类的实例化
Money allMoney = (Money) allConstructor.newInstance(1);
System.out.println("money1 = " + money1);
//4. 给实例对象属性赋值, 执行对象方法
hello.set(money1,999);
Object invoke = sayHello.invoke(money, "");
System.out.println("----------------------");
System.out.println("money1 = " + money1);
System.out.println("invoke = " + invoke);
//执行结果:
/* money1 = Money(hello=100000)
hello world
----------------------
money1 = Money(hello=999)
invoke = hello world*/
反射的使用:
①获取目标类的class对象
②通过class对象解析类的成员(属性和方法包括构造方法)
③通过操作属性对象和方法对象, 构造方法对象, 可以对类的实例对象进行修改属性值,执行方法,也可以生成实例对象
2. 注解
①与类, 接口, 抽象类, 枚举等平级, 定义方式为 @interface
②相当于一种标记, 在程序(类,方法,属性等)中加入注解就等于为程序加入某段标记
③javac编译器, 开发工具和其他程序可以通过反射反射来了解类和各种元素上有无标记, 有什么标记, 去干相应的事情, 例如@Data可被识别为添加get(),set(),toString()方法等
注解只在编译阶段生效, 类加载器不会加载注解
ex: lombok的相关注解@Data
idea需要lombok插件解析注解, 通过反射生成get, set等方法, 同样的javac或者其他编译工具, 在编译时, 会有启动类识别注解, 通过反射完成注解的功能, 注解只是标记, 具体的功能实现其实是底层代码完成的
2.1. 元注解
①给注解标注的注解叫元注解, 给注解或者元注解注解的只能是元注解, 同时
②注解的功能性是通过元注解实现的
③注解是由元注解和代码块组成的
元注解有五种:
①Retention: 注解存活时间
②Documented: 文档相关, 是能将注解的内容写入javadoc(javadoc用于生成开发文档)
③Target: 指定了注解修饰的程序元素(接口, 类, 字段等)
④Inherited: 表示继承的意思, 若一个超类被@Inherited注解过的注解注解了, 这个超类的子类没有被任何注解注解时, 将会继承这个超类的注解
⑤Repeatable: 可重复
@interface Persons {Person[] value();}
@Repeatable(Persons.class)
@interface Person{String role default "";}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{}
@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。
什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解。
我们再看看代码中的相关容器注解。
@interface Persons {
Person[] value();
}
按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组。
注解的属性
注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
屁话, 就是类型属性的规范和类不一致而已, 就说类型和属性
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id();
String msg();
}
上面代码定义了 TestAnnotation 这个注解中拥有 id 和 msg 两个属性。在使用的时候,我们应该给它们进行赋值。
赋值的方式是在注解的括号内以 value=”” 形式,多个属性之前用 ,隔开。
⑤FunctionalInterfance: 函数式接口注解, 用来标记函数式接口
2.2. 注解的运行
注解的内部原理是在解析类注解时,将类注解上配置的值存储到一个Map集合中,并且基于注解接口生成一个动态的代理对象,同时在构建该动态代理对象的AnnotationInvocationHandler对象时,将之前解析到的Map集合传入。接下来在调用注解对象获取属性值时,实际调用的其实是动态代理对象的获取属性值的方法,从而触发AnnotationInvocationHandler的invoke方法执行,在该方法内,从Map集合中将属性对应的值返回。
原文链接:https://blog.csdn.net/finally_vince/article/details/127423544
然后通过反射获取class对象然后运行的
ex:
①使用类的class类型, 调用isAnnotationPresent(注解的class)方法来判断本类是否使用了该注解
②使用getAnnotations()方法获取注解
③解析注解的属性
@TestAnnotation()
public class Test {
public static void main(String[] args) {
boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if ( hasAnnotation ) {
TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
System.out.println("id:"+testAnnotation.id());
System.out.println("msg:"+testAnnotation.msg());
}
}
}
注解本身就是一个标签, 用来标记不同的类, 本身不具备代码运行的作用, 但是程序(编译器,处理器,开发工具及其他程序)可以通过识别这个标签, 然后对注解标注的程序执行功能性操作(例如lombok的@Data注解, 会在编译时生成get(),set()等方法)
注释的作用:
注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。
注解有许多用处,主要如下:
提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
值得注意的是,注解不是代码本身的一部分。