一、调用构造器
1、调用运行时类中的指定的构造器
① 调用 getDeclaredConstructor() 方法根据参数获取指定的构造器
② 保证此构造器是可以访问的(暴力反射,针对非 public 修饰)
③ 调用 newInstance(Object obj, initargs) 方法创建对象
1 @Test
2 public void testConstructor() throws Exception {
3 Class clazz = Person.class;
4
5 //private Person(String name)
6 /*
7 1.获取指定的构造器
8 getDeclaredConstructor():参数:指明构造器的参数列表
9 */
10
11 Constructor constructor = clazz.getDeclaredConstructor(String.class);
12
13 //2.保证此构造器是可访问的
14 constructor.setAccessible(true);
15
16 //3.调用此构造器创建运行时类的对象
17 Person per = (Person) constructor.newInstance("Tom");
18 System.out.println(per);
19
20 }
2、
二、调用属性
1、调用指定属性
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作
public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。
在 Field 中:
public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容
2、操作运行时类的指定的 public 的属性
① 获取运行时的类;
② 获取指定的属性:要求运行时类中属性声明为public
③ 设置、获取当前属性的值
1 @Test
2 public void testField() throws Exception {
3 Class clazz = Person.class;
4
5 //创建运行时类的对象
6 Person p = (Person) clazz.newInstance();
7
8
9 //获取指定的属性:要求运行时类中属性声明为public
10 //通常不采用此方法
11 Field id = clazz.getField("id");
12
13 /*
14 设置当前属性的值
15
16 set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少
17 */
18
19 id.set(p,1001);
20
21 /*
22 获取当前属性的值
23 get():参数1:获取哪个对象的当前属性值
24 */
25 int pId = (int) id.get(p);
26 System.out.println(pId);
27
28
29 }
3、操作运行时类中的指定的非public修饰属性
① 获取运行时的类;
② 获取运行时类中指定变量名的属性;
③ 保证当前属性是可访问的(暴力反射,防止非 public 修饰的)
setAccessible(true):暴力反射 // 忽略访问权限修饰符的安全检查
④ 获取、设置指定对象的此属性值
1 @Test
2 public void testField2() throws Exception {
3 Class clazz = Person.class;
4
5 //创建运行时类的对象
6 Person p = (Person) clazz.newInstance();
7
8 //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
9 Field name = clazz.getDeclaredField("name");
10
11 //2.保证当前属性是可访问的
12 name.setAccessible(true); //暴力反射
13 //3.获取、设置指定对象的此属性值
14 name.set(p,"Tom");
15
16 System.out.println(name.get(p));
17 }
三、调用方法
1、操作运行时类中的指定的方法
Object invoke(Object obj, Object … args) 执行方法
说明:
① Object 对应原方法的返回值,若原方法无返回值,此时返回null;
② 若原方法若为静态方法,此时形参Object obj可为null;
③ 若原方法形参列表为空,则Object[] args为null;
④ 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
操作步骤:
① 根据运行时类对象获取指定的某个方法
② 保证当前方法是可访问的(暴力反射,防止非 public 修饰)
③ 调用invoke() 方法
1 @Test
2 public void testMethod() throws Exception {
3
4 Class clazz = Person.class;
5
6 //创建运行时类的对象
7 Person p = (Person) clazz.newInstance();
8
9 /*
10 1.获取指定的某个方法
11 getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表
12 */
13 Method show = clazz.getDeclaredMethod("show", String.class);
14 //2.保证当前方法是可访问的
15 show.setAccessible(true);
16
17 /*
18 3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
19 invoke()的返回值即为对应类中调用的方法的返回值。
20 */
21 Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
22 System.out.println(returnValue);
23
24 System.out.println("*************如何调用静态方法*****************");
25
26 // private static void showDesc()
27
28 Method showDesc = clazz.getDeclaredMethod("showDesc");
29 showDesc.setAccessible(true);
30 //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
31 //Object returnVal = showDesc.invoke(null);
32 Object returnVal = showDesc.invoke(Person.class);
33 System.out.println(returnVal);//null
34
35 }
2、
四、关于 setAccessible 方法
1、Method和Field、 Constructor对象都有setAccessible()方法。
2、setAccessible启动和禁用访问安全检查的开关。
3、参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
① 提高反射的效率。 如果代码中必须用反射, 而该句代码需要频繁的被调用, 那么请设置为true。
② 使得原本无法访问的私有成员也可以访问;
4、参数值为 false 则指示反射的对象应该实施Java语言访问检查。
五、操作数组
在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array类来动态的创建数组,操作数组元素等。
Array 类提供了如下几个方法:
public static Object newInstance(Class<?> componentType, int... dimensions):创建一个具有指定的组件类型和维度的新数组
public static void setXxx(Object array,int index,xxx value):将array数组中[index]元素的值修改为value。
public static xxx getXxx(Object array,int index,xxx value):将array数组中[index]元素的值返回。
此处的 Xxx 对应 8 种基本数据类型,如果该属性的类型是引用数据类型,则直接使用 set(object arr,int index, Object value)方法或get(Object array, int index) 方法。
Demo:
1 import java.lang.reflect.Array;
2
3 public class TestArray {
4 public static void main(String[] args) {
5 Object arr = Array.newInstance(String.class, 5);
6 Array.set(arr, 0, "Hello");
7 Array.set(arr, 1, "World");
8 System.out.println(Array.get(arr, 0));
9 System.out.println(Array.get(arr, 1));
10 System.out.println(Array.get(arr, 2));
11 }
12 }
五、案例
需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
实现步骤:
实现技术
① 配置文件
② 反射
实现步骤:
① 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
② 在程序中加载读取配置文件
③ 使用反射技术来加载类文件进内存
④ 创建对象
⑤ 执行方法
代码实现:
1 // 定义的配置文件 pro.properties
2 className=cn.java.domain.Student
3 methodName=sleep
程序:
1 // 框架类
2 public class ReflectTest {
3 public static void main(String[] args) throws Exception {
4 //可以创建任意类的对象,可以执行任意方法
5
6 /*
7 前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
8 */
9
10 //1.加载配置文件
11 //1.1创建Properties对象
12 Properties pro = new Properties();
13 //1.2加载配置文件,转换为一个集合
14 //1.2.1获取class目录下的配置文件
15 ClassLoader classLoader = ReflectTest.class.getClassLoader();
16 InputStream is = classLoader.getResourceAsStream("pro.properties");
17 pro.load(is);
18
19 //2.获取配置文件中定义的数据
20 String className = pro.getProperty("className");
21 String methodName = pro.getProperty("methodName");
22
23
24 //3.加载该类进内存
25 Class cls = Class.forName(className);
26 //4.创建对象
27 Object obj = cls.newInstance();
28 //5.获取方法对象
29 Method method = cls.getMethod(methodName);
30 //6.执行方法
31 method.invoke(obj);
32
33
34 }
35 }