mSonAge = age;
}
private int getSonAge(){
return mSonAge;
}
private String getSonName(){
return mSonName;
}
}
1\. 获取类的所有变量信息
--------------
/**
- 通过反射获取类的所有变量
*/
private static void printFields(){
//1.获取并输出类的名称
Class mClass = SonClass.class;
System.out.println("类的名称:" + mClass.getName());
//2.1 获取所有 public 访问权限的变量
// 包括本类声明的和从父类继承的
Field[] fields = mClass.getFields();
//2.2 获取所有本类声明的变量(不问访问权限)
//Field[] fields = mClass.getDeclaredFields();
//3. 遍历变量并输出变量信息
for (Field field :
fields) {
//获取访问权限并输出
int modifiers = field.getModifiers();
System.out.print(Modifier.toString(modifiers) + " ");
//输出变量的类型及变量名
System.out.println(field.getType().getName()
+ " " + field.getName());
}
}
以上代码注释很详细,就不再解释了。需要注意的是注释中 2.1 的 `getFields()` 与 2.2的 `getDeclaredFields()` 之间的区别,下面分别看一下两种情况下的输出。看之前强调一下:
`SonClass` extends `FatherClass` extends `Object` :
* 调用 `getFields()` 方法,输出 `SonClass` 类以及其所继承的父类( 包括 `FatherClass` 和 `Object`) 的 `public` 方法。注:`Object` 类中没有成员变量,所以没有输出。
```
类的名称:obj.SonClass
public java.lang.String mSonBirthday
public java.lang.String mFatherName
public int mFatherAge
```
* 调用 `getDeclaredFields()` , 输出 `SonClass` 类的所有成员变量,不问访问权限。
```
类的名称:obj.SonClass
private java.lang.String mSonName
protected int mSonAge
public java.lang.String mSonBirthday
```
2\. 获取类的所有方法信息
--------------
/**
- 通过反射获取类的所有方法
*/
private static void printMethods(){
//1.获取并输出类的名称
Class mClass = SonClass.class;
System.out.println("类的名称:" + mClass.getName());
//2.1 获取所有 public 访问权限的方法
//包括自己声明和从父类继承的
Method[] mMethods = mClass.getMethods();
//2.2 获取所有本类的的方法(不问访问权限)
//Method[] mMethods = mClass.getDeclaredMethods();
//3.遍历所有方法
for (Method method :
mMethods) {
//获取并输出方法的访问权限(Modifiers:修饰符)
int modifiers = method.getModifiers();
System.out.print(Modifier.toString(modifiers) + " ");
//获取并输出方法的返回值类型
Class returnType = method.getReturnType();
System.out.print(returnType.getName() + " "
+ method.getName() + "( ");
//获取并输出方法的所有参数
Parameter[] parameters = method.getParameters();
for (Parameter parameter:
parameters) {
System.out.print(parameter.getType().getName()
+ " " + parameter.getName() + ",");
}
//获取并输出方法抛出的异常
Class[] exceptionTypes = method.getExceptionTypes();
if (exceptionTypes.length == 0){
System.out.println(" )");
}
else {
for (Class c : exceptionTypes) {
System.out.println(" ) throws "
+ c.getName());
}
}
}
}
同获取变量信息一样,需要注意注释中 2.1 与 2.2 的区别,下面看一下打印输出:
* 调用 `getMethods()` 方法
获取 `SonClass` 类所有 `public` 访问权限的方法,包括从父类继承的。打印信息中,`printSonMsg()` 方法来自 `SonClass` 类, `printFatherMsg()` 来自 `FatherClass` 类,其余方法来自 Object 类。
```
类的名称:obj.SonClass
public void printSonMsg( )
public void printFatherMsg( )
public final void wait( ) throws java.lang.InterruptedException
public final void wait( long arg0,int arg1, ) throws java.lang.InterruptedException
public final native void wait( long arg0, ) throws java.lang.InterruptedException
public boolean equals( java.lang.Object arg0, )
public java.lang.String toString( )
public native int hashCode( )
public final native java.lang.Class getClass( )
public final native void notify( )
public final native void notifyAll( )
```
* 调用 `getDeclaredMethods()` 方法
打印信息中,输出的都是 `SonClass` 类的方法,不问访问权限。
```
类的名称:obj.SonClass
private int getSonAge( )
private void setSonAge( int arg0, )
public void printSonMsg( )
private void setSonName( java.lang.String arg0, )
private java.lang.String getSonName( )
```
三、访问或操作类的私有变量和方法
================
在上面,我们成功获取了类的变量和方法信息,验证了在运行时 **动态的获取信息** 的观点。那么,仅仅是获取信息吗?我们接着往后看。
都知道,对象是无法访问或操作类的私有变量和方法的,但是,通过反射,我们就可以做到。没错,反射可以做到!下面,让我们一起探讨如何利用反射访问 **类对象的私有方法** 以及修改 **私有变量或常量**。
老规矩,先上测试类。
注:
1. 请注意看测试类中变量和方法的修饰符(访问权限);
2. 测试类仅供测试,不提倡实际开发时这么写 : )
**TestClass.java**
![](https://img-blog.csdnimg.cn/20181109194923645.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNzAxOTU2,size_16,color_FFFFFF,t_70)
3.1 访问私有方法
----------
以访问 `TestClass` 类中的私有方法 `privateMethod(...)` 为例,方法加参数是为了考虑最全的情况,很贴心有木有?先贴代码,看注释,最后我会重点解释部分代码。
/**
-
访问对象的私有方法
-
为简洁代码,在方法上抛出总的异常,实际开发别这样
*/
private static void getPrivateMethod() throws Exception{
//1. 获取 Class 类实例
TestClass testClass = new TestClass();
Class mClass = testClass.getClass();
//2. 获取私有方法
//第一个参数为要获取的私有方法的名称
//第二个为要获取方法的参数的类型,参数为 Class...,没有参数就是null
//方法参数也可这么写 :new Class[]{String.class , int.class}
Method privateMethod =
mClass.getDeclaredMethod("privateMethod", String.class, int.class);
//3. 开始操作方法
if (privateMethod != null) {
//获取私有方法的访问权
//只是获取访问权,并不是修改实际权限
privateMethod.setAccessible(true);
//使用 invoke 反射调用私有方法
//privateMethod 是获取到的私有方法
//testClass 要操作的对象
//后面两个参数传实参
privateMethod.invoke(testClass, "Java Reflect ", 666);
}
}
需要注意的是,第3步中的 `setAccessible(true)` 方法,是获取私有方法的访问权限,如果不加会报异常 **IllegalAccessException**,因为当前方法访问权限是“private”的,如下:
java.lang.IllegalAccessException: Class MainClass can not access a member of class obj.TestClass with modifiers “private”
正常运行后,打印如下,**调用私有方法**成功:
Java Reflect 666
3.2 修改私有变量
----------
以修改 `TestClass` 类中的私有变量 `MSG` 为例,其初始值为 “Original” ,我们要修改为 “Modified”。老规矩,先上代码看注释。
/**
-
修改对象私有变量的值
-
为简洁代码,在方法上抛出总的异常
*/
private static void modifyPrivateFiled() throws Exception {
//1. 获取 Class 类实例
TestClass testClass = new TestClass();
Class mClass = testClass.getClass();
//2. 获取私有变量
Field privateField = mClass.getDeclaredField("MSG");
//3. 操作私有变量
if (privateField != null) {
//获取私有变量的访问权
privateField.setAccessible(true);
//修改私有变量,并输出以测试
System.out.println("Before Modify:MSG = " + testClass.getMsg());
//调用 set(object , value) 修改变量的值
//privateField 是获取到的私有变量
//testClass 要操作的对象
//"Modified" 为要修改成的值
privateField.set(testClass, "Modified");
System.out.println("After Modify:MSG = " + testClass.getMsg());
}
}
此处代码和访问私有方法的逻辑差不多,就不再赘述,从输出信息看出 **修改私有变量** 成功:
Before Modify:MSG = Original
After Modify:MSG = Modified
3.3 修改私有常量
----------
在 3.2 中,我们介绍了如何修改私有 **变量**,现在来说说如何修改私有 **常量**,
### 01\. 真的能修改吗?
常量是指使用 `final` 修饰符修饰的成员属性,与变量的区别就在于有无 `final` 关键字修饰。在说之前,先补充一个知识点。
Java 虚拟机(JVM)在编译 `.java` 文件得到 `.class` 文件时,会优化我们的代码以提升效率。其中一个优化就是:JVM 在编译阶段会把引用常量的代码替换成具体的常量值,如下所示(部分代码)。
编译前的 `.java` 文件:
//注意是 String 类型的值
private final String FINAL_VALUE = “hello”;
if(FINAL_VALUE.equals(“world”)){
//do something
}
编译后得到的 `.class` 文件(当然,编译后是没有注释的):
private final String FINAL_VALUE = “hello”;