反射:框架设计的灵魂
本笔记为个人学习笔记,仅供学习。视频链接:https://www.bilibili.com/video/BV1C4411373T
框架:半成品软件。可以在框架的基础上进行软件开发,简化编码反射:将类的各个组成部分封装为其他对象,这就是反射机制
一.Java代码在计算机经历的三个阶段:
使用反射好处:
- 可以在程序运行过程中,操作这些对象。(例子:代码提示时弹出自定义方法)
- 可以解耦,提高程序的可扩展性。
二. 获取Class对象的方式:
- Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
多用于配置文件,将类名定义在配置文件中。读取文件,加载类
- 类名.class:通过类名的属性class获取
多用于参数的传递
- 对象.getClass():getClass()方法在Object类中定义着。
多用于对象的获取字节码的方式
每个阶段对应一种方法:
测试代码:
package reflect;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
public class ReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
//方式一:Class.forName("全类名")
Class aClass = Class.forName("reflect.Person");
System.out.println(aClass);
//2. 类名.class:通过类名的属性class获取
Class aClass1 = Person.class;
System.out.println(aClass1);
//3. 对象.getClass():getClass()方法在Object类中定义着。
Person person=new Person();
Class aClass2 = person.getClass();
System.out.println(aClass2);
//比较三个对象
System.out.println(aClass==aClass1);//true
System.out.println(aClass==aClass2);//true
System.out.println(aClass1==aClass2);//true
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person{
private String name;
private int age;
}
结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
三. Class对象功能:
获取功能:
获取成员变量们
Field[] getFields() :获取所有public修饰的成员变量
Field getField(String name) 获取指定名称的 public修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getDeclaredField(String name)
获取构造方法们
Constructor<?>[] getConstructors()
Constructor getConstructor(类<?>… parameterTypes)
Constructor getDeclaredConstructor(类<?>… parameterTypes)
Constructor<?>[] getDeclaredConstructors()
- 获取成员方法们:
Method[] getMethods()
Method getMethod(String name, 类<?>… parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>… parameterTypes)
- 获取全类名
- String getName()
1.Field:成员变量
操作:
//1. 设置值
void set(Object obj, Object value)//
//2. 获取值
get(Object obj)
//3. 忽略访问权限修饰符的安全检查
*setAccessible(true):暴力反射
代码:
/*
1. 获取成员变量们
- Field[] getFields() :获取所有public修饰的成员变量
- Field getField(String name) 获取指定名称的 public修饰的成员变量
- Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
- Field getDeclaredField(String name)
*/
public class ReflectFields {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Class personClass = Person.class;
//Field[] getFields() :获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.println(" "+field);
}
//Field getField(String name):获取指定名称的 public修饰的成员变量
Field pub=personClass.getField("pub");
System.out.println(pub);
//获取成员变量的值
Person person=new Person();
Object value=pub.get(person);
System.out.println("value = [" + value + "]");
//设置pub的值
pub.set(person,"KAKA");
System.out.println("pub = [" + person.getPub() + "]");
//Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField:declaredFields) {
System.out.println(" "+declaredField);
}
// - Field getDeclaredField(String name)
Field pri = personClass.getDeclaredField("pri");
//忽略访问权限修饰符的安全检查:暴力反射
pri.setAccessible(true);
Object value2=pri.get(person);
System.out.println("value2 = [" + value2 + "]");
}
}
2. Constructor:构造方法
操作:
T newInstance(Object... initargs)
// 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
代码:
/*
2. 获取构造方法们
- Constructor<?>[] getConstructors()
- Constructor<T> getConstructor(类<?>... parameterTypes)
- Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
- Constructor<?>[] getDeclaredConstructors()
*/
public class ReflectConstructors {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class personClass = Person.class;
//Constructor<?>[] getConstructors()
Constructor constructor = personClass.getConstructor(String.class, int.class);
System.out.println("constructor = [" + constructor + "]");
//创建对象
Object kaka = constructor.newInstance("kaka", 22);
System.out.println("person="+kaka);
//空参构造
Constructor constructor1 = personClass.getConstructor();
System.out.println("constructor1 = [" + constructor1 + "]");
Object kaka1 = constructor1.newInstance();
System.out.println("person="+kaka1);
//简化空参构造
Object kaka3=personClass.newInstance();
System.out.println("person = [" + kaka3 + "]");
}
}
3. Method:方法对象
//执行方法:
Object invoke(Object obj, Object... args)
//获取方法名称:
String getName:获取方法名
代码:
/*
3. 获取成员方法们:
- Method[] getMethods()
- Method getMethod(String name, 类<?>... parameterTypes)
- Method[] getDeclaredMethods()
- Method getDeclaredMethod(String name, 类<?>... parameterTypes)
4. 获取全类名
- String getName()
*/
public class ReflectMethosd {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class personClass = Person.class;
//Method getMethod(String name, 类<?>... parameterTypes)
Person person=new Person();
Method eat_method = personClass.getMethod("eat");
eat_method.invoke(person);
//加入方法参数
Method eat_method1 = personClass.getMethod("eat",String.class);
eat_method1.invoke(person,"rice");
//Method[] getMethods()
Method[] methods = personClass.getMethods();
for (Method m:methods) {
//获取方法名
System.out.print(m.getName()+"===>");
System.out.println("[" + m + "]");
}
//获取类名
String className = personClass.getName();
System.out.println("className = [" + className + "]");
}
}
四. 案例:
**需求:**写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
实现:
- 配置文件
- 反射
步骤:
1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
2. 在程序中加载读取配置文件
3. 使用反射技术来加载类文件进内存
4. 创建对象
5. 执行方法
项目架构:
代码:
pro.properties:
className=reflectExample.Student
methodName=sleep
ReflectExampleTest.class
package reflectExample;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
//不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
public class ReflectExampleTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1.加载配置文件
//1.1创建Properties对象
Properties properties=new Properties();
ClassLoader classLoader = ReflectExampleTest.class.getClassLoader();
//注意maven项目配置文件要放在Resources下
InputStream resource = classLoader.getResourceAsStream("pro.properties");
properties.load(resource);
//2.获取配置文件中定义的数据
String className = properties.getProperty("className");
String methodName =properties.getProperty("methodName");
//3.反射加载该类进内存
Class aClass = Class.forName(className);
//4.创建对象
Object o = aClass.newInstance();
//5.获取方法对象
Method method = aClass.getMethod(methodName,int.class);
//6.执行方法
method.invoke(o,9);
}
}