JAVA基礎知識之JVM-——使用反射生成並操作對象


Class對象可以獲取類里的方法,由Method對象表示,調用Method的invoke可以執行對應的方法;可以獲取構造器,由Constructor對象表示,調用Constructor對象的newInstance方法可以執行類對應的構造方法;可以獲取成員變量,由Field對象表示,通過Field對象可以直接修改類的成員變量的訪問權限和值。

創建對象

通過反射有兩種方式創建對象

使用Class對象的newInstance(),這是最常用的方式,根據配置文件信息創建對象。

使用Class對象獲取指定的Constructor對象,再由Constructor對象的newInstance創建類的對象。

調用方法

首先要獲取類在JVM中對應的Class對象,通過類的Class對象的getMethod方法來獲取類的方法,返回Method類型的對象,最后通過Method對象的invoke來執行類的方法。

下面的例子將演示從配置文件中讀取數據來動態地創建對象,還通過配置文件信息來執行對象的setter來初始化對象。

 1 package jvmTest;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.IOException;
 5 import java.lang.reflect.Method;
 6 import java.util.HashMap;
 7 import java.util.Map;
 8 import java.util.Properties;
 9 
10 public class ExtendedObjectPoolFactory {
11     private Map<String, Object> objectPool = new HashMap<>();
12     private Properties config = new Properties();
13     public void init(String fileName) {
14         try {
15             FileInputStream fis = new FileInputStream(fileName);
16             config.load(fis);
17         } catch (IOException ex) {
18             System.out.println("讀取 " + fileName + " 異常");
19             ex.printStackTrace();
20         }
21     }
22     private Object createObject(String clazzName) throws Exception {
23         Class<?> clazz = Class.forName(clazzName); 24         //使用clazz默認構造器創建對象
25         return clazz.newInstance(); 26     }
27     //根據配置文件創建對象
28     public void initPool() throws Exception {
29         for (String name : config.stringPropertyNames()) {
30             if (!name.contains("%")) {
31                 objectPool.put(name, createObject(config.getProperty(name)));
32             }
33         }
34     }
35     
36     //根據配置文件來執行對象對應的set方法
37     public void initProperty() throws Exception {
38         for (String name : config.stringPropertyNames()) {
39             if (name.contains("%")) {
40                 String[] objAndPro = name.split("%");
41                 //獲取對象
42                 Object target = getObject(objAndPro[0]);
43                 //構造setter方法名
44                 String mtdName = "set"+ objAndPro[1].substring(0,1).toUpperCase() + objAndPro[1].substring(1);
45                 Class<?> targetClass = target.getClass(); 46                 Method mtd = targetClass.getMethod(mtdName, String.class); 47  mtd.invoke(target, config.getProperty(name)); 48             }
49         }
50     }
51     
52     public Object getObject(String name) {
53         return objectPool.get(name);
54     }
55     
56     public static void main(String[] args) throws Exception {
57         ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory();
58         epf.init("extObj.txt");
59         epf.initPool();
60         epf.initProperty();
61         System.out.println(epf.getObject("a"));
62     }
63 }

上面程序第23行獲取Class對象后在第25行使用默認的構造器動態創建出了對象, 

如果要使用指定構造器,需要先獲取Constructor對象,再使用Cronstructor對象的newInstance創建對象,像下面這樣,

1 Constructor ctor = clazz.getConstructor(String.class);
2 return ctor.newInstance("here is parameter for specific constructor");

第45行獲取Class對象后,在46行通過Class對象獲取指定要執行的方法(帶一個參數),用Method對象表示,最后在47行調用Method對象的invoke可以執行類的指定方法

上面的例子使用下面的測試文件進行測試,

extObj.txt

1 a=javax.swing.JFrame
2 b=javax.swing.JLabel
3 a%title=Test title

輸入結果,

javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=Test title,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
View Code

 訪問成員變量

通過Class對象的getField方法可以訪問指定的成員變量,調用繼承自AccessableObject的setAccessable方法可以獲得private成員變量的訪問權限。

以下方法可以訪問成員變量,

get(Object obj), 獲取成員變量值。  如果是基本類型,直接使用類似 getInt(Object obj),  getChar(Object obj)的方式

set(Object obj), 設置成員變量值。 如果是基本類型,直接使用類似 setInt(Object, int val), setChar(Object obj, char c)的方式

下面演示訪問成員變量,

 1 package jvmTest;
 2 
 3 import java.lang.reflect.Field;
 4 
 5 class Person {
 6     private String name;
 7     private int age;
 8     public String toString() {
 9         return "Person[name: "+name+", age: "+age+"]";
10     }
11 }
12 public class FieldTest {
13     public static void main(String[] args) throws Exception {
14         Person p = new Person();
15         Class<Person> clazz = Person.class;
16         //getDeclaredFiled可以獲取所有訪問權限的成員變量
17         Field nameField = clazz.getDeclaredField("name");
18         //true表示取消權限限制
19         nameField.setAccessible(true);
20         nameField.set(p, "Tom"); 21         Field ageField = clazz.getDeclaredField("age");
22         ageField.setAccessible(true);
23         ageField.setInt(p, 18); 24         System.out.println(p);
25     }
26 }

上面程序第20行使用的是set(), 而23行使用的是setInt(), 輸出結果,

Person[name: Tom, age: 18]

操作數組

java.lang.reflect還包含一個Array類,可以動態地創建數組,設置元素的值,比較強大,主要方法有,

newInstance(..),創建數組,可以指定數組元素類型, 數組維度, 數組長度

get(...), 獲取索引為index的元素,對於基本數據類型的數組,方法為getInt(...),  getChar(...) ...

set(...), 設置索引為index的元素,對於基本數據類型的數組,方法為setInt(...),  setChar(...) ...

下面演示用法,

 1 package jvmTest;
 2 
 3 import java.lang.reflect.Array;
 4 
 5 public class ArrayTest2 {
 6     public static void main(String[] args) {
 7         // 創建三維數組
 8         Object arr = Array.newInstance(String.class, 3, 4, 10);
 9         // 獲取index為2的元素,該元素是一個二維數組
10         Object arrObj = Array.get(arr, 2);
11         // 給二維數組index為2的元素賦值
12         // 二維數組的元素是一維數組,所以賦值也要用數組來賦值
13         Array.set(arrObj, 2, new String[] { "天王蓋地虎", "寶塔鎮河妖" });
14         //獲取二維數組指定index的元素(結果是一維數組)
15         Object anArr = Array.get(arrObj, 3);
16         Array.set(anArr, 8, "野雞悶頭鑽,哪能上天王山");
17         //將arr強制轉換為三維數組
18         String[][][] cast = (String[][][])arr;
19         
20         System.out.println(cast[2][2][0]);
21         System.out.println(cast[2][2][1]);
22         System.out.println(cast[2][3][8]);
23     }
24 }

輸出如下,

1 天王蓋地虎
2 寶塔鎮河妖
3 野雞悶頭鑽,哪能上天王山

 


免責聲明!

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



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