反射原理:
* 反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
* 其实就是获取一个类的Class对象,并且操作类中的各个组成部分的一个技术。
* 在运行时可以获取。
反射使用的第一步:
* 获取一个类的Class对象(字节码对象)。
* 有三种方法:(重点,熟练掌握)
1. Class.forName("全类名")
解耦性更好,一般用于配置文件设置全类名。在框架中经常使用。
2. 类名.class
编写方便
3. 对象.getClass()
即使是使用接口类型接受的对象,也能获取其字节码对象
* 一个类,在同一jvm中,只会加载一次。Class对象无论获取多少次,都是同一个。
Class对象的使用
1. 获取成员变量
1)Field getField(String fieldName);
根据指定的成员变量名,获取指定的成员变量,只能获取非私有化成员变量
2)Field getDeclaredField(String fieldName);
根据指定的成员变量名,获取指定的成员变量,可以获取私有化成员变量
2. 获取构造方法
1)Constructor getConstructor(Class… parameterTypes);
根据参数类型,获取指定参数类型的构造方法,只能获取非私有化的构造方法
2)Constructor[] getConstructors();
获取当前类内的所有非私有化构造方法
3)Object newInstance(Object… args);
通过Constructor类对象,调用newInstance方法,
创建类对象 Object… 不定长参数,用于传入构造方法所需的参数
4)setAccessible(boolean) ;
给予 私有化构造方法,成员方法,成员变量 操作权限
3. 获取成员方法
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);
4. 类中的其他信息
类名 getName()
包名 getPackageName()
类实现的所有接口 Class<?>[] getInterfaces()
在上述方法中,Declared修饰的方法代表获取所有已声明的类的组成部分。
从而可以达到绕过修饰符限制访问这些组成部分的效果。
注意:真正使用这些组成部分是,需要“暴力反射”。setAccessable(true)
即使可以使用反射绕过访问修饰符,
但是实际操作时不推荐访问private修饰的组成部分。因为这种操作可能有安全隐患。
反射调用方法的步骤
* 步骤
1):获取方法所在类的字节码对象.
2):获取方法对象.
3):使用反射调用方法.
* 案例
* 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
* 上述方法传参:
传递的是相对于类路径的一个相对路径。不需要在前面写 /