avatar

反射

概念

官方介绍

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

通俗解释

把.class文件加载到内存,并创建对应的Class对象(这是第一步),然后获取Class对象(字节码对象)中的Constructor,Field,Method(这是第二步)。并通过这三个对象任意操作内存中的对象属性和行为(这是第三步)。

名词解释:

Constructor 构造方法描述器

Field 属性描述器

Method 方法描述器

获取Class对象的方式

当Person.class或者Dog.class文件加载到内存后可以得到一个对象,这个对象叫做Class,我们称为字节码对象,.class文件我们称为字节码文件,下面介绍如何把.class文件加载到内存,并得到对应的Class对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class 获取Class对象的三种方式 {
public static void main(String[] args) throws Exception {
//1.类名.class
Class clazz = Person.class;

//2.Class.forName("全类名");
Class clazz2 = Class.forName("cn.itcast.domain.Person");

//3.对象名.getClass()
Class clazz3 = new Person().getClass();

System.out.println(clazz==clazz2);//true
System.out.println(clazz3==clazz2);//true
}
}

输出都是true,说明同一个.class文件被加载到内存后我们得到的Class对象都是一样的

通过反射操作对象的属性

案例一:通过反射给person对象的name属性赋值,并且在获取name属性的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Person person = new Person("李四", 13);



/*1.获取字节码对象*/
Class clazz = Person.class;

//2.获取name属性对应的Field对象
Field field = clazz.getDeclaredField("name");

//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
field.setAccessible(true);

//4.通过field给person对象的name属性赋值,等价于 person.name = "张三";
field.set(person, "张三");

//5.通过field获取person对象的name属性值,等价于 String name = person.name;
String name = (String)field.get(person);

//测试输出结果
System.out.println(name);//输出张三

通过反射操作构造方法

案例一:获取public Person(String name, int age){}构造方法对应的Constructor并创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
//1.获取字节码对象
Class clazz = Person.class;

//2.获取Person(String, int)构造对应的Constructor对象
Constructor constructor = clazz.getDeclaredConstructor(String.class,int.class);

//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
constructor.setAccessible(true);

//4.通过constructor创建对象,等价于 Person person = new Person("张三",13);
Person person = (Person)constructor.newInstance("张三", 13);

System.out.println(person);

案例二:获取public Person(int age, String name){}构造方法对应的Constructor并创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
//1.获取字节码对象
Class clazz = Person.class;

//2.获取Person(int age, String name)构造对应的Constructor对象
Constructor constructor = clazz.getDeclaredConstructor(int.class,String.class);

//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
constructor.setAccessible(true);

//4.通过constructor创建对象,等价于 Person person = new Person(13,"张三");
Person person = (Person)constructor.newInstance(13,"张三");

System.out.println(person);

案例三:获取Person()构造方法对应的Constructor并创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
//1.获取字节码对象
Class clazz = Person.class;

//2.获取Person()构造对应的Constructor对象
Constructor constructor = clazz.getDeclaredConstructor();

//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
constructor.setAccessible(true);

//4.通过constructor创建对象,等价于 Person person = new Person();
Person person = (Person)constructor.newInstance();

System.out.println(person);

思考

1.getDeclaredConstructor方法应该传入什么参数

2.newInstance方法需要传入什么参数

通过反射操作对象的普通方法

案例一:通过反射调用person对象的public setName(String name){}方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Person person = new Person("张三", 13);



//1.获取字节码对象
Class clazz = Person.class;

//2.获取public setName(String name){} 方法对应的Method对象
Method setName = clazz.getDeclaredMethod("setName",String.class);

//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
setName.setAccessible(true);

//4.执行person对象的setName方法,等价于person.setName("李四");
setName.invoke(person,"李四");

System.out.println(person);

案例二:通过反射调用person对象的public getName(){}方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Person person = new Person("张三", 13);



//1.获取字节码对象
Class clazz = Person.class;

//2.获取public String getName(){} 方法对应的Method对象
Method getName = clazz.getDeclaredMethod("getName");

//3.暴力反射,目的1.获取私有属性访问权限 2.提升程序性能
getName.setAccessible(true);

//4.执行person对象的getName方法,等价于String name = person.getName("李四");
String name = getName.invoke(person);

System.out.println(name);

思考

1.getDeclaredMethod方法应该传入什么参数

2.invoke方法需要传入什么参数

反射案例

通过反射执行配置文件中指定类的指定方法

在src根目录新建配置文件config.properties,内容如下:

1
2
className=cn.itcast.domain.Student
methodName=sleep

Student类代码,该类必须放在cn.itcast.domain包下

1
2
3
4
5
public class Student{
public void sleep(){
System.out.println("睡觉");
}
}

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Properties properties = new Properties();
//表示将src目录下的config.properties中的数据加载到当前properties集合中
properties.load(反射的应用真实案例.class.getClassLoader().getResourceAsStream("config.properties"));

/*目的:执行className对应的类中methodName对应的方法*/
/*换句话说目的就是:执行Student类中sleep()方法*/

/*1.根据配置文件中className的值将Student类加载到内存,得到Class字节码对象*/
String className = properties.getProperty("className");
Class clazz = Class.forName(className);

/*2.通过Class字节码对象得到Constructor对象,并创建Student对象*/
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object stu = constructor.newInstance();

/*3.通过Class字节码对象和methodName的值得到Method对象,并执行stu对象的方法*/
String methodName = properties.getProperty("methodName");//methodName的值sleep
Method method = clazz.getDeclaredMethod(methodName);
method.setAccessible(true);
method.invoke(stu);

好处

通过反射创建的这个测试类代码通用性非常强,可以执行任意类的任意无参方法。好处就是把项目部署到服务器以后如果想执行其他类的方法,只需要修改服务器上的config.properties配置文件即可。如果按照以前的方式:new Student().sleep(),想执行其他类的方法那么必须要在开发者电脑上修改源代码,重新编译项目,最后还需要重新上传到服务器,非常麻烦。

文章作者: 微信:hao_yongliang
文章链接: https://haoyongliang.gitee.io/2019/12/04/javaSE/%E5%8F%8D%E5%B0%84/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 郝永亮的主页
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论