所谓反射就是在程序运行期间,能够动态获取到类的属性和方法,以及动态操作对象的属性和方法。
反射技术其实应用很广泛,尤其是各种框架技术都离不开反射,一些常用的 jar 包中间件(比如各个数据库厂商提供的 JDBC 访问驱动程序)也使用反射技术。之所以要总结一下反射技术,主要还是为了能够看懂相关 Java 框架相关源码,领悟其设计思想的巧妙之处,更好的学习和掌握各种框架。
首先我们先准备一个 Person 类,下面所有的代码演示,都使用该类,代码如下:
package com.jobs.reflect; public class Person { //公开字段 public String name; public int age; //私有字段 private int salary; //------------------------------ //无参构造函数 public Person() { } //全参构造函数 public Person(String name, int age, int salary) { this.name = name; this.age = age; this.salary = salary; } //私有构造函数 private Person(String name, int age) { this.name = name; this.age = age; this.salary = 100; System.out.println("私有构造函数被调用..."); } //------------------------------ //私有方法 private void method1() { System.out.println("私有方法 method1 被调用,无参无返回值"); } public String method2(String name) { System.out.println("method2 被调用,有参有返回值,参数为" + name); return "method2 返回值为:" + name + " fastoder..."; } }
为什么要获取一个类的 Class 对象?
一个类的 Class 对象,可以理解为模板。
获取到一个类的 Class 对象后,就相当于获取到了一个类的模板,然后就可以进行以下操作:
获取一个类的 Class 模板对象,有三种方式:
无论采用哪种方式获取,获取到的对象,都是同一个对象。代码演示如下:
public class reflectDemo1 { public static void main(String[] args) throws ClassNotFoundException { //第 1 种方式获取 Person 的 Class 类对象 Class cla1 = Class.forName("com.jobs.reflect.Person"); System.out.println(cla1); //第 2 种方式获取 Person 的 Class 类对象 Class cla2 = Person.class; System.out.println(cla2); //第 3 种方式获取 Person 的 Class 类对象 Person per = new Person(); Class cla3 = per.getClass(); System.out.println(cla3); //无论哪种方式获取,获取到的都是 Person 的同一个类对象 System.out.println(cla1 == cla2); //true System.out.println(cla1 == cla3); //true } }
通过 getConstructors 方法,可以获取到所有的 public 修饰的构造方法
通过 getDeclaredConstructors 方法,可以获取到所有构造方法,包括 private 修饰的构造方法
通过 getConstructor(参数类型...) 方法,可以获取到 public 修饰的具体的一个构造方法
通过 getDeclaredConstructor(参数类型...) 方法,可以获取到具体的一个构造方法,包括私有构造方法
通过构造方法对象的 newInstance(参数值...) 方法,可以创建对象实例。
需要注意:如果想使用私有的构造方法对象创建实例对象,需要调用私有构造方法对象的 setAccessible(boolean flag) 方法,并传入 true 即可。(true 表示取消安全访问限制)
具体演示代码如下:
public class reflectDemo2 { public static void main(String[] args) { try { //获取 Person 的 Class 类对象 Class cla = Class.forName("com.jobs.reflect.Person"); System.out.println("Person类的所有构造方法为:"); //获取所有的构造方法,包括私有构造方法 Constructor[] constructors = cla.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } System.out.println("--------------------------"); //获取无参构造方法,创建实例对象 Constructor constructor1 = cla.getConstructor(); Person per1 = (Person) constructor1.newInstance(); System.out.println(per1); //打印实例化对象的地址值 //获取全参构造方法,创建实例对象 Constructor constructor2 = cla.getConstructor(String.class, int.class, int.class); Person per2 = (Person) constructor2.newInstance("侯胖胖", 40, 22000); System.out.println(per2); //打印实例化对象的地址值 //获取私有构造方法,创建实例对象 Constructor constructor3 = cla.getDeclaredConstructor(String.class, int.class); //取消安全访问限制 constructor3.setAccessible(true); Person per3 = (Person) constructor3.newInstance("任肥肥", 38); System.out.println(per3); //打印实例化对象的地址值 } catch (Exception ex) { ex.printStackTrace(); } } } /* 打印出的结果如下: Person类的所有构造方法为: private com.jobs.reflect.Person(java.lang.String,int) public com.jobs.reflect.Person(java.lang.String,int,int) public com.jobs.reflect.Person() -------------------------- com.jobs.reflect.Person@12edcd21 com.jobs.reflect.Person@34c45dca 私有构造函数被调用... com.jobs.reflect.Person@52cc8049 */
通过 getFields() 方法,可以获取到所有的 public 修饰的字段
通过 getDeclaredFields() 方法,可以获取到所有字段,包括私有字段
通过 getField(String name) 方法,可以获取到 public 修饰的具体一个字段
通过 getDeclaredField(String name) 方法,可以获取到具体一个字段,包括私有字段
通过 set(Object obj, Object value) 方法,给字段赋值,第一个参数是具体的实例化对象
通过 get(Object obj) 方法,获取具体一个字段的值,该参数是具体的实例化对象
需要注意:如果想要获取私有字段的值,以及设置私有字段的值,需要调用私有字段对象的 setAccessible(boolean flag) 方法,并传入 true 即可。(true 表示取消安全访问限制)
具体演示代码如下:
public class reflectDemo3 { public static void main(String[] args) { try { //获取 Person 的 Class 类对象 Class cla = Class.forName("com.jobs.reflect.Person"); System.out.println("Person类的所有字段为:"); Field[] fields = cla.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } System.out.println("--------------------------"); //获取无参构造方法,创建实例对象 Constructor cst = cla.getConstructor(); Person per = (Person) cst.newInstance(); //给 name 赋值 Field fieldName = cla.getField("name"); fieldName.set(per, "乔豆豆"); //给 age 赋值 Field fieldAge = cla.getField("age"); fieldAge.set(per, 38); //给私有字段 salary 复制 Field fieldSalary = cla.getDeclaredField("salary"); //取消安全访问限制 fieldSalary.setAccessible(true); fieldSalary.set(per, 28000); //获取相应字段的值 System.out.println("Person实例化对象的字段值为:"); System.out.println(fieldName.getName() + " ---> " + fieldName.get(per)); System.out.println(fieldAge.getName() + " ---> " + fieldAge.get(per)); System.out.println(fieldSalary.getName() + " ---> " + fieldSalary.get(per)); } catch (Exception ex) { ex.printStackTrace(); } } } /* 打印出的结果如下: Person类的所有字段为: public java.lang.String com.jobs.reflect.Person.name public int com.jobs.reflect.Person.age private int com.jobs.reflect.Person.salary -------------------------- Person实例化对象的字段值为: name ---> 乔豆豆 age ---> 38 salary ---> 28000 */
通过 getMethods() 方法,可以获取到所有 public 修饰的方法,包括继承的方法
通过 getDeclaredMethods() 方法,可以获取到所有方法,但是不包括继承的方法
通过 getMethod(方法名, 参数类型...) 方法,可以获取到具体一个 public 修饰的方法
通过 getDeclaredMethod(方法名, 参数类型...) 方法,可以获取到具体一个方法,包括私有方法
通过 invoke(Object obj, 参数值...) 方法,可以调用具体一个方法,第一个参数是具体的实例化对象
需要注意:如果想要调用私有方法,需要调用私有方法对象的 setAccessible(boolean flag) 方法,并传入 true 即可。(true 表示取消安全访问限制)
具体演示代码如下:
public class reflectDemo4 { public static void main(String[] args) { try { //获取 Person 的 Class 类对象 Class cla = Class.forName("com.jobs.reflect.Person"); System.out.println("Person类的所有方法为:"); Method[] methods = cla.getDeclaredMethods(); for (Method method : methods) { System.out.println(method); } System.out.println("--------------------------"); //获取无参构造方法,创建实例对象 Constructor cst = cla.getConstructor(); Person per = (Person) cst.newInstance(); //调用私有方法,私有方法无参数 Method method1 = cla.getDeclaredMethod("method1"); //取消私有方法的安全访问限制 method1.setAccessible(true); method1.invoke(per); //调用有参数,有返回值的共有方法 Method method2 = cla.getMethod("method2", String.class); String result = (String) method2.invoke(per, "任天蓬"); System.out.println(result); } catch (Exception ex) { ex.printStackTrace(); } } } /* 打印出的结果如下: Person类的所有方法为: public java.lang.String com.jobs.reflect.Person.method2(java.lang.String) private void com.jobs.reflect.Person.method1() -------------------------- 私有方法 method1 被调用,无参无返回值 method2 被调用,有参有返回值,参数为任天蓬 method2 返回值为:任天蓬 fastoder... */
到此为止,有关 Java 的一些简单的反射技术,已经介绍完毕,以上代码都经过测试无误,希望能够对大家有所帮助。