反射: 框架设计的灵魂.
反射的前提: 类进入到内存中.
反射: 我们可以使用class文件对象 , 获取class文件中的成员变量 , 成员方法 , 构造方法 .
反射的好处: 对于任何一个类 , 都可以使用相同的方式获取.
类加载器:
注: java 运行的 都 是 c l a s s 文 件.
类加载器会做两件事:
1.会把.class文件加载到内存中的方法区中!
2.会为 class文件创建一个对象!
class文件对象: 是由类加载器创建 , 我 们 无 权 创 建 和 销 毁.
1.获取Class文件对象: (只有一个)
- Class.forName(“全类名”) : 通过指定的字符串路径获取.
- 类名.class(): 通过类名的属性class获取.
- 对象.getClass(): 通过对象的getClass()方 法获取
注:3个获取方式获取的都是同一个文件对象(地址值一样.)
Student类:
public class Student {
private String name;
private int age;
public Student() {
System.out.println("1. 调用公共的无参数的构造方法 ... ...");
}
public Student(String name) {
this.name = name;
System.out.println("2. 调用公共的有参数的(String name)构造方法 ... ...");
}
private Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("3. 调用私有的有参数的(String name, int age)构造方法 ... ...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类: 获取class文件对象.
@Test
public void test02() throws ClassNotFoundException {
/**
* 需求: 获取指定类的字节码对象.
*/
// 方式一: 类名.class.
Class<Student> clazz01 = Student.class;
System.out.println(clazz01); // class com.aaa.reflect.Student
// 方式二: 对象.getClass(); 何为对象? new出来的就是对象.
Student student = new Student("张三");
Class<? extends Student> clazz02 = student.getClass();
System.out.println(clazz02); // class com.aaa.reflect.Student
// 方式三: Class.forName(类的全限定名)
Class<?> clazz03 = Class.forName("com.aaa.reflect.Student");
System.out.println(clazz03); // class com.aaa.reflect.Student
// 注: 三个方法获取的都是同一个字节码对象.
}
Class对象相关的API:
String getSimpleName(); 获得简单类名,只是类名,没有包
String getName(); 获取完整类名,包含包名+类名
T newInstance() ;创建实例化对象。 要求:类必须有public的无参数构造方法
2.获取构造方法对象:
- getConstructors(): 获取所有的public修饰的构造方法.
- getConstructor(Class…parame): 只能获取public修饰的构造方法.
- newInstance(Object…initargs): 根据指定参数创建对象.
- setAccessible(true): 设置为可以直接访问私有类型的构造方法.
举个例子:
@Test
public void test03() throws Exception {
/**
* 需求: 通过反射new对象.
* 方式一: 通过构造方法获取对象.
*/
// 1.获取类的字节码对象.
Class<?> clazz = Class.forName("com.aaa.reflect.Student");
// 2.获取构造方法对象.
Constructor<?> constructor = clazz.getConstructor();
// 3.执行.
Student stu01 = (Student) constructor.newInstance();
System.out.println(stu01);
/**
* 方式二: 通过无参数构造方法直接获取对象. (使用的是Class对象的方法)
* 使用前提: 必须要有无参构造方法.
*/
Class<?> clazz02 = Class.forName("com.aaa.reflect.Student");
Student stu02 = (Student) clazz02.newInstance();
System.out.println(stu02);
}
3.获取成员方法对象:
- getMethods(): 获取所有的public修饰的成员方法 , 包括父类中 .
- getMethod(“方法名” , 方法的参数类型…类型): 获取一个方法类型.
- invoke(Object obj,Object…args):根据参数ars调用对象obj的成员方法.
举个例子:
@Test
public void test04() throws Exception {
/**
* 需求: 通过调用setName方法 设置姓名
*/
// 1.获取类的字节码对象.
Class<?> clazz = Class.forName("com.aaa.reflect.Student");
// 2.获取对象.
Student stu = (Student) clazz.newInstance();
// 3.获取类中的setname方法.
Method method = clazz.getMethod("setName", String.class);
// 4.使用Method类中的方法invoke执行获取到的成员方法
method.invoke(stu, "老王");
System.out.println(stu); // Student{name='老王', age=0}
}
反射综合案例:
/*
反射综合案例
我们可以
创建Person对象,调用eat方法
创建Student对象,调用study方法
创建Worker对象,调用work方法
要求:
不修改代码,只能创建一个对象,调用3个类的方法
*/
public class Test {
public static void main(String[] args) throws Exception {
// 1. 创建properties对象.
Properties prop = new Properties();
// 2. 使用load方法读取文件.
prop.load(new FileReader("day13_reflect\\prop.txt"));
// 3. 使用集合中的getProperty方法, 通过key获取值.
String className = prop.getProperty("className"); // 获取路径名.
String methodName = prop.getProperty("methodName"); // 获取方法名.
// 4. 使用获取到的全类名,通过Class类的forName方法获取class文件对象
Class<?> clazz = Class.forName(className);
// 5. 创建实例化对象.
Object o = clazz.newInstance();
// 6 .使用方法.
Method method = clazz.getMethod(methodName);
method.invoke(o);
}
}
prop.txt:
className=demo03_reflectTest.Person
methodName=eat
className=demo03_reflectTest.Student
methodName=study
className=demo03_reflectTest.Worker
methodName=work