1.反射的概念
反射是一种机制,利用反射可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量、成员方法、构造方法)。
通俗来讲,在我们开发需要用到某个类时,必须知道它是什么类,是用来干嘛的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
Person p=new Person();
p.setAge(9);
上述操作,对Person类进行初始化,可以理解为“正射”。而反射则是一开始并不知道我们要初始化的类的对象是什么,自然就不能用new关键字指定来创建对象了。这个时候,我们使用JDK提供的反射API进行反射调用。
Class clz=Class.forName("com.dwk.reflect.Person");
Method method=clz.getMethod("setAge",int.class);
Constructor construcotr=clz.getConstructor();
Object object =construcotr.newInstance();
method.invoke(object,4);
上述两个代码的执行结果是完全一样的,但是创建类的方式和思路却完全不一样,第一段正射代码是在未运行的时候确定了要创建Person类,而第二段反射代码则是在运行时,通过字符串值才得知要运行的类("com.dwk.reflect.Person")
。
所以,反射就是在运行时才知道要操作的类是什么,并且在运行时获取类的完整构造,并调用对应的方法。
1.1反射的过程
首先,会有一个java文件,然后编译成.class文件,字节码文件(里面包含了当前这个类里面所有的数据信息)会加载到内存,JVM负责管理,为了方便管理,它会将每一个加载进来的class文件生成一个对应的Class对象<对应的类型就是生成字节码之前的java文件对应的类名类型>,这个class对象它就代表对应的类(java文件),那么我们可以通过这个类对应的class对象获得这个类中的数据信息(成员变量、成员方法、构造函数)
所以,我们使用反射,其实就是获得某个类的Class对象,然后通过这个对象对类中的成员数据进行处理。
1.2反射前提
我们必须得到这个类的字节码文件(.class文件)=====》Class对象。
Java类与Class对象的对应关系:
一个类 一个Class对象
类中的一个成员变量 一个Field对象
类中的一个成员方法 一个Method对象
类中的一个构造方法 一个Constructor对象
1.3反射的固定套路
1.先获取字节码文件
下面展示一些 内联代码片
。
Class clz=Class.forName("com.dwk.reflect.Person");
2.获取构造函数
Constructor construcotr=clz.getConstructor();
若构造函数有参,则传入对应类型的.class
Constructor construcotr=clz.getConstructor(int.class,String.class);
3.利用构造函数反射创建对象
下面展示一些 内联代码片
。
//无参构造函数对象
Person p = (Person) constructor.newInstance();
//有参构造函数对象
Person p1 = (Person) constructor.newInstance(18,"vlaicp3");
4.获取到想要反射调用的方法
这里只获取的public修饰的方法,至于获取私有方法后续会有提到
Method method=clz.getMethod("setAge");
//开启暴力访问
method.setAccessible(true);
5.传入对象,反射调用方法
method.invoke(p);
//如果方法有返回值,则定义一个Object去接收
Object str= method.invoke(p);
2.如何获得类对应的Class对象
2.1通过Class类的静态方法
// 参数:某个类的全限定类名(包含包名和类名)
static Class<?> forName(String className) // 返回与给定字符串名称的类或接口相关联的Class对象。
Class<?> clazz = Class.forName("包名.类名");
// 给定类的全限定类名书写错误,会导致异常!ClassNotFoundException
2.2通过类对象的getClass方法
// 先获得到某个类的对象
Student student = new Student();
// 通过对象调用方法获得Class对象!
Class<? extends Student> clazz1 = student.getClass();
// getClass()方法是从Object继承过来的
2.3通过类名的静态属性class
// 通过类名.class获得Class对象!
Class<Student> clazz = Student.class;
2.4通过Class对象的常用功能
* String getSimpleName(); // 获得类名字符串:类名
* String getName(); // 获得类全名:包名+类名
* T newInstance() ; // 创建Class对象关联类的对象(公有构造方法)
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
System.out.println(clazz.getSimpleName()); // Student
System.out.println(clazz.getName()); // com.itheima.demo01_反射.Student
Object obj = clazz.newInstance(); // com.itheima.demo01_反射.Student@1647C
}
3.通过Class对象操作构造方法
3.1获得Constructor对象
Constructor<?>[] getConstructors() // 通过Class对象获得类中所有的公有的构造方法对象
Constructor<?>[] getDeclaredConstructors() // 通过Class对象获得类中所有的构造方法对象(公有私有,全都要)
Constructor<T> getConstructor(Class<?>... parameterTypes) // 通过Class对象获得类中指定公有的构造方法对象! 【★★★★★】
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) // 通过Class对象获得类中指定的构造方法!(公有私有,全都要)
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
/通过Class对象获得Student类中构造方法对应的Constructor对象!///
// 获得类中所有的公共的构造方法对应的对象数组
Constructor<?>[] constructors = clazz.getConstructors();
System.out.println(Arrays.toString(constructors)); //
// 获得类中所有的构造方法对应的对象数组(包括私有的!)
Constructor<?>[] dc = clazz.getDeclaredConstructors();
System.out.println(Arrays.toString(dc));
// 获得类中指定构造方法对应的对象
Constructor<?> c1 = clazz.getConstructor();
System.out.println(c1);
Constructor<?> c2 = clazz.getDeclaredConstructor(String.class, int.class);
System.out.println(c2);
}
3.2通过构造方法对象创建类的实例对象
//使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
T newInstance(Object... initargs)
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
// 获得Student类中指定的构造方法对象!
Constructor<?> constructor = clazz.getConstructor();
// 根据构造方法对象创建Student类的实例对象!
Object o = constructor.newInstance();
System.out.println(o); // com.itheima.demo01_反射.Student@14ae5a5
Student s = (Student) o;
}
一旦类中的构造方法使用private修饰,那么通过构造方法对象创建了的实例对象也要发生变化
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
// 获得Student类中指定的构造方法对象!
Constructor<?> constructor = clazz.getDeclaredConstructor(); // 有变动!
// 暴力访问!
constructor.setAccessible(true); // 有变动!
// 根据构造方法对象创建Student类的实例对象!
Object o = constructor.newInstance();
System.out.println(o); // com.itheima.demo01_反射.Student@14ae5a5
Student s = (Student) o;
}
4.通过Class对象操作成员方法对象
4.1获得成员方法Method对象
Method getMethod(String name, Class<?>... parameterTypes)
// 返回指定公有的方法对象! 【★★★★★】
Method[] getMethods()
// 返回所有公有的方法对象构成的数组,包括由类或接口声明的对象以及从超类和超级接口继承的类。
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
// 返回指定的方法对象!(私有的也可以玩)
Method[] getDeclaredMethods()
// 返回所有的方法对象构成的数组,包括public,protected,default(package)访问和私有方法,但不包括继承方法。
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
// 获得Student类以及父类(接口)中所有的公有成员方法
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
System.out.println("====================================");
// 获得Student类中所有的成员方法(包含私有)
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
System.out.println("====================================");
// 获得指定方法(公有无参)
Method show1 = clazz.getMethod("show1");
// 获得指定方法(私有无参)
Method show2 = clazz.getDeclaredMethod("show2");
// 获得指定方法(公有带参数)
Method method1 = clazz.getMethod("method1", String.class);
// 创建类的对象
Object obj = clazz.getConstructor().newInstance();
// 获得成员方法对象,期目的是为了执行这个成员方法
Object result = show1.invoke(obj);// 参数1:当前方法所属对象! 参数2:show1方法的参数值(若定义show1方法没有参数,这里就不写内容)
System.out.println(result); // null [show1方法定义的时候没有返回值]
System.out.println("======================");
// 执行带有参数的公有成员方法
Object o = method1.invoke(obj, "666");
System.out.println(o);
}
5.通过Class对象操作成员变量
5.1获得成员变量Field对象
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Student.class;
// 获得类的实例
Student s = (Student) clazz.newInstance();
// 获得公有成员变量对应的Field对象(很多)
Field[] fields = clazz.getFields();
System.out.println(Arrays.toString(fields));
// 获得指定公有成员变量
Field name = clazz.getField("name");
System.out.println(name);
// 获得所有的成员变量对应的Field对象(公有私有均可)
Field[] declaredFields = clazz.getDeclaredFields();
System.out.println(Arrays.toString(declaredFields));
// 获得指定成员变量(公有私有均可)
Field age = clazz.getDeclaredField("age");
System.out.println(age);
}
5.2通过Field对象获取成员变量的值或者设置值
public static void main(String[] args) throws Exception{
// 获得Student类对应的Class对象
Class<?> clazz = Student.class;
// 获得类的实例
//Object obj = clazz.newInstance();
Student s = (Student) clazz.newInstance();
//操作公有的成员变量
// 获得指定公有成员变量
Field name = clazz.getField("name");
// 为公有的成员变量设置值
System.out.println(s.getName()); // null
name.set(s,"jack");
// 获得指定公有成员变量的值
System.out.println(name.get(s)); // jack
System.out.println(s.getName()); // jack
//操作私有的成员变量
// 获得指定成员变量(公有私有均可)
Field age = clazz.getDeclaredField("age");
// 暴力访问
age.setAccessible(true);
// 获取值
System.out.println(age.get(s)); // 0
// 设置值
age.set(s,38);
System.out.println(age.get(s)); // 38
}