先看一下我们主要用到的方法
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
API文档解释:
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。name 参数是一个 String,它指定所需字段的简称。注意,此方法不反映数组类的 length 字段。
这里的Filed是一个类,要获取类属性和方法就要用到Field类中的方法
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
有了这些我们就可以开始了
首先我们创建一个简单的Person类:
public class Person {
public String name;
protected String gender;
int height;
private int age;
public Person() {
}
@Override
public String toString() {
return "Person "+"["+"name="+this.name+","+"age="+this.age+"]";
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height=height;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender=gender;
}
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;
}
}
此处注意到我们给这里的四个属性分别设置了四种不同的访问权限。
下面开始:
先试试这两个方法的输出结果:getDeclaredFields()和 getFields()
public static void main(String[] args) throws ClassNotFoundException {
Class c = Class.forName("RunTimeTest.Person");
Field[] declaredFields=c.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("=========================");
Field[] fields=c.getFields();
for (Field field : fields) {
System.out.println(field);
}
}
我们来看看API怎么说:
getFields() : 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
getDeclaredFields() :返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
看完我们就明白了,加了Declare的方法可以访问所有属性(方法),没有加Declare的方法只能访问公共字段。
下面试试设置public型变量:
public static void main(String[] args) throws ClassNotFoundException {
Class c = Class.forName("RunTimeTest.Person");
Person p = new Person();
System.out.println(p);
try {
Field name=c.getField("name");
name.set(p,"王五");
System.out.println(p);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
可以看到我们通过set()成功把公有属性的name设置成了”王五“
在试试访问保护成员
public static void main(String[] args) throws ClassNotFoundException {
Class c = Class.forName("RunTimeTest.Person");
Person p = new Person();
try {
Field gender=c.getField("gender");
// Field gender=c.getDeclaredField("gender");
System.out.println(gender);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
可以看到,这里如果用getField(“gender”)来获取gender属性,在打印此属性的时候则会报错,提示:NoSuchFieldException
因为前面也说了getField();方法只能获取到公有的属性或方法。
在使用了getDeclaredField(“gender”);之后就能成功打印了
统一设置一波:
public static void main(String[] args) throws ClassNotFoundException {
Class c = Class.forName("RunTimeTest.Person");
Person p = new Person();
System.out.println(p);
try {
Field name=c.getField("name");
Field gender=c.getDeclaredField("gender");
Field height=c.getDeclaredField("height");
Field age=c.getDeclaredField("age");
name.set(p,"王五");
gender.set(p,"男");
height.set(p,180);
// age.set(p,66);
System.out.println(p);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
这里我们又注意到,我age怎么不设置,why?
aee~ 因为age是私有变量嘛,私有变量哪有这么容易被访问,还想修改。。。 在这里对于私有属性或方法,我们需要多进行一个步骤即可访问/设置,那就是setAccessible(boolean flag)
看看API的解释:
public void setAccessible(boolean flag) throws SecurityException
将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
如果不设置,可以看看报错信息:
cannot access a member of class RunTimeTest.Person with modifiers "private"
翻译一下就是:无法访问带有修饰符“ private”的RunTimeTest.Person类的成员
所有只要在上面添加一句:age.setAccessible(true); 就可以成功访问啦
调用方法和属性差不多,只不过在调用的时候是用invoke(),而不是用set()。
如下:
try {
Method setName=c.getMethod("setName", String.class);
setName.invoke(p,"李四");
System.out.println(p);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
同样,如果方法是私有的话也要使用setAccessible(boolean flag);
flag为 true,并且调用的方法必须是getDeclaredMethod(String name, Class<?>... parameterTypes)
,不能使用getMethod(String name, Class<?>... parameterTypes)
不然也会报错:NoSuchMethodException
总结:
1、getField()方法只能访问公有 属性/方法,getDeclaredField()方法可以访问所有 属性/方法
2、设置属性用 set(),设置方法用 invoke()
3、访问私有属性/方法时要使用带有Declare的方法,并且将setAccessible(boolean flag)的flag设为true
PS:文章中多次提到API文档,这里附上一份链接供大家下载参考:
链接:https://pan.baidu.com/s/18TspjA37qM9qKMKqWQwB2w
提取码:p3hz
大家还可以看看这篇文章JAVA中的反射机制