java語言中,在一個類中,為了不讓外界訪問到有的屬性和方法,通常將其設置為private,用正常的方式(對象名.屬性名,對象名.方法名)將無法訪問此屬性與方法,但有沒有其他方法可以訪問呢?答案是有的,這就是java反射帶來的便利。利用反射訪問類的私有屬性及方法如下:
1.準備一個java類,包含私有屬性及方法:
- //Exam.java
- public class Exam{
- private String field1="私有屬性";
- public String field2="公有屬性";
- public void fun1(){
- System.out.println("fun1:這是一個public訪問權限方法");
- }
-
- private void fun2(){
- System.out.println("fun2:這是一個private訪問權限方法");
- }
-
- private void fun3(String arg){
- System.out.println("fun3:這是一個private訪問權限且帶參數的方法,參數為:"+arg);
- }
-
- }
將其編譯成class,然后刪除java源文件。注意:刪除java源文件并非必須,但是在實際情況中,我會使用的往往不是java源文件,而是jar包,而jar包中的文件都是class,所以為了貼近實際的情況,將Exam.java編譯成Exam.class文件后,刪除Exam.java文件,只保留Exam.class文件。
2.獲取類中屬性及方法的信息
第一步做好后,接下來進行第二步:獲取類中屬性及方法的信息。對于一個class文件,無法查看源碼,如何獲取類中屬性及方法的信息呢?很自然地,我們會想到利用java反射功能,得到類的屬性、方法:
- //Test01.java
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
-
- public class Test01 {
- public static void main(String args[]){
- Exam e=new Exam(); //初始化Exam實例
- System.out.println("獲取類中所有的屬性:");
- Field field[] = e.getClass().getFields();
- for(Field f : field){
- System.out.println(f);
- }
- System.out.println("獲取類中所有的方法:");
- Method[] method = e.getClass().getMethods();
- for(Method m : method){
- System.out.println(m);
- }
-
- }
- }
運行結果如下:

從運行結果可以看到,獲取的屬性中只有field2,而獲取的方法中似乎多了好多類中沒有的,但最主要的是,無論是屬性還是方法,都是public聲明的,沒有得到private聲明的屬性或方法。由此可以推斷:
1.聲明為private的屬性或方法無法通過這種途徑來獲取。
2.結果中多出來的方法為從Object類中繼承來的方法。
那么,是不是就沒有辦法了呢?不是的!我們可以使用jdk中的javap命令來突破這個問題:
java -private Exam ; -private 標志表示所有的成員都應該顯示,甚至包括私有成員
運行結果如下:

此時,可以看到類中所有的屬性、方法都有了。
3.調用Method及Field類中的相關方法獲取private聲明的屬性及方法
也許你還在為第二步中如此辛苦地得到類中的信息相當不解,其實這是在為這一步作鋪墊。請看以下代碼:Testo2.java
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
-
- public class Test02 {
- public static void main(String args[]){
- Exam e=new Exam();
- try {
- field1 = e.getClass().getDeclaredField("field1");
- field2 = e.getClass().getDeclaredField("field2");
- field1.setAccessible(true);
- System.out.println("field1: "+field1.get(e));
- field1.set(e,"重新設置一個field1值");
- System.out.println("field1: "+field1.get(e));
- System.out.println("field2: "+field2.get(e));
- field2.set(e,"重新設置一個field2值");
- System.out.println("field2: "+field2.get(e));
- } catch (NoSuchFieldException e1) {
- e1.printStackTrace();
- }catch (IllegalArgumentException e1) {
- e1.printStackTrace();
- } catch (IllegalAccessException e1) {
- e1.printStackTrace();
- }
-
- try {
-
- Method method1 = e.getClass().getDeclaredMethod("fun1");
- method1.invoke(e);
-
- Method method2 = e.getClass().getDeclaredMethod("fun2");
- method2.setAccessible(true);
- method2.invoke(e);
-
- Method method3 = e.getClass().getDeclaredMethod("fun3",String.class);
- method3.setAccessible(true);
- method3.invoke(e,"fun3的參數");
- } catch (NoSuchMethodException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- } catch (SecurityException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }catch (IllegalAccessException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- } catch (IllegalArgumentException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- } catch (InvocationTargetException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- }
- }
注意以下語句:
- field1 = e.getClass().getDeclaredField("field1");
getDeclaredField(String fieldName)中,參數fieldName為屬性名,
- Method method3 = e.getClass().getDeclaredMethod("fun3",String.class);
getDeclaredMethod(String methodName,Class parameterType)中,第一個參數為方法名,第二個參數為方法參數類型,當然在此方法中第二個參數為可孌參數。
Test02.java運行結果如下:

由運行結果可知,利用反射不但可以訪問類的私有屬性、方法,還可以重新設置私有屬性的值,調用私有方法。
|