反射原理
-
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
-
其实就是获取一个类的Class对象,并且操作类中的各个组成部分的一个技术。
-
在运行时可以获取。
反射使用的第一步
-
获取一个类的Class对象(字节码对象)。
-
有三种方法:(重点,熟练掌握)
-
Class.forName(“全类名”)
解耦性更好,一般用于配置文件设置全类名。在框架中经常使用。 -
类名.class
编写方便 -
对象.getClass()
即使是使用接口类型接受的对象,也能获取其字节码对象
-
-
一个类,在同一jvm中,只会加载一次。Class对象无论获取多少次,都是同一个。
Class对象的使用
-
获取成员变量
1. Field getField(String fieldName); 根据指定的成员变量名,获取指定的成员变量,只能获取非私有化成员变量 2)Field getDeclaredField(String fieldName); 根据指定的成员变量名,获取指定的成员变量,可以获取私有化成员变量
-
获取构造方法
1)Constructor getConstructor(Class… parameterTypes); 根据参数类型,获取指定参数类型的构造方法,只能获取非私有化的构造方法 2)Constructor[] getConstructors(); 获取当前类内的所有非私有化构造方法 3)Object newInstance(Object… args); 通过Constructor类对象,调用newInstance方法, 创建类对象 Object… 不定长参数,用于传入构造方法所需的参数 4)setAccessible(boolean) ; 给予 私有化构造方法,成员方法,成员变量 操作权限
-
获取成员方法
Method getMethod(String methodName, Class… parameterTypes); 根据方法名和参数类型,获取指定的非私有化成员方法 methodName:当前方法的名字 parameterTypes: 不定长参数,用于约束方法的参数 Method getDeclaredMethod(String methodName, Class… parameterTypes); 根据方法名和参数类型,可以获取指定的私有化成员方法 Method[] getMethods(); 获取类内所有非私有化成员方法,和从父类继承而来的可以在子类使用的成员方法 invoke(Object obj, Object… args); 指定Method类对象方法 obj: 执行当前方法的类对象 args:不定长参数,是当前执行方法所需的实际参数列表 1)指定方法的名称以及参数列表(用Class表示) 2)Method对象,可以调用其invoke方法执行该方法。 3)静态方法,在调用时,不需要指定在哪个对象上执行,所以直接传null即可。 staticMethod.invoke(null);
-
类中的其他信息
类名 getName() 包名 getPackageName() 类实现的所有接口 Class<?>[] getInterfaces() 在上述方法中,Declared修饰的方法代表获取所有已声明的类的组成部分。 从而可以达到绕过修饰符限制访问这些组成部分的效果。
-
注意:
真正使用这些组成部分是,需要“暴力反射”。setAccessable(true)
即使可以使用反射绕过访问修饰符,
但是实际操作时不推荐访问private修饰的组成部分。因为这种操作可能有安全隐患。
反射调用方法的步骤
-
步骤
-
获取方法所在类的字节码对象.
-
获取方法对象.
-
使用反射调用方法.
-
-
案例
public class User {
public void doWork() {
System.out.println("User doWork()");
}
public static void doWork(String name) {
System.out.println("User doWork()"+name);
}
private String sayHello(String name, int age) {
System.out.println("User sayHello()"+name+","+age);
return name + "," + age;
}
}
public class InvokeMethodDemo {
public static void main(String[] args) throws Exception {
//1.获取方法所在类的字节码对象.
Class<User> user = User.class;
//调用doWork()
//2.获取方法对象.
Method method1 = user.getMethod("doWork");
//3.使用反射调用方法.
method1.invoke(user.newInstance());//User doWork()
System.out.println("================================");
//调用doWork(String name)
//2.获取方法对象.
Method method2 = user.getMethod("doWork", String.class);
//3.使用反射调用方法.
method2.invoke(user.newInstance(), "杨哥");//User doWork()杨哥
System.out.println("================================");
//调用private String sayHello(String name,int age)
//2.获取方法对象.
Method method3 = user.getDeclaredMethod("sayHello", String.class,int.class);
method3.setAccessible(true);//设置可访问私有的成员
//3.使用反射调用方法.
String ret = (String) method3.invoke(user.newInstance(), "杨哥",18);//User sayHello()杨哥,18
System.out.println(ret);
}
}
使用反射优缺点
-
可以在配置文件中进行类的相关设置。修改功能时,无需修改代码,而是修改配置。
-
好处:
不改代码,不需要重新编译。也不需要重新打包,部署。
配置文件不参与编译,所以修改非常灵活。 -
坏处:
代码的复杂度提升了。维护代码不方便。对编程人员的技术要求更高
ClassLoader加载文件
-
类加载器。只能获取类路径下的资源。
-
getResource
获取类路径下的资源,返回一个URL对象 -
getResourceAsStream
获取类路径下的资源,返回一个流对象InputStream -
上述方法传参:
传递的是相对于类路径的一个相对路径。不需要在前面写 /