反射&注解
# 反射&注解
# 1. 反射 reflect重点
# 1. 反射概述
- 框架的灵魂
- 将类的各个组成部分封装为其他对象,这就是反射机制
# 2. 获取Class类对象的方式
# 1. 三种方式 3个阶段: 源码,Class,runtime阶段
Class.forName(“全限定类型”); [未加载进内存]
将字节码文件加载进内存,返回Class对象
- 应用: 多用于配置文件,将类名定义在配置文件中,读取文件,加载类
类名.class [加载进内存]
通过类名的属性class获取
- 应用: 多用于参数的传递
对象.getClass()
getClass()方法在Object类中定义着
- 对用于对象的获取字节码的方式
# 2. 结论
- 同一个字节码文件(*.class)在一次运行过程中,只会被加载一次,不论哪种方式加载进内存,都是同一个.
public static void main(String[] args) throws Exception {
// 1. Class.forName
Class<?> cls1 = Class.forName("com.chggx.domain.Person");
System.out.println(cls1);
// class com.chggx.domain.Person
// 2. 类名.class
Class<Person> cls2 = Person.class;
System.out.println(cls2);
// 3. 对象.getClass()
Person person = new Person();
Class<? extends Person> cls3 = person.getClass();
System.out.println(cls3);
// 4. 比较三个对象
// ==: 基本类型比较值,引用类型比较地址值
System.out.println(cls1 == cls2); // true
System.out.println(cls1 == cls3); // true
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 3. Class对象
# 1. 获取功能
# 1. 获取成员变量
- field:
- ① 设置值 set()
- ②获取值 get()
- 忽略安全修饰符,暴力反射
ddd.setAccessible(true);
Field ddd = personClass.getDeclaredField("ddd");
// get() [java.lang.IllegalAccessException]
// 不为public修饰时,忽略访问权限修饰符的安全检查
// 暴力反射
ddd.setAccessible(true);
Object value1 = ddd.get(person);
System.out.println(value1);
1
2
3
4
5
6
7
2
3
4
5
6
7
修饰符和类型 | 方法 | 描述 |
---|---|---|
Field | getField(String name) | 获取所有public修饰的成员变量 |
Field[] | getFields() | 获取指定名称的 public修饰的成员变量 |
Field | getDeclaredField(String name) | 获取所有的成员变量,不考虑修饰符 |
Field[] | getDeclaredFields() | 获取指定的成员变量,不考虑修饰符 |
- 代码
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
// 0. 获取Person的Class对象
Class<Person> personClass = Person.class;
/*
1. 获取成员变量们
* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的 public修饰的成员变量
* Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name)
*/
// 1.1 Field[] getFields() :获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
Arrays.asList(fields).forEach(System.out::println);
System.out.println("----------");
// 1.2 Field getField(String name) 获取指定名称的 public修饰的成员变量
Field aaa = personClass.getField("aaa");
// 操作获取的成员变量 aaa
// get(): 获取
Person person = new Person();
Object value = aaa.get(person);
System.out.println(value);
// set(): 设置
aaa.set(person,"张三");
System.out.println(person);
System.out.println("=============");
// 1.3 Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field[] declaredFields = personClass.getDeclaredFields();
Arrays.asList(declaredFields).forEach(System.out::println);
// 1.4 Field getDeclaredField(String name)
Field ddd = personClass.getDeclaredField("ddd");
// get() [java.lang.IllegalAccessException]
// 不为public修饰时,忽略访问权限修饰符的安全检查
// 暴力反射
ddd.setAccessible(true);
Object value1 = ddd.get(person);
System.out.println(value1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 2. 获取构造方法
修饰符和类型 | 方法 | 描述 |
---|---|---|
Constructor | getConstructor(Class... parameterTypes) | |
Constructor[] | getConstructors() | |
Constructor<T> | getDeclaredConstructor(Class<?>... parameterTypes) | |
Constructor[] | getDeclaredConstructors() |
Constructor创建对象
T newInstance(Object... initargs)
代码
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
// 0. 获取Person的Class对象
Class<Person> personClass = Person.class;
/*
2. 获取构造方法们
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(Class<?>... parameterTypes)
* Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
* Constructor<?>[] getDeclaredConstructors()
*/
// 1. Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
System.out.println(constructor);
// 创建对象 T newInstance(Object... initargs)
Person person = constructor.newInstance("张三", 23);
System.out.println(person);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 3. 获取成员方法
修饰符和类型 | 方法 |
---|---|
Method | getDeclaredMethod(String name, Class... parameterTypes) |
Method[] | getDeclaredMethods() |
Method | getEnclosingMethod() |
Method | getMethod(String name, Class... parameterTypes) |
Method[] | getMethods() |
- 执行方法对象
- Object invoke(Object obj, Object... args)
- 获取方法名---
- getName();
# 4. 获取类型
饰符和类型 | 方法 |
---|---|
String | getName() |
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 0. 获取Person的Class对象
Class<Person> personClass = Person.class;
/*
3. 获取成员方法们:
* Method[] getMethods()
* Method getMethod(String name, 类<?>... parameterTypes)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)
*/
// 1.1
Method method = personClass.getMethod("eat");
Person person = new Person();
// 执行方法: Object invoke(Object obj, Object... args)
method.invoke(person);
// 1.2
Method method2 = personClass.getMethod("eat", String.class);
// 执行方法: Object invoke(Object obj, Object... args)
method2.invoke(person,"吃饭");
System.out.println("-----------------------------");
// 2.1
Method[] methods = personClass.getMethods();
// 所有的方法
Arrays.asList(methods).forEach(method1 -> {
System.out.println(method);
// 获取方法名 getName();
System.out.println(method.getName());
});
// 支持暴力反射
System.out.println("-----------------------------");
// 3 获取全限定类名 String getName()
String className = personClass.getName();
System.out.println(className);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 4. 案例
- 需求: 写一个""框架",可以帮我们创建任意类的对象,并执行其中的任意方法
# 1. 代码 [配置文件形式]
- Person
public class Person {
public void eat(){
System.out.println("eat...");
}
public void eat(String food){
System.out.println(food);
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- Student
public class Student {
public void sleep(){
System.out.println("sleep...");
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
- pro.properties
#className=com.chggx.domain.Person
#methodName=eat
className=com.chggx.domain.Student
methodName=sleep
1
2
3
4
2
3
4
- ReflectTest
public class ReflectTest {
public static void main(String[] args) throws Exception {
// 可以创建任意对象,可以执行任意方法
// Person person = new Person();
// person.eat();
// 反射
// 1. 加载配置文件
// 1.1 创建Properties
Properties properties = new Properties();
// 1.2 加载配置文件,转换为一个集合
// 1.2.1 获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
// 1.3 加载
properties.load(is);
// 2. 获取配置文件中定义的数据
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
// 3. 加载该类进内存
Class<?> cls = Class.forName(className);
// 4. 创建对象
Object obj = cls.newInstance();
// 5. 获取方法对象
Method method = cls.getMethod(methodName);
// 6. 执行方法
method.invoke(obj);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 2. 注解 annotation
# 1. 概述
* 概念: 说明程序的。给计算机看的
* 注释: 用文字描述程序的。给程序员看的
* 定义: 注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
* 概念描述:
* JDK1.5之后的新特性
* 说明程序的
* 使用注解:@注解名称
* 作用分类:
①编写文档: 通过代码里标识的注解生成文档【生成文档doc文档】
②代码分析: 通过代码里标识的注解对代码进行分析【使用反射】
③编译检查: 通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 2. JDK中预定义的一些注解
@Override: 检测被该注解标注的方法是否是继承自父类(接口)的
@Deprecated: 该注解标注的内容,表示已过时
@SuppressWarnings: 压制警告
* 一般传递参数all @SuppressWarnings("all")
1
2
3
4
2
3
4
- 代码
@SuppressWarnings("all")
public class AnnotationDemo01 {
@Override
public String toString() {
return super.toString();
}
/**
* @Deprecated: 标注已过时
*/
@Deprecated
public void show1(){
// 有缺陷
}
/**
* 压制警告 "all"
*/
@SuppressWarnings("all")
public void show2(){
// 取代show1()
}
public void demo(){
//
show1();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 3. 自定义注解
# 1. 格式
// 元注解
public @interface 注解名称{
属性列表;
}
1
2
3
4
2
3
4
# 2. 本质
本质:注解本质上就是一个接口,该接口默认继承Annotation接口
public interface MyAnnotation extends java.lang.annotation.Annotation {}
1
2
2
# 3. 属性:接口中的抽象方法
要求:
1. 属性的返回值类型有下列取值
* 基本数据类型
* String
* 枚举
* 注解
* 以上类型的数组
2. 定义了属性,在使用时需要给属性赋值
1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 1. 案例一
- 代码
public @interface MyAnnotation {
// 基本类型
int anno1();
// String类型
String anno2();
// 枚举
Person per();
// 注解
MyAnnotation2 annotation();
// 数组
String[] str();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 赋值
@MyAnnotation(anno1 = 1, anno2 = "2", per = Person.P1, annotation = @MyAnnotation2,str = {"aaa","bbb"})
public void show2() {
// 取代show1()
}
1
2
3
4
2
3
4
# 2. 案例一
public @interface MyAnnotation3 {
// 基本类型
int value();
// String类型 默认default数据
String anno2() default "2";
// 枚举
Person per();
// 注解
MyAnnotation2 annotation();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 4. 元注解:用于描述注解的注解
1. @Target: 描述注解能够作用的位置
* ElementType取值:
* TYPE: 可以作用于类上
* METHOD: 可以作用于方法上
* FIELD: 可以作用于成员变量上
2. @Retention:描述注解被保留的阶段
* RetentionPolicy取值:
* SOURCE: 源码
* CLASS: class
* RUNTIME: 运行时
* @Retention(RetentionPolicy.RUNTIME): 当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
3. @Documented: 描述注解是否被抽取到api文档中
4. @Inherited: 描述注解是否被子类继承
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 1. @Target
/**
* @Author: CHGGX
* @Date: 2020/04/28 18:06
* @Description: <h1> 元注解: 用于描述注解的注解 </h1>
* 1. @Target:描述注解能够作用的位置
* * ElementType取值:
* * TYPE:可以作用于类上
* * METHOD:可以作用于方法上
* * FIELD:可以作用于成员变量上
* 2. @Retention:描述注解被保留的阶段
* * @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
* 3. @Documented:描述注解是否被抽取到api文档中
* 4. @Inherited:描述注解是否被子类继承
*/
@Target(value = {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
public @interface MyAnnotation4 {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@MyAnnotation4
public class Worker {
@MyAnnotation4
private String name;
@MyAnnotation4
public void eat(){
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 类,属性,方法都可使用
# 2. @Retention
/**
* @Author: CHGGX
* @Date: 2020/04/28 18:06
* @Description: <h1> 元注解: 用于描述注解的注解 </h1>
* 1. @Target:描述注解能够作用的位置
* * ElementType取值:
* * TYPE:可以作用于类上
* * METHOD:可以作用于方法上
* * FIELD:可以作用于成员变量上
* 2. @Retention:描述注解被保留的阶段
* * RetentionPolicy取值:
* * SOURCE: 源码
* * CLASS: class
* * RUNTIME: 运行时
* * @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
* 3. @Documented:描述注解是否被抽取到api文档中
* 4. @Inherited:描述注解是否被子类继承
*/
@Target(value = {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotation4 {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 4. 在程序中是用(解析)注解
- 获取注解中定义的属性值
1. 获取注解定义的位置的对象 (Class,Method,Field)
2. 获取指定的注解
* getAnnotation(Class)
//其实就是在内存中生成了一个该注解接口的子类实现对象
public class ProImpl implements Pro{
public String className(){
return "cn.itcast.annotation.Demo1";
}
public String methodName(){
return "show";
}
}
3. 调用注解中的抽象方法获取配置的属性值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
代码
- 注解类: 取代之前配置的pro.properties配置文件
/** * @Author: CHGGX * @Date: 2020/04/28 21:11 * @Description: <h1> 描述需要执行的类名,和方法名 </h1> * 作用于类(TYPE),在运行期执行 */ @Target(value = ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Pro { /** * 类名 */ String className(); /** * 方法名 */ String methodName(); /* public class ProImpl implements Pro{ public String className(){ return "com.chggx.annotation.Pr1"; }; public String methodName(){ return "show"; }; } */ }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32- 应用方法的类
public class Pr1 { public void show1(){ System.out.println("Pr1...show1...."); } }
1
2
3
4
5
6
7
8public class Pr2 { public void show2(){ System.out.println("Pr2...show2...."); } }
1
2
3
4
5
6
7- 反射测试类 注解实现
@Pro(className = "com.chggx.annotation.Pr1", methodName = "show1") public class ReflectTest { public static void main(String[] args) throws Exception { // 1. 解析注解 // 1.1 获取该类的字节码文件对象 Class<ReflectTest> rtc = ReflectTest.class; // 2 获取上边注解对象 // 就是在内存中去生成了一个该注解接口的子类实现对象 Pro annotation = rtc.getAnnotation(Pro.class); /* public class ProImpl implements Pro{ public String className(){ return "com.chggx.annotation.Pr1"; }; public String methodName(){ return "show"; }; } */ // 3.调用注解对象中定义的抽象方法,获取返回值 // 类名 String className = annotation.className(); // 方法名 String methodName = annotation.methodName(); System.out.println(className); System.out.println(methodName); // 4. 加载该类进内存 Class<?> cls = Class.forName(className); // 5. 创建对象 Object obj = cls.newInstance(); // 6. 获取方法对象 Method method = cls.getMethod(methodName); // 7. 执行方法 method.invoke(obj); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 5. 案例
上次更新: 2023/03/27, 08:56:25