反射
- 1、反射:框架的灵魂
-
- 1.1 概述
- 2、获取Class对象
-
- 2.1 获取Class对象方式
- 3、Class对象功能
-
- 3.1 获取功能(部分,具体参考api文档)
-
- 1、获取成员变量
- 2、获取构造方法
- 3、获取成员方法
- 4、获取全类名
- 3.2 java.lang.reflect.Field:成员变量
- 3.3 java.lang.reflect.Method:方法对象
- 4、反射机制的应用案例
-
- 需求
- 实现
- 思路
- 代码实现
1、反射:框架的灵魂
1.1 概述
- 框架:半成品软件。可以在框架的基础上进行软件开发,简化编码
反射:将类的各个组成部分封装为其他对象,这就是反射机制;学习使用框架并不需要了解反射,但是如果想要自己写一个框架,那么就需要对反射机制有很深入的了解。
好处:
1. 可以在程序运行过程中,操作这些对象。
2. 可以解耦,提高程序的可扩展性。
在学习反射以前,我们先来了解一下Java代码在计算机中所经历的三个阶段:

1、Source源代码阶段:.java被编译成*.class字节码文件。
2、Class类对象阶段:.class字节码文件被类加载器加载进内存,并将其封装成Class对象(用于在内存中描述字节码文件),Class对象将原字节码文件中的成员变量抽取出来封装成数组Field[],将原字节码文件中的构造函数抽取出来封装成数组Construction[],将成员方法封装成数组Method[]。当然Class类内不止这三个,还封装了很多,我们常用的就这三个。
3、RunTime运行时阶段:使用new创建对象的过程。
2、获取Class对象
2.1 获取Class对象方式
Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
* 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
2. 类名.class:通过类名的属性class获取
* 多用于参数的传递
3. 对象.getClass():getClass()方法在Object类中定义着。
* 多用于对象的获取字节码的方式
* 结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
代码实现:
//方法一:对象.getClass()
public void test01(){
Person p = new Person();
Class<? extends Person> aClass = p.getClass();
System.out.println(aClass);
}
//方法二:类名.class
public void test02(){
Class<Person> personClass = Person.class;
System.out.println(personClass);
}
test01和test02获取类对象,都需要依赖api Person
//方法三:Class.forName("全类名");
public void test03() throws ClassNotFoundException {
//手动加载Person.class文件进内存
Class aClass = Class.forName("net.zretc.reflect.Person");//传入Person的路径
System.out.println(aClass);
}
3、Class对象功能
3.1 获取功能(部分,具体参考api文档)
1、获取成员变量
Field[] getFields() //获取所有public修饰的成员变量
Field getField(String name) //获取指定名称的public修饰的成员变量
Field[] getDeclaredFields() //获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name) //获取指定的成员变量,不考虑修饰符
2、获取构造方法
Constructor<?>[] getConstructors() //获取所有public修饰的构造函数
Constructor<T> getConstructor(类<?>... parameterTypes) //获取指定的public修饰的构造函数
Constructor<?>[] getDeclaredConstructors() //获取所有的构造函数,不考虑修饰符
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) //获取指定的构造函数,不考虑修饰符
3、获取成员方法
Method[] getMethods() //获取所有public修饰的成员方法
Method getMethod(String name, 类<?>... parameterTypes) //获取指定名称的public修饰的成员方法
Method[] getDeclaredMethods() //获取所有的成员方法,不考虑修饰符
Method getDeclaredMethod(String name, 类<?>... parameterTypes) //获取指定名称的成员方法,不考虑修饰符
4、获取全类名
String getName()
3.2 java.lang.reflect.Field:成员变量
- 操作:
- 设置值 void set(Object obj, Object value)
- 获取值 Object get(Object obj)
- 忽略访问权限修饰符的安全检查 setAccessible(true):暴力反射
Field的get/set方法举例代码:
测试实体类:
class SupClass{
public String gendar;
}
public class Person {
public String name;
byte[] b;
private int age;
public void show(){
System.out.println("show------null----");
}
public void show(String name,int age){
System.out.println("show------name---age----");
}
private void sup(){
System.out.println("sup---");
}
}
测试类:
public void test06() throws Exception {
Class pClass = Class.forName("net.zretc.reflect.Person");
Field name_field = pClass.getDeclaredField("name");//私有变量
Person person = (Person) pClass.newInstance();//实例化对象
//设置访问权限:暴力反射
name_field.setAccessible(true);
Object name_value = name_field.get(person);//person.getName()
System.out.println(name_value);
}
public void test05() throws Exception {
Class pClass = Class.forName("net.zretc.reflect.Person");
Field age_field = pClass.getField("age");
//实例化Person
Person person = (Person) pClass.newInstance();//newInstance相当于执行的空参数的构造器
//设置值
age_field.set(person,20); //person.setAge(20);
//查看age变量的值
Object age_value = age_field.get(person); // person.getAge()
System.out.println(age_value);
}
3.3 java.lang.reflect.Method:方法对象
- 执行方法:Object invoke(Object obj, Object… args) 没有返回值,为null
-
获取方法名称:String getName:获取方法名
代码展示
public void test08() throws Exception {
//类对象
Class pClass = Class.forName("net.zretc.reflect.Person");
//实例化Person
Person person = (Person) pClass.newInstance();
Method test = pClass.getDeclaredMethod("test");//私有化方法
//暴力反射
test.setAccessible(true);
test.invoke(person);
//show方法对象
Method show = pClass.getMethod("show", String.class, int.class);
//执行show方法: person.show("hehe",30)
Object result = show.invoke(person, "hehe", 30);
System.out.println(result);//没有返回值,为null
}
4、反射机制的应用案例
需求
写一个"框架",在不改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中的任意方法。
实现
(1)配置文件
(2)反射机制
思路
(1)将需要创建的对象的全类名和需要执行的方法定义在配置文件中
(2)在程序中加载读取配置文件
(3)使用反射技术把类文件加载进内存
(4)创建对象
(5)执行方法
代码实现
实体类:
Person 类
public class Person {
//无参方法
public void eat(){
System.out.println("eat...");
}
}
Student类
public class Student {
//无参方法
public void study(){
System.out.println("I am a Student");
}
}
配置文件编写:
className =zretc.edu.cn.Person
methodName = eat
代码:
public class ReflectTest {
public static void main(String[] args) throws Exception {
//1.加载配置文件
//1.1创建Properties对象
Properties pro = new Properties();
//1.2加载配置文件
//1.2.1获取class目录下的配置文件(使用类加载器)
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("pro.properties");
pro.load(inputStream);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(obj);
}
}