java反射機制初體驗2


Constructor

Constructor 提供關於類的單個構造方法的信息以及對它的訪問權限。

Constructor 允許在將實參與帶有底層構造方法的形參的 newInstance() 匹配時進行擴展轉換,但是如果發生收縮轉換,則拋出 IllegalArgumentException。

示例代碼:

View Code
 1 package reflect;
 2 
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6 import java.util.Iterator;
 7 
 8 public class ReflectTest {
 9 
10     /**
11      * @author alan
12      * @param args
13      * @throws Exception
14      */
15     public static void main(String[] args) throws Exception {
16         /*
17          * Contructor類解析:類的構造方法沒有順序 Constructor 提供關於類的單個構造方法的信息以及對它的訪問權限。
18          * Constructor 允許在將實參與帶有底層構造方法的形參的 newInstance() 匹配時進行擴展轉換,
19          * 但是如果發生收縮轉換,則拋出 IllegalArgumentException。
20          * 
21          * 反射比較占用時間,需要緩存。程序性能下降:查看Class源碼
22          * */
23           System.out.println("Contructor類解析"); 
24           Constructor constructor=String.class.getConstructor(StringBuffer.class); //getConstructor是可變參數,選擇構造方法(含一個字符串參數),運行時執行 
25           String string2=(String) constructor.newInstance(new StringBuffer("string"));//通過參數來選擇構造方法創建實例。(與構造方法同樣類型的變量) 
26           System.out.println(string2);// 編譯時無措:二進制代碼 運行時出錯:Exception in thread "main"  java.lang.IllegalArgumentException: argument type mismatch
27         
28          //String string3=(String) constructor.newInstance("string");//參數不同,運行時出錯
29          //System.out.println(string3);
30     }
31 }

 

Class 的newInstance()方法解析。newInstance ()中調用的newInstance0()方法使用緩存機制來保存默認構造方法的實例。

jdk源碼解析:

public T newInstance()

        throws InstantiationException, IllegalAccessException

    {

        if (System.getSecurityManager() != null) {

            checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), false);

        }

        return newInstance0();

    }

 

private T newInstance0()

        throws InstantiationException, IllegalAccessException

    {

        // NOTE: the following code may not be strictly correct under

        // the current Java memory model.

 

        // Constructor lookup

        if (cachedConstructor == null) {    //緩存為空

            if (this == Class.class) {

                throw new IllegalAccessException(

                    "Can not call newInstance() on the Class for java.lang.Class"

                );

            }

            try {

                Class<?>[] empty = {};

                final Constructor<T> c = getConstructor0(empty, Member.DECLARED);//得到無參的構造方法構造實例對象。

                // Disable accessibility checks on the constructor

                // since we have to do the security check here anyway

                // (the stack depth is wrong for the Constructor's

                // security check to work)

                java.security.AccessController.doPrivileged(

                    new java.security.PrivilegedAction<Void>() {

                        public Void run() {

                                c.setAccessible(true);

                                return null;

                            }

                        });

                cachedConstructor = c;     //將c緩存起來,下次調用newInstance時,直接讀取緩存並返回

            } catch (NoSuchMethodException e) {

                throw new InstantiationException(getName());

            }

        }

        Constructor<T> tmpConstructor = cachedConstructor;            //調用緩存內的實例對象創建tmpConstructor

 

 

Field

 

Field 提供有關類或接口的單個字段的信息,以及對它的動態訪問權限。反射的字段可能是一個類(靜態)字段或實例字段。

 

Array 允許在執行 get 或 set 訪問操作期間進行擴展轉換,但如果將發生收縮轉換,則拋出一個 IllegalArgumentException。

示例代碼:

View Code
 1 package reflect;
 2 
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6 import java.util.Iterator;
 7 
 8 public class ReflectTest {
 9 
10     /**
11      * @author alan
12      * @param args
13      * @throws Exception
14      */
15     public static void main(String[] args) throws Exception {
16 
17 
18         /*
19          * Field類解析 Field 提供有關類或接口的單個字段的信息,以及對它的動態訪問權限。反射的字段可能是一個類(靜態)字段或實例字段。
20          * Array 允許在執行 get 或 set 訪問操作期間進行擴展轉換,但如果將發生收縮轉換,則拋出一個
21          * IllegalArgumentException。
22          */ 
23           System.out.println("Field類解析:"); 
24           ReflectPoint reflectPoint=new ReflectPoint(1, 2);
25           
26           Field fieldX=reflectPoint.getClass().getField("x"); //x!=1對應到類字節碼的變量,沒有對應類對象身上。
27           System.out.println(fieldX.get(reflectPoint)); //在reflectPoint具體對象中的變量
28           
29           Field fieldY=reflectPoint.getClass().getDeclaredField("y"); //y不可見
30           fieldY.setAccessible(true); //暴力反射
31           System.out.println(fieldY.get(reflectPoint));
32           
33           System.out.println("替換成員變量中的字符實驗");
34           replaceWord(reflectPoint); //替換成員變量中的字符。
35           System.out.println(reflectPoint);
36 
37         
38     }
39     public static void replaceWord(Object object) throws Exception {
40         Field[] fields = object.getClass().getFields();
41         for (Field field : fields) {
42             if (field.getType() == String.class) { // 只有一份字節碼,所以使用“==”比較,而非equals
43                 String oldWord = (String) field.get(object);
44                 String newWord = oldWord.replace("w", "W");
45                 field.set(object, newWord);
46             }
47         }
48     }
49 }
50 
51     

 輔助ReflectPoint類代碼:

View Code
 1 package reflect;
 2 
 3 public class ReflectPoint {
 4      public int x;
 5      private  int y;
 6      public String string="helloworld";
 7     
 8     public ReflectPoint(int x, int y) {
 9         super();
10         this.x = x;
11         this.y = y;
12     }
13     
14     public String  toString () {
15         return "helloworld has changed to "+string;
16     }
17 }

 

Method

 

Method 提供關於類或接口上單獨某個方法(以及如何訪問該方法)的信息。所反映的方法可能是類方法或實例方法(包括抽象方法)。

Method 允許在匹配要調用的實參與底層方法的形參時進行擴展轉換;但如果要進行收縮轉換,則會拋出 IllegalArgumentException。

1、  得到類中的某個方法,如下示:

Method chatAt=Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);

2、  調用方法,如下示:

通用方式:System.out.println(string.charAt(1));

反射方式:System.out.println(charAt.invoke(string,1));如果傳遞給Method對象的invoke()方法的一個參數為null時,說明Method對象對應的是一個靜態方法。

示例代碼:

View Code
 1 package reflect;
 2 
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6 import java.util.Iterator;
 7 
 8 public class ReflectTest {
 9 
10     /**
11      * @author alan
12      * @param args
13      * @throws Exception
14      */
15     public static void main(String[] args) throws Exception {
16         /*
17          * Method類解析
18          */
19         System.out.println("Method類解析:"); 
20         String string = "string";
21         System.out.println("通用方式:"+string.charAt(1));
22         Class class1 = string.getClass();
23         Method charAtMethod = String.class.getMethod("charAt", int.class);
24         System.out.println("反射方式:"+charAtMethod.invoke(string, 1));// 指定對象string調用由此 Method對象charAtMethod表示的底層方法charAt().        
25     }    
26 }

 

3、寫一個程序,程序能夠根據用戶提供的類名,去執行該類中的main方法。

示例代碼:

View Code
 1 package reflect;
 2 
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6 import java.util.Iterator;
 7 
 8 public class ReflectTest {
 9 
10     /**
11      * @author alan
12      * @param args
13      * @throws Exception
14      */
15     public static void main(String[] args) throws Exception {
16 
17     //寫一個程序,程序能夠根據用戶提供的類名,去執行該類中的main方法
18     System.out.println("寫一個程序,程序能夠根據用戶提供的類名,去執行該類中的main方法:");
19     TestArgument.main(new String[]{"1","2","3"});        //普通方式:類的靜態方法調用
20     //使用反射方式調用。源程序無法知道類的名稱,
21     String startString=args[0];
22     Method mainMethod=Class.forName(startString).getMethod("main", String[].class);
23     //mainMethod.invoke(null, new String[]{"1","2","3"});    //出現錯誤Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
24     mainMethod.invoke(null, new Object[]{ new String[]{"1","2","3"}});    //invoke第一個參數為null表示為靜態方法調用。
25     //mainMethod.invoke(null, (Object)new String[]{"1","2","3"});    
26     }
27 }
28 
29 class TestArgument{
30     public static void  main(String[] args) {
31         for (String string : args) {
32             System.out.println(string);
33         }
34         
35     }
36     
37 }

問題:Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments

原因:jdk1.4語法中,數組中每個元素對應一個參數。為了兼容jdk1.4,javac會按jdk1.4語法處理。即把數組打散成為若干個單獨的參數。

 解決方法:

              1、mainMethod.invoke(null, new Object[]{ new String[]{"1","2","3"}});      //再包裝一層,拆后為實際所需的參數。此方法效率較低。

              2、mainMethod.invoke(null, (Object)new String[]{"1","2","3"}); //給的是對象,不是數組,不會拆。編譯器會做特殊處理,編譯時不把參數當作數組對待,也就不會將數組打散為若干個參數了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM