反射

JAVA反射:在程序运行过程中,可以对任意类型进行任意的操作 例如:加载任意类型,调用类型的任意方法,获取任意的成员变量,构造方法,可以创建该类型的对象。

获取类的字节码对象的三种方式:

  1. 对象名.getClass():返回的是某个引用指向的具体对象所属的运行是类,的字节码对象。获取到的是那个真正用来创建对象的子类的字节码对象。

2、类名.class:如果已经有了类名,可以通过.class的方式获取这个类的字节码对象。

3、通过Class.forName(String className):Class类中的一个静态方法,可以根据一个类的全类名,动态的加载某个类型。传入一个类的全类名,将类名描述的字节码文件,加载到内存中,形成一个字节码对象,并且把这个对象作为该方法的返回值。(在调用方法之前,是内存中没有这个字节码对象的)。字符串的来源非常广泛,来源于代码,可以来源于键盘录入、网络传输、文件读取、数据库

代码示例

 1 package com.Fanshe.demos;
 2 
 3  
 4 
 5 public class Demo01_获取字节码对象的三种方式 {
 6 
 7 public static void main(String[] args) throws ClassNotFoundException {
 8 
 9 Person p = new Person("zhangsan", 23);
10 
11 //getClass的方法获取Person类型的字节码对象
12 
13 Class c1 = p.getClass();
14 
15 System.out.println(c1);
16 
17  
18 
19 //类名.class的方式获取Person的字节码对象
20 
21 Class c2 = Person.class;
22 
23 System.out.println(c2);
24 
25  
26 
27 //比较两个引用是否指向了同一个对象
28 
29 System.out.println(c1 == c2);
30 
31  
32 
33 //Class的静态forName方法获取字节码对象
34 
35 Class c3 = Class.forName("com.ujiuye.demos.Person");
36 
37 System.out.println(c3);
38 
39 System.out.println(c2 == c3);
40 
41 }
42 
43 }

Class类型的理解
1、Class类型的实例表示正在运行的java应用程序的类或者接口

2、举例:

(张三, 23),(李四, 24),姓名和年龄都不相同,但是都有年龄和姓名的描述,因此将姓名和年龄抽取到一个类型中,就形成了Person类型,的概念

(yellow,4),(white,2),颜色和腿的个数各不相同,但是都有颜色和腿个数的描述,因此将颜色和腿个数的描述抽取到一个类型中,就形成了Animal类型,的概念

(姓名, 年龄),(颜色, 腿个数),成员变量和成员方法各不相同,但是都有成员变量和成员方法的描述,因此将成员变量和成员方法抽取到一个类型中,就形成Class类型,的概念

3、反射的举例:

房屋设计图纸---->实体房屋,反射:有了房屋,获取设计图纸

汽车设计图纸---->实体汽车,反射:有了汽车,获取设计图纸

无论是房屋设计图纸,还是汽车的设计图纸,都是图纸:抽取一个图纸类型

Class类中的方法
概述
1、一旦获取了类的字节码对象,就可以使用这个对象的所有方法,这些方法都定义在Class类型中。

2、成员方法:获取这个类的各种信息

成员变量、成员方法、构造方法、内部类、类的注解、类所在的包、类的修饰符、判断类的类型

以上内容获取出来以后,又是一些对象,有专门的数据类型,描述这些对象

3、其中,有一个非常常用的方法:newInstance()

创建这个字节码描述的类型的实例对象,例如使用Person.class字节码对象,调用newInstance方法,就可以创建一个真正的Person对象

代码示例

 1 package com.Fanshe.demos;
 2 
 3  
 4 
 5 public class Demo02_Class类型中的方法 {
 6 
 7  
 8 
 9 public static void main(String[] args) throws Exception  {
10 
11 //获取Person的字节码对象
12 
13 Class clazz = Class.forName("com.ujiuye.demos.Person");
14 
15 //创建该类型的实例对象
16 
17 Object obj = clazz.newInstance();
18 
19 Person p = (Person)obj;
20 
21 System.out.println(obj);
22 
23 System.out.println(p.getName() + "..." + p.getAge());
24 
25 }
26 
27 }
 

榨汁机案例
代码示例

1 package com.Fanshe.demos;
  2 
  3  
  4 
  5 import java.io.BufferedReader;
  6 
  7 import java.io.FileReader;
  8 
  9 import java.io.IOException;
 10 
 11  
 12 
 13 public class Demo03_榨汁机案例 {
 14 
 15  
 16 
 17 public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
 18 
 19 // JuiceMachine jm = new JuiceMachine();
 20 
 21 // Apple a = new Apple();
 22 
 23 // jm.makeJuice(a);
 24 
 25 //
 26 
 27 // Orange o = new Orange();
 28 
 29 // jm.makeJuice(o);
 30 
 31 //
 32 
 33 // WaterMelon w = new WaterMelon();
 34 
 35 // jm.makeJuice(w);
 36 
 37 JuiceMachine jm = new JuiceMachine();
 38 
 39 //准备一个流对象,读取配置文件中的配置信息
 40 
 41 BufferedReader br = new BufferedReader(new FileReader("fruit.txt"));
 42 
 43 //读取文件中的类名
 44 
 45 String className = br.readLine();
 46 
 47 //使用反射的方式,获取类名对应的字节码对象
 48 
 49 Class c = Class.forName(className);
 50 
 51 //创建c字节码对象描述的,真正的水果对象
 52 
 53 Object obj = c.newInstance();
 54 
 55 //使用榨汁机,榨水果,先把对象转成水果类型
 56 
 57 Fruit f = (Fruit)obj;
 58 
 59 jm.makeJuice(f);
 60 
 61 }
 62 
 63 }
 64 
 65  
 66 
 67 interface Fruit {
 68 
 69 public abstract void flow();
 70 
 71 }
 72 
 73  
 74 
 75 class JuiceMachine {
 76 
 77 public void makeJuice(Fruit f) {
 78 
 79 f.flow();
 80 
 81 }
 82 
 83 }
 84 
 85  
 86 
 87 class Apple implements Fruit {
 88 
 89 public void flow() {
 90 
 91 System.out.println("流出苹果汁");
 92 
 93 }
 94 
 95 }
 96 
 97  
 98 
 99 class Orange implements Fruit {
100 
101 public void flow() {
102 
103 System.out.println("流出橘子汁");
104 
105 }
106 
107 }
108 
109  
110 
111 class WaterMelon implements Fruit {
112 
113  
114 
115 public void flow() {
116 
117 System.out.println("流出西瓜汁");
118 
119 }
120 
121 }
122 
123  
124 
125 class Banana implements Fruit {
126 
127  
128 
129 @Override
130 
131 public void flow() {
132 
133 System.out.println("流出香蕉酱");
134 
135 }
136 
137 }
 

获取类中的构造方法并使用
1、通过Class类中的:

getConstructor(Class…paramTypes)

参数列表:Class…paramTypes,表示一个可变参数,需要传入构造方法的类型的字节码

返回值类型:Constructor,表示一个构造方法类型的对象

2、Constructor类型:

1、表示构造方法类型,这个类的每个对象,都是一个确定的,具体的构造方法

2、构造方法对象应该具有的功能:获取构造方法各种信息(构造方法修饰符、构造方法名称、构造方法的参数列表、构造方法的注解),最基本的一个功能就是,创建对象

newInstance(Object...objs)

返回一个Object类型的对象

代码示例

 1 package com.Fanshe.demos;
 2 
 3  
 4 
 5 import java.lang.reflect.Constructor;
 6 
 7 import java.lang.reflect.InvocationTargetException;
 8 
 9  
10 
11 public class Demo05_获取类的构造方法并使用 {
12 
13  
14 
15 public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
16 
17 Class c = Person.class;
18 
19 //获取c中的构造方法
20 
21 Constructor con = c.getConstructor(String.class, int.class);
22 
23 //使用这个构造方法,创建有参数的Person对象
24 
25 Object obj = con.newInstance("zhangsan", 23);
26 
27 System.out.println(obj);
28 
29 }
30 
31 }
 

获取类中的成员变量并使用
1、通过Class类中的:

getField(String propertyName)

参数列表:属性的名称

返回值类型:Field,表示一个成员变量对象,所属的类型

2、Field类型:表示一个成员变量类型,每个对象都是一个具体的成员变量

作用:获取成员变量的各种信息(修饰符、注解、名称);做各种数据类型的转换

赋值:set(Object obj, Object value),用于给obj对象的,该成员变量,赋value值

代码示例

 1 package com.Fanshe.demos;
 2 
 3  
 4 
 5 import java.lang.reflect.Field;
 6 
 7  
 8 
 9 public class Demo06_获取类的成员变量并赋值 {
10 
11  
12 
13 public static void main(String[] args) throws Exception {
14 
15 Person p = new Person();
16 
17 System.out.println(p);
18 
19 //首先获取类的字节码对象
20 
21 Class c = p.getClass();
22 
23 //获取age这个成员变量
24 
25 Field f = c.getField("age");
26 
27 //给p对象的f成员变量,赋23值
28 
29 f.set(p, 23);
30 
31 System.out.println(p);
32 
33 }
34 
35 }
 

暴力反射
1、通过Class类中:

getDeclaredXxx方法:可以获取类中的所有声明的成员(属性、方法、内部类),私有的成员也可以获取到。

2、修改该对象的访问权限:

成员变量、构造方法还是成员方法,都是AccessibleObject类型的子类,就具有判断是否可以访问,和设置是否可以访问的方法

isAccessible():判断当前对象是否可以访问

setAccessible(boolean flag):设定当前对象是否可以访问

3、一旦设定当前对象可以访问,私有的成员也可以被访问,被修改

代码示例

 1 package com.Fanshe.demos;
 2 
 3  
 4 
 5 import java.lang.reflect.Field;
 6 
 7  
 8 
 9 public class Demo07_暴力反射 {
10 
11 public static void main(String[] args) throws Exception {
12 
13 Person p = new Person();
14 
15 //获取Person的字节码对象
16 
17 Class c = p.getClass();
18 
19 //获取Person类中的age成员变量,由于age是私有属性,所以需要使用getDeclaredField
20 
21 Field f = c.getDeclaredField("age");
22 
23 //判断该成员变量是否可以访问
24 
25 System.out.println(f.isAccessible());
26 
27 //将该成员变量设置为可以访问的权限
28 
29 f.setAccessible(true);
30 
31 System.out.println(f.isAccessible());
32 
33 //使用f的set方法,将p对象的f属性,设置为23这个值
34 
35 f.set(p, 23);
36 
37 System.out.println(p);
38 
39 }
40 
41 }

获取类中的成员方法并且执行
1、通过Class类中:

getMethod(String methodName, Class…paramTypes)

参数列表:methodName表示方法名称,paramTypes表示参数列表的数据类型

返回值类型:Method

2、Method类型:

1、表示成员方法的类型,该类型的每个对象,都是一个具体的成员方法

2、成员方法对象的方法:获取成员方法信息(方法的注解、方法的修饰符、方法的返回值类型、方法的名称、方法的参数)

3、其中,最重要的一个方法,就是该方法的执行:

 invoke(Object obj, Object...values)

在obj对象上,执行该方法,使用的实际参数是values

代码示例

 1 package com.Fanshe.demos;
 2 
 3  
 4 
 5 import java.lang.reflect.Method;
 6 
 7  
 8 
 9 public class Demo08_获取成员方法并执行 {
10 
11  
12 
13 public static void main(String[] args) throws Exception {
14 
15 Person p = new Person();
16 
17 //获取设置age的方法,就先获取Person类型的字节码对象
18 
19 Class c = p.getClass();
20 
21 //方法的名称:setAge
22 
23 Method m = c.getMethod("setAge", int.class);
24 
25 //给p的age属性赋值为123,不能使用暴力反射
26 
27 m.invoke(p, 123);
28 
29  
30 
31 System.out.println(p);
32 
33 }
34 
35 }
 

练习
//有如下集合

ArrayList list = new ArrayList<>();

list.add(666);

//设计代码,将字符串类型的"abc",添加到上述带有Integer泛型的集合list中

代码示例

 1 package com.Fanshe.demos;
 2 
 3  
 4 
 5 import java.lang.reflect.Method;
 6 
 7 import java.util.ArrayList;
 8 
 9 import java.util.Scanner;
10 
11  
12 
13 public class Demo09_泛型擦除 {
14 
15  
16 
17 public static void main(String[] args) throws Exception {
18 
19 //有如下集合
20 
21 ArrayList<Integer> list = new ArrayList<>();
22 
23 list.add(666);
24 
25 //设计代码,将字符串类型的"abc",添加到上述带有Integer泛型的集合list中
26 
27 // list.add("abc");
28 
29 /**
30 
31  * 在编译阶段,检查add方法的实际参数,如果在编译阶段,不要调用add方法,
32 
33  * 就会避免掉在编译阶段,对实际参数数据类型的检查
34 
35  * 在运行阶段,调用add方法
36 
37  * 使用反射的方式,调用某个方法,在写代码的阶段,根本不知道将来调用哪个方法
38 
39  * 编译器也就没有办法在编译阶段对代码进行检查
40 
41  *
42 
43  * 这种方式叫做“泛型擦除”
44 
45  * 在java中,只会在编译阶段,对泛型进行检查,到了运行阶段,对泛型不检查
46 
47  * 称这种泛型为:伪泛型
48 
49  */
50 
51 //获取类的字节码对象
52 
53 Class c = list.getClass();
54 
55 //获取类中的add方法
56 
57 String methodName = new Scanner(System.in).nextLine();
58 
59 Method m = c.getMethod(methodName, Object.class);
60 
61 //执行该方法
62 
63 m.invoke(list, "abc");
64 
65 System.out.println(list);
66 
67 }
68 
69 }
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值