反射:研究Class与它对象及其使用。反射其实就是通过访问, Class对象,从而在运行时获取到,Class对象所对应的类的信息
获取Class对象三种方式:
第一种:调用Object类中的getClass()方法,但是如果使用此类操作则必须有实例化对象
// 第一种获取Class对象的而方式
Demo1 demo1 = new Demo1();
Class aClass = demo1.getClass();
System.out.println(aClass);
第二种:使用“类 . class”取得,此时可以不需要通过指定类的实例化对象取得
// 通过类的字面值常量,获取类对应的字节码文件对象
Class demo1Class = Demo1.class;
第三种:调用Class类提供的方法:public static Class forName(String className)
注意:类名字符串,必须得是全类名
//通过Class类的静态方法获取字节码文件对象
Class demo1 = Class.forName("com.cskaoyan.reflection.getclass.Demo1");
注意事项:第二种和第三种方式都会触发类加载过程,但是第二种获取Class对象的方式触发的是一个不完整的类加载过程,第三种方式触发的是一个完整的类加载过程
从配置文件中读取相关信息:
// 配置文件和Class.forName方法配合,完成在不修改代码的情况下,改变所要加载的类
private static void LoadForConfigFile() throws IOException, ClassNotFoundException {
//1. 此时我们加载的类的全类名,被放在配置文件中,所以我们首先要从,配置文件中读取到要加载的类的全类名
// Properties相当于一个容器,配置文件的内容,就可以放在这个容器中
// Properties对象中的一个存储单元对应一个配置:属性名 属性值
Properties properties = new Properties();
//通过流,读取配置文件内容,配置文件内容就会被自动存储到我们的Properties对象中
FileInputStream fis = new FileInputStream("config.properties");
//调用properties对象的load方法,通过这个load方法,通过流将配置文件内容读取到Properties对象中,并保存
properties.load(fis);
//从已经读取到配置文件内容并保存到Properties对象中,获取我们想要的属性值
String className = properties.getProperty("className");
//根据配置文件的配置完成类加载
Class cls = Class.forName(className);
System.out.println(cls);
}
通过Class实例对象获取构造方法:
1、获取类中多个构造方法:
Constructor[] getConstructors() //只会返回当Class对象所表示的类中,定义的public访问权限的构造方法
Class clz = Class.forName("com.cskaoyan.reflection.constructor.TestGetConstructor");
Constructor[] constructors = clz.getConstructors();
System.out.println(Arrays.toString(constructors));
Constructor[] getDeclaredConstructors() // 返回当前Class对象所表示的类中,定义的所有构造方法
Class clz = Class.forName("com.cskaoyan.reflection.constructor.TestGetConstructor");
Constructor[] declaredConstructors = clz.getDeclaredConstructors();
System.out.println(Arrays.toString(declaredConstructors));
2、获取指定的构造方法:
前提:区分一个类中定义的构造方法,通过构造方法的参数列表来区分不同的构造方法,所以在获取单个构造方法的时候,我们必须指明构造方法的参数列表。*ParameterTypes *: 表示我们要获取的那个构造方法的参数列表,参数列表使用数据类型对应字节码文件对象表示
Constructor getConstructor(Class… parameterTypes) //获取当前Class对象所表示的类中定义的具有指定参数列表的public构造方法
Class clz = Class.forName("com.cskaoyan.reflection.constructor.TestGetConstructor");
Constructor constructor = clz.getConstructor(int.class, double.class, boolean.class);
System.out.println(constructor);
Constructor getDeclaredConstructor(Class… parameterTypes) //可以获取当前Class对象所表示的类中, 定义的任意构造方法
Class clz = Class.forName("com.cskaoyan.reflection.constructor.TestGetConstructor");
Constructor declaredConstructor = clz.getDeclaredConstructor(int.class);
System.out.println(declaredConstructor);
可变参数:数据类型 … 。参数对应的个数是可变的,实质对应到一个数组。
但一个方法的可变参数,只能在方法参数列表的最后一个位置。这意味着一个方法的参数中只能最多定义一个可变参数
// 演示可变参数的含义
public static void testMehotdVariable(int... a) {
// 可变参数的实质,对应一个数组
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
System.out.println("hello variable");
}
使用获取到的构造方法: newInstance(参数列表) //使用此 Constructor 对象表示的构造方法,来创建该构造方法的声明类(这里的这个构造方法应该是属于Class类)的新实例,并用指定的初始化参数初始化该实例。需要向下转型。
private static void use(Class clz)
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class clz = Class.forName("com.cskaoyan.reflection.constructor.TestGetConstructor");
//使用获取到的构造方法:使用构造方法,创建对象
Constructor privateConstructor = clz.getDeclaredConstructor(int.class);
//暴力破解权限问题, 一旦使用setAccessble, 并且参数值传true,该方法,就可以让jvm在运行的时候,绕过权限检查机制,不在检查权限
privateConstructor.setAccessible(true);
TestGetConstructor o = (TestGetConstructor) privateConstructor.newInstance(100);
System.out.println(o.intValue);
}
class TestGetConstructor {
public int intValue;
public double doubleValue;
public boolean booleanValue;
//私有权限的一参构造方法
private TestGetConstructor(int intValue) {
this.intValue = intValue;
}
//默认权限的,无参构造方法
TestGetConstructor() {}
/*
受保护的访问权限的,两参构造
*/
protected TestGetConstructor(double doubleValue, boolean booleanValue) {
this.doubleValue = doubleValue;
this.booleanValue = booleanValue;
}
/*
public 访问权限的3参构造
*/
public TestGetConstructor(int intValue, double doubleValue, boolean booleanValue) {
this.intValue = intValue;
this.doubleValue = doubleValue;
this.booleanValue = booleanValue;
}
}
通过Class实例对象获取成员变量:
1、获取多个成员变量:
前提:Field类用来描述所有的成员变量(包括静态成员变量),一个Field对象表示类中定义 的一个成员变量
Field[] getFields() //获取当前Class对象,所表示的类中定义的 public 成员变量,同时还可以通过该方法,获取到该类的父类中定义的public成员变量
class Father {
//父类中定义的public成员变量
public int i;
double fatherJ;
}
class Son extends Father {
public int i;
double j;
protected boolean k;
private String name;
}
public class Demo1 {
public static void main(String[] args) {
//拿到子类对应的字节码文件
Class sonClass = Son.class;
Field[] fields = sonClass.getFields();
System.out.println(Arrays.toString(fields));
}
}
Field[] getDeclaredFields() //获取当前Class对象,所表示的类中定义的所有成员变量但是不包括父类中定义的成员变量
class Father {
//父类中定义的public成员变量
public int i;
double fatherJ;
}
class Son extends Father {
public int i;
double j;
protected boolean k;
private String name;
}
public class Demo1 {
public static void main(String[] args) {
//拿到子类对应的字节码文件
Class sonClass = Son.class;
// Field[] getDeclaredFields()
Field[] declaredFields = sonClass.getDeclaredFields();
System.out.println(Arrays.toString(declaredFields));
}
}
2、获取指定的成员变量
Field getField(String name) //可以通过指定成员变量的变量名,从Class对象对应的类中获取指定名称的public成员变量(Field对象表示), 还可以获取到父类中定义的同名public成员变量(子类中)
注意事项:该方法,在类中查找指定名称的public的成员变量的顺序是 先子类,如果子类中没有, 再在父类中查找。如果子类、父类中都没有找到指定名称的成员变量,此时抛出异常 NoSuchFieldException
Field getDeclaredField(String name) //在Class对象表示的类中,获取任意权限的指定名称的成员变量(不包括父类中定义的成员变量)
使用获取到的成员变量:
1、可以在该类的任意对象上,获取该成员变量的值—Object get(Object obj) 在指定对象上,获取该 成员变量 的值( Field对象.get(指定对象) )
private static void getUse(Class sonClass) throws NoSuchFieldException, IllegalAccessException {
//利用反射,可以做到访问对象的私有成员变量
Son sonObj = new Son("张三");
//首先获取Son类中定义的name成员变量
//Field name = sonClass.getDeclaredField("name");
//破解权限问题
//name.setAccessible(true); //张三
//在指定对象上获取,该成员变量的值
//String o = (String) name.get(sonObj);
//System.out.println(o);
//对于基本数据类型的成员变量,也可以用这种方式获取成员变量的值
Field j = sonClass.getDeclaredField("j");
//破解权限问题
j.setAccessible(true); //张三
//在指定对象上获取,该成员变量的值
double o = (double) j.get(sonObj);
System.out.println(o);
}
2、可以在该类的任意对象上,设置该成员变量的值—void set(Object obj, Object newValue) 在指定对象上,将该成员变量的值,修改为新的值( Field对象.set(目标对象, 要修改的新值) )
private static void setUse(Class sonClass) throws NoSuchFieldException, IllegalAccessException {
//创建目标对象
Son son = new Son(false);
// 获取表示成员变量的Field对象
Field k = sonClass.getDeclaredField("k");
//解决权限问题
k.setAccessible(true);
//修改指定对象上的,Field对象表示的成员变量的值,为新值
k.set(son, true);
System.out.println(son);
}
通过Class实例对象获取成员方法:
1、获取多个成员方法:
前提:Method该类用来描述类中定义的方法,一个Method对象,表示类中定义的一个方法
Method[] getMethods() // 获取当前Class对象表示类中的public成员方法, 也可以获取到当前类的父类中定义的public 成员方法
private static void getMultiMethods(Class clz) {
// Method[] getMethods()
Method[] methods = clz.getMethods();
//System.out.println(Arrays.toString(methods));
//Method[] getDeclaredMethods()
Method[] declaredMethods = clz.getDeclaredMethods();
System.out.println(Arrays.toString(declaredMethods));
}
Method[] getDeclaredMethods() // 获取当前Class对象表示类中所有的成员方法(但是不包括父类中定义的)
2、获取指定的成员方法:
Method getMethod(String name, Class... parameterTypes) //可以获取当前类及其父类中定义的public方法
Method getDeclaredMethod(String name, Class... parameterTypes) //只能获取当前类中定义的任意访问权限的方法(不包括父类中定义的方法)
private static void getSingleMethod(Class clz) throws NoSuchMethodException {
//Method getMethod(String name, Class... parameterTypes)
Method publicMethod = clz.getMethod("publicMethod", int.class, String.class);
System.out.println(publicMethod);
//Method getDeclaredMethod(String name, Class... parameterTypes)
Method privateMethod = clz.getDeclaredMethod("privateMethod");
System.out.println(privateMethod);
}
通过反射获取到的方法的使用:就是在指定对象上,传递实际参数,并调用该方法—Object invoke(Object obj, Object… args)( Method对象.invoke(指定对象,方法运行所需要的实际参数) )
private static void use(Class clz)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
//利用反射来完成
MethodClass methodObj = new MethodClass();
// 获取要执行的目标方法信息
Method secondPrivate = clz.getDeclaredMethod("secondPrivate", int.class, String.class);
//处理权限问题
secondPrivate.setAccessible(true);
// 在指定对象上,调用Method对象所表示的方法
int result = (int) secondPrivate.invoke(methodObj, 10, "张三");
System.out.println(result);
// 获取并调用类中定义的静态方法
Method staticMethod = clz.getDeclaredMethod("staticMethod", int.class);
//调用静态方法
staticMethod.invoke(null, 10);
}