所谓反射就是在程序运行期间,能够动态获取到类的属性和方法,以及动态操作对象的属性和方法。
反射技术其实应用很广泛,尤其是各种框架技术都离不开反射,一些常用的 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 对象后,就相当于获取到了一个类的模板,然后就可以进行以下操作:
- 获取 Class 对象(模板)中的任意属性和方法。即使是私有属性和方法,也能够获取到。
- 通过 Class 对象(模板)创建对象实例,参照模板中的属性和方法,可以为实例对象的属性赋值,以及调用其方法
获取一个类的 Class 模板对象,有三种方式:
- 从文件中获取,调用 Class.forName(全类名) 方法
- 在内存中获取,可以调用一个类的 Class 属性
- 对于一个实例化的对象,可以调用其 getClass() 方法
无论采用哪种方式获取,获取到的对象,都是同一个对象。代码演示如下:
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 的一些简单的反射技术,已经介绍完毕,以上代码都经过测试无误,希望能够对大家有所帮助。