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]
訪問成員變量
通過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 野雞悶頭鑽,哪能上天王山