反射机制
9.1 反射概述
在日常生活中,反射是一种物理现象。例如,通过照镜子可以反射出自己的容貌,水面可以反射出景物,等等,这些都是反射。通过反射,可以将一个虚像映射到实物,这样就可以获取实物的某些形态特征。
Java程序中也有反射,Java程序中的反射也是同样的道理,常规情况下程序通过类创建对象,反射就是将这一过程反转,通过实例化对象来获取所属类的信息。
Java的反射机制可以动态获取程序信息以及动态调用对象的功能,它主要有以下4个作用:
(1)在程序运行状态中,构造任意一个类的对象。
(2)在程序运行状态中,获取任意一个对象所属的类的信息。
(3)在程序运行状态中,调用任意一个类的成员变量和方法。
(4)在程序运行状态中,获取任意一个对象的属性和方法。
反射机制的优点是可以实现动态创建对象和动态编译,特别是在JavaEE的开发中,反射的灵活性表现得十分明显。例如,一个大型软件,不可能一次就把程序设计得很完美,当这个程序编译、发布上线后,需要更新某些功能时,如果采用静态编译,就需要把整个程序重新编译一次,同时也需要用户把以前的软件卸载,再重新安装新的版本。而采用反射机制,程序可以在运行时动态地创建和编译对象,不需要用户重新安装软件,即可实现功能的更新。
9.2 认识Class类
Class类的常用方法
方法声明 | 功能描述 |
---|---|
forName(String className) | 获取与给定字符串名称的类或接口相关联的Class对象 |
getConstructors() | 获取类中所有public修饰的构造方法对象 |
getDeclaredFields() | 获取所有成员变量对应的字段类对象,包括public、protected、default和private修饰的字段,但不包括从父类继承的字段 |
getFields() | 获取所有public修饰的成员变量对应的字段类对象,包括从父类继承的字段 |
getMethods() | 获取所有public修饰的成员方法对应的方法类对象,包括从父类继承的方法 |
getMethod(String name,Class…parameter Type) | 根据方法名和参数类型获得对应的方法类对象,并且只能获得public修饰的方法类对象 |
getInterfaces() | 获取当前类实现的全部接口 |
getClass() | 获取调用该方法的Class对象 |
getName() | 获取类的完整名称,名称中包含包的名称 |
getPackage() | 获取类所属的包名称 |
getSuperclass() | 获取类的父类 |
newInstance() | 创建Class对象关联类的对象 |
getComponentType() | 获取数组的对应Class对象 |
isArray() | 判断此Class对象是否是一个数组 |
因为Class类本身并没有定义任何构造方法,所以Class类不能直接使用构造方法进行对象的实例化,使用Class类进行对象的实例化可以使用以下3种方式:
(1)根据全限定类名获取:Class.forName(“全限定类名”)。
(2)根据对象获取:对象名.getClass()。
(3)根据类名获取:类名.class。
下面通过一个案例演示Class类的3种实例化方式
class A{
}
class Example01 {
public static void main(String[] args){
Class<?> c1 = null; //声明Class对象c1
Class<?> c2 = null; //声明Class对象c2
Class<?> c3 = null; //声明Class对象c3
try{
c1 = Class.forName("com.test.A");//通过第(1)种方式实例化c1对象
}catch(ClassNotFoundException e){
e.printStackTrace();
}
c2 = new A().getClass(); //通过第(2)种方式实例化c2对象
c3 = A.class; //通过第(3)种方式实例化c3对象
System.out.println("类名称:"+c1.getName());
System.out.println("类名称:"+c2.getName());
System.out.println("类名称:"+c3.getName());
}
}
9.3 Class类的使用
9.3.1 通过无参构造方法实例化对象
如果想通过Class类实例化其他类的对象,则可以调用newInstance()方法,在调用newInstance()方法实例化其他类的对象时,必须保证被实例化的类中存在无参构造方法。下面通过一个案例演示Class类通过无参构造方法实例化对象
class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
class Example02 {
public static void main(String[] args) {
Class<?> c = null;
try {
c = Class.forName("com.test.Person"); //调用forName()方法实例化c
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Person person = null; //声明Person类对象person
try {
person = (Person) c.newInstance(); //
} catch (Exception e) {
e.printStackTrace();
}
person.setName("张三");
person.setAge(20);
System.out.println(person);
}
}
然而,newInstance()
方法已经被弃用
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
class Example02 {
public static void main(String[] args) {
try {
// 获取Person类的Class对象
Class<?> c = Class.forName("com.test.Person");
// 获取无参构造函数
Constructor<?> constructor = c.getDeclaredConstructor();
// 确保构造函数可访问
constructor.setAccessible(true);
// 使用构造函数创建Person类的实例
Person person = (Person) constructor.newInstance();
// 设置属性并打印对象
person.setName("张三");
person.setAge(20);
System.out.println(person);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
9.3.2 通过有参构造方法实例化对象
如果类中没有无参构造方法,则可以通过有参构造方法实例化对象。通过有参构造方法实例化对象时,需要明确调用的构造方法,并传递相应的参数。通过有参构造方法实例化对象的操作步骤如下:
(1)通过调用Class类中的getConstructors()方法获取要实例化的类中的全部构造方法。
(2)获取实例化使用的有参构造方法对应的Constructor对象。
(3)通过Constructor类实例化对象。
方法声明 | 功能描述 |
---|---|
getModifiers() | 获取构造方法的修饰符 |
getName() | 获取构造方法的名称 |
getParameterTypes() | 获取构造方法中参数的类型 |
toString() | 返回此构造方法的信息 |
newInstance(Object…initargs) | 通过该构造方法的指定参数列表创建一个该类的对象,如果未设置参数则表示采用默认无参的构造方法。Object…initargs代表Object类型的可变参数 |
import java.lang.reflect.Constructor;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// getters and setters...
@Override
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
class Example01 {
public static void main(String[] args) {
Class<?> c = null; // 声明Class对象c
try {
c = Class.forName("com.test.Person"); // 实例化对象c
} catch (ClassNotFoundException e) {
e.printStackTrace();
return; // 如果找不到类,则退出程序
}
Constructor<?> cons[] = null; // 声明Constructor类对象数组cons
try {
cons = c.getDeclaredConstructors(); // 使用getDeclaredConstructors来获取所有构造方法,包括非公共的
// 如果需要公共构造方法,使用c.getConstructors();
} catch (Exception e) {
e.printStackTrace();
return; // 如果找不到构造方法,则退出程序
}
Person person1 = null;
Person person2 = null;
try {
// 假设Person类有一个接受String和int参数的构造方法
Constructor<?> constructor = cons[0]; // 获取第一个构造方法,这里假设它是正确的构造方法
constructor.setAccessible(true); // 如果构造方法不是public的,设置为可访问
person1 = (Person) constructor.newInstance("张三", 30); // 实例化第一个Person对象
// 通常情况下,你不会同时有两个接受相同参数的构造方法,因此这里仅创建一个人
// 如果Person类确实有多个构造方法,并且你想使用另一个,你需要确保选择正确的构造方法
} catch (Exception e) {
e.printStackTrace();
}
// 如果需要创建第二个对象,确保使用不同的变量,并且确保构造方法可用
System.out.println(person1); // 打印第一个人的信息
// System.out.println(person2); // 如果创建了第二个人,可以打印第二个人的信息
}
}
对象数组cons中存储了Person类的全部构造方法,因为在Person类中只有一个构造方法,所以对象数组cons中其实只有一个构造方法,cons[0]表示Person类的第一个构造方法。\
9.4 通过反射获取类结构
在实际开发中,通过反射可以得到一个类的完整结构,包括类的构造方法、类的属性和类的方法。通过反射获取类结构需要使用java.lang.reflect包中的以下3个类:
(1)Constructor:用于获取类中的构造方法。
(2)Field:用于获取类中的属性
(3)Method:用于获取类中的方法Constructor类、Field类和Method类都是AccessibleObject类的子类,AccessibleObject类的继承关系如图所示
9.4.1 获取类实现的全部接口
要获取一个类实现的全部接口,可以调用Class类中的getInterfaces()方法getInterfaces()方法声明格式如下:
public Class[] getInterfaces();
getInterfaces()方法返回一个Class类的对象数组,其中存储的是类实现的接口。使用对象数组中的元素(接口)调用Class类中的getName()方法可以获取接口的名称。下面通过一个案例讲解如何通过getInterfaces()方法获取一个类所实现的全部接口
interface China{
public static final String NATION = "China";
public static final String AUTHOR = "张三";
}
class Person implements China{
private String name;
private int age;
public Person(String name, int age){
this.setName(name);
this.setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class Example01 {
public static void main(String[] args) {
Class<?> c=null;
try{
c=Class.forName("com.test.Person");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
Class<?> cons[]=c.getInterfaces();
for(int i=0; i<cons.length; i++){
System.out.println("实现的接口名称:" + cons[i].getName());
}
}
}
第1~4行代码定义了China接口,并在China接口中定义了两个常量—NATION和AUTHOR。第5~27行代码定义了Person类并实现了China接口。因为一个类可能实现了多个接口,所以第36~39行代码以Class数组的形式将全部接口对象返回,并利用循环的方式将数组的内容依次输出。
9.4.2 获取父类
如果要获取一个类的父类,可以调用Class类中的getSuperclass()方法。getSuperclass()方法声明格式如下:
public Class<? Super T> getSuperclass();
getSuperclass()方法返回一个Class类的实例,通过该实例调用Class类中的getName()方法可以获取类的名称
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.setName(name);
this.setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class Example01 {
public static void main(String[] args) {
Class<?> c1 = null; //声明Class类的对象
try {
c1 = Class.forName("com.test.Person"); //实例化Class类的对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class<?> c2 = c1.getSuperclass(); //获取Person类的父类
System.out.println("父类名称:" + c2.getName());
}
}
第1~23行代码定义了Person类,在其中定义了属性和方法。第26行代码声明了Class对象c1。第28行代码将Person类的全限定类名作为参数传人forName()方法中,通过调用forName()方法进行Class对象的实例化,并将实例化后的值返回给Class对象c1。第32行代码通过cl对象调用getSuperclass()方法获取Person类的父类,并赋值给Class类的对象c2。第33行代码通过c2对象调用getName()方法获取Psrson类的父类名称并输出。
由图可知,Person类在编写时没有显式地继承一个父类,所以会默认继承Object类。
9.4.3 获取全部构造方法
类的构造方法的获取在9.3.2节已经讲解,获取类的构造方法需要调用Class类的getConstructors()方法。调用getConstructors()方法获取的构造方法需要存储到Constructor类的数组中。通过调用Constructor类的方法可以获取构造方法的详细信息,如构造方法的权限、名称、参数信息等。下面通过一个案例讲解如何使用Constructor类的常用方法获取一个类中的全部构造方法。
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
class Person {
private String name;
private int age;
public Person() {
} //无参构造方法
public Person(String name) { //有一个参数的构造方法
this.name = name;
}
public Person(String name, int age) { //有两个参数的构造方法
this.setName(name);
this.setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class Example01 {
public static void main(String[] args) {
Class<?> c1 = null;
try {
c1 = Class.forName("com.test.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//获取全部构造方法,存储到Constructors类数组con中
Constructor<?> con[] = c1.getConstructors();
for (int i = 0; i < con.length; i++) { //循环打印构造方法信息
//获取构造方法详细信息并输出
Class<?> p[] = con[i].getParameterTypes();
System.out.print("构造方法:");
System.out.print(con[i].getModifiers()+" "); //获取构造方法权限
int mo = con[i].getModifiers();
System.out.print(Modifier.toString(mo) + " ");
System.out.print(con[i].getName() + " "); //获取构造方法名称
System.out.print(" (");
for (int j = 0; j < p.length; j++) {
//打印构造方法参数信息
System.out.print(p[j].getName() + " arg" + i);
if (j < p.length - 1){
System.out.print(",");
}
}
System.out.println("){}");
}
}
}
第2~28行代码定义了Person类,在类中定义了3个构造方法和其他成员。第31行代码声明了一个Class对象c1。第33行代码将Person类的全限定类名作为参数传人forName()方法中,通过调用forName()方法进行Class对象的实例化,并将实例化后的值返回给Class对象c1。第38行代码调用Class类的getConstructors()方法获取Person类的全部构造方法,并将获取的构造方法存储到Constructor类的数组con中。第39~54行代码遍历con数组,并调用Constructor类的方法获取构造方法的详细信息。
9.4.4 获取全部方法
要获取类中的所有public修饰的成员方法对象,可以使用Class类中的getMethods()方法,该方法返回一个Method类的对象数组。如果想要进一步获取方法的具体信息,如方法的参数、抛出的异常声明等,可以调用Method类提供的一系列方法。Method类的常用方法如表所示。
方法声明 | 功能描述 |
---|---|
getModifiers() | 获取方法的权限修饰符 |
getName() | 获取方法的名称 |
getParameterTypes() | 获取方法的全部参数的类型 |
getReturnType() | 获取方法的返回值类型 |
getExceptionType() | 获取方法抛出的全部异常类型 |
newInstance(Object…initargs) | 通过反射调用类中的方法 |
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.setName(name);
this.setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class Example02 {
public static void main(String[] args) {
Class<?> c = null;
try {
c = Class.forName("com.test.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//获取全部方法,存储到Method类数组对象 m 中
Method m[] = c.getMethods();
for (int i = 0; i < m.length; i++) { //遍历数组m循环输出方法信息
Class<?> r = m[i].getReturnType(); //获取方法的返回值类型
Class<?> p[] = m[i].getParameterTypes(); //获取全部的参数类型
int xx = m[i].getModifiers(); //获取方法的权限修饰符
System.out.print(Modifier.toString(xx) + " "); //还原修饰符
System.out.print(r.getName() + " "); //获取方法名称
System.out.print(m[i].getName());
System.out.print("(");
for(int x=0; x< p.length;x++){
System.out.print(p[x].getName() + " "+"args"+" ");
if(x<p.length-1){
System.out.print(",");
}
}
//获取方法抛出的全部异常
Class<?> ex[] =m[i].getExceptionTypes();
if(ex.length>0){
System.out.print(") throws ");
}else{
System.out.print(")");
}
for(int j=0;i<ex.length;j++){
System.out.print(ex[j].getName()); //输出异常信息
if(j<ex.length-1){
System.out.print(",");
}
}
System.out.println();
}
}
}
第3~25行代码定义了Person类,并在Person类中声明了name和age属性以及其getter和setter方法。第28~33行代码声明了Class对象c并在try…catch语句中对其进行了实例化。第35行代码通过Class对象c调用getMethods()方法获取了Person类的所有方法,并将获取的所有方法存储到Method类的数组m当中。第36~64行代码输出Person类中的所有方法信息,包括方法的返回值类型、权限参数类型、修饰符、方法名称、参数、抛出的全部异常等。
9.4.5 获取全部属性
通过反射也可以获取一个类中的全部属性,类中的属性包括两种:从父类继承的属性和本类定义的属性。针对这两种属性,Java提供了两种获取方式,分别如下:
(1)获取本类中,以及实现的接口或继承的父类中的公共属性,需要调用getFields()方法。getFields()方法声明格式如下:
public Field[] getFields() throws SecurityException;
(2)获取本类中的全部属性,需要调用getDeclaredFields()方法。getDeclareFields()方法声明格式如下:
public Field[] getDeclaredFields throws SecurityException;
上述两个方法返回的都是Field数组,Field数组中的每一个Field对象表示类中的一个属性。如果要获取属性的详细信息,就需要调用Field类提供的一系列方法。Field类的常用方法如表所示。
方法声明 | 功能描述 |
---|---|
getModifiers() | 获取属性的权限修饰符 |
getName() | 获取属性的名称 |
isAccessible() | 判断属性是否可被外部访问 |
setAccessible(Boolean flag) | 设置属性是否可被外部访问 |
toString() | 返回Field类的信息 |
get(Object obj) | 获取指定对象中属性的具体内容 |
set(Object obj,Object value) | 设置指定对象中属性的具体内容 |
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.setName(name);
this.setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class Example02 {
public static void main(String[] args) {
Class<?> c1 = null;
try {
c1 = Class.forName("com.test.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
{
//获取本类属性,存储到Field类数组f当中
Field f[] = c1.getDeclaredFields();
for (int i = 0; i < f.length; i++) { //循环输出属性信息
Class<?> r = f[i].getType(); //获取属性的类型
int mo = f[i].getModifiers(); //获取属性权限修饰符
String priv = Modifier.toString(mo); //转换属性的权限修饰符
System.out.print("本类属性:");
System.out.print(priv + " "); //输出属性权限修饰符
System.out.print(r.getName() + " "); //输出属性类型
System.out.print(f[i].getName()); //输出属性名称
System.out.println(";");
}
}
}
}
第3~25行代码定义了Person类,并在Person类中定义了name和age属性以及相应的getter和setter方法。第30行代码实例化了Class对象c1。第36行代码通过调用Class类的getDeclaredFields()方法获取Person类的所有属性,并存人Field数组f中。第37~46行代码通过for循环输出Field数组中Person类的属性信息。
9.4.6 通过反射调用类中的方法
通过反射调用类中的方法时,需要使用Method类完成,具体操作步骤如下:
(1)通过调用Class类的getMethod()方法获取一个Method类的对象。调用getMethod()方法时需要传人方法名称作为参数。
(2)通过获取的Method对象调用invoke()方法,执行目标方法。调用invoke()方法时,需要传递Class对象的实例作为参数。
import java.lang.reflect.Method;
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.setName(name);
this.setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void sayHello() {
System.out.println("Hello World!");
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class Example02 {
public static void main(String[] args) {
Class<?> c = null;
try {
c = Class.forName("com.test.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
Method met = c.getMethod("sayHello");
met.invoke(c.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
第2~31行代码定义了Person类,并声明了Person类的属性以及方法。第34行代码声明了Class类的对象c。第36行代码将Person类的全限定类名作为参数传人forName()方法中,通过forName()方法进行Class类的对象的实例化,并将实例化后的值返回给Class类的对象c。第41行代码通过Class类的getMethod()方法根据类中的方法名称sayHello获取Method对象met,对象met中保存的是sayHello()方法的信息。第42行代码通过对象met调用invoke()方法执行sayHello()方法。需要注意的是,在调用invoke()方法时,必须传人一个类的实例化对象作为参数。
9.5 反射的应用
9.5.1 通过反射调用类中的getter/setter方法
在第3章读者就了解到类中的属性必须封装,封装之后的属性要通过setter方法设置属性值,通过getter方法获取属性值。在使用反射调用类中方法的操作中,最重要的是调用类中的getter/setter方法。getter/setter方法的调用过程与9.4.6节中类方法的调用过程类似,但因为getter/setter方法需要访问属性,所以稍显复杂。下面通过一个案例讲解如何使用反射调用类中的getter/setter方法。
import java.lang.reflect.Method;
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.setName(name);
this.setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
public class Example02 {
public static void main(String[] args){
Class<?> c = null;
Object obj = null;
try{
c = Class.forName("com.test.Person"); //对象c为Class类型
}catch(ClassNotFoundException e){
e.printStackTrace();
}
try{
obj = c.newInstance(); //实例化Class对象
}catch(InstantiationException e){
e.printStackTrace();
}catch(IllegalAccessException e){
e.printStackTrace();
}
setter(obj,"name","张三",String.class);
setter(obj,"age",18,int.class);
System.out.print("姓名:");
getter(obj,"name");
System.out.print("年龄:");
getter(obj,"age");
}
public static void setter(Object obj,String att,Object value,Class<?>
type){
try {
Method met= obj.getClass().getMethod("set"+initStr(att),type);
met.invoke(obj,value);
}catch(Exception e){
e.printStackTrace();
}
}
public static void getter(Object obj,String att){
try {
Method met= obj.getClass().getMethod("get"+initStr(att));
System.out.println(met.invoke(obj));
}catch(Exception e){
e.printStackTrace();
}
}
public static String initStr(String old){
String str= old.substring(0,1).toUpperCase()+old.substring(1);
return str;
}
}
(1)设置方法名称。第45、46行代码在设置方法名称时直接使用了属性名称,例如name和age。但实际需要的方法名称是setName()、getName()、setAge()、getAge(),所有属性名称的首字母必须大写。为了解决这个问题,程序中单独定义了initStr()方法,通过此方法将字符串中的首字母转换成大写,随后将字符串连接到set字符串及get字符串之后,找到对应的方法。
(2)调用setter()方法时,传人了实例化对象、要操作的属性名称、要设置的参数内容以及具体的参数类型,这样做是为了满足getMethod()方法和invoke()方法的调用要求。
(3)在调用getter()方法时,同样传人了一个实例化对象,因为其getter方法本身不需要接收任何参数,所以只传人了属性名称。对文件中的程序,读者只需要了解基本操作原理即可。在实际的开发中,很多框架会为开发者预告实现以上功能。
9.5.2 通过反射操作属性
在反射操作中,虽然可以通过调用类中的getter/setter方法访问类的属性,但是这样操作起来太烦琐。除了调用getter/setter方法访问类的属性之外,反射机制也可以直接通过Field类操作类中的属性,通过Field类提供的set()方法和get()方法可以直接设置、获取类的属性值。通过调用Field类的getter/setter方法访问类的属性时,首先要调用Field类中的setAccessible()方法将需要操作的属性权限设置成可以被外部访问。
import java.lang.reflect.Field;
class Person {
private String name;
private int age;
public Person(){}
public Person(String name){
this.name = name;
}
public Person(String name,int age){
this.setName(name);
this.setAge(age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void sayHello(){
System.out.println("Hello World!");
}
public String toString() {
return "姓名:"+this.name+",年龄:"+this.age;
}
}
public class Example02 {
public static void main(String[] args) throws Exception{
Class<?> c = null; //声明一个Class对象
Object obj = null; //声明一个Object对象
c = Class.forName("com.test.Person"); //实例化Class对象
obj = c.newInstance(); //实例化Object对象
Field nameField = null; //表示name属性
Field ageField = null; //表示age属性
nameField = c.getDeclaredField("name"); //获取name属性
ageField = c.getDeclaredField("age"); //获取age属性
nameField.setAccessible(true); //将name属性设置为可被外部访问
nameField.set(obj,"张三"); //设置name属性值
ageField.setAccessible(true); //将age属性设置为可被外部访问
ageField.set(obj,30); //设置age属性值
System.out.println("姓名:"+nameField.get(obj));
System.out.println("年龄:"+ageField.get(obj));
}
}
第2~31行代码定义了Person类,并声明了Person类的属性以及方法。第34、35行代码声明了Class类的对象c和Object类的对象obj。第36行代码将Person类的全限定类名作为参数传人forName()方法中,调用forName()方法完成Class类的对象的实例化,并将实例化后的值返回给Class类的对象c。第37行代码实例化Object类的对象obj。第40、41行代码通过调用Class类的getDeclaredField()方法获取Person类的name属性和age属性。第42~45行代码将name属性和age属性设置为可被外部访问,并设置name属性和age属性的值。需要注意的是,以上程序在扩大类属性的访问权限后直接进行了属性的操作,所以在Person类中并不需要编写getter方法和setter方法,但是在实际开发中,这种直接操作属性的方式是不安全的,读者在以后的开发中,不要直接操作属性,而要通过getter方法和setter方法操作属性。文件只是演示通过反射可以直接访问类中的属性。
本章内容如下:反射机制原理;
Class类;Class类的使用,包括通过无参构造方法实例化对象和通过有参构造方法实例化对象;
通过反射获取类结构,包括获取接口、获取父类、获取构造方法、获取全部方法、获取全部属性和通过反射调用类中的方法;
反射的应用,包括通过反射调用类中的getter和setter方法和通过反射操作类中的属性。