电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

java使用反射創(chuàng)建并操作對象的方法

瀏覽:50日期:2022-08-30 16:53:10

Class 對象可以獲得該類里的方法(由 Method 對象表示)、構(gòu)造器(由 Constructor 對象表示)、成員變量(由 Field 對象表示),這三個類都位于 java.lang.reflect 包下,并實現(xiàn)了 java.lang.reflect.Member 接口。程序可以通過對象來執(zhí)行對應(yīng)的方法,通過 Constructor 對象來調(diào)用對應(yīng)的構(gòu)造器創(chuàng)建實例,能通過 Field 對象直接訪問并修改對象的成員變量值。

創(chuàng)建對象

通過反射來生成對象需要先使用 Class 對象獲取指定的 Constructor 對象,再調(diào)用 Constructor 對象的 newInstance() 方法來創(chuàng)建該 Class 對象對應(yīng)類的實例。通過這種方式可以選擇使用指定的構(gòu)造器來創(chuàng)建實例。

在很多 Java EE 框架中都需要根據(jù)配置文件信息來創(chuàng)建 Java 對象,從配置文件讀取的只是某個類的字符串類名,程序需要根據(jù)該字符串來創(chuàng)建對應(yīng)的實例,就必須使用反射。

下面程序就實現(xiàn)了一個簡單的對象池,該對象池會根據(jù)配置文件讀取 key-value 對,然后創(chuàng)建這些對象,并將這些對象放入一個 HashMap 中。

public class ObjectPoolFactory { // 定義一個對象池,前面是對象名,后面是實際對象 private Map<String, Object> objectPool = new HashMap<>(); // 定義一個創(chuàng)建對象的方法 // 該方法只要傳入一個字符串類名,程序可以根據(jù)該類名生成Java對象 private Object createObject(String clazzName) throws Exception, IllegalAccessException, ClassNotFoundException { // 根據(jù)字符串來獲取對應(yīng)的Class對象 Class<?> clazz = Class.forName(clazzName); // 使用clazz對應(yīng)類的默認構(gòu)造器創(chuàng)建實例 return clazz.getConstructor().newInstance(); } // 該方法根據(jù)指定文件來初始化對象池 // 它會根據(jù)配置文件來創(chuàng)建對象 public void initPool(String fileName) throws InstantiationException, IllegalAccessException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream(fileName)) { Properties props = new Properties(); props.load(fis); for (String name : props.stringPropertyNames()) { // 每取出一對key-value對,就根據(jù)value創(chuàng)建一個對象 // 調(diào)用createObject()創(chuàng)建對象,并將對象添加到對象池中 objectPool.put(name, createObject(props.getProperty(name))); } } catch (Exception ex) { System.out.println('讀取' + fileName + '異常'); } } public Object getObject(String name) { // 從objectPool中取出指定name對應(yīng)的對象 return objectPool.get(name); } public static void main(String[] args) throws Exception { ObjectPoolFactory pf = new ObjectPoolFactory(); pf.initPool('obj.txt'); System.out.println(pf.getObject('a')); // ① System.out.println(pf.getObject('b')); // ② }}

上面程序中 createObject() 方法里的兩行粗體字代碼就是根據(jù)字符串來創(chuàng)建 Java 對象的關(guān)鍵代碼,程序調(diào)用 Class 對象的 newInstance() 方法即可創(chuàng)建一個 Java 對象。程序中的 initPool() 方法會讀取屬性文件,對屬性文件中每個 key-value 對創(chuàng)建一個 Java 對象,其中 value 是該 Java 對象的實現(xiàn)類,而 key 是該 Java 對象放入對象池中的名字。為該程序提供如下屬性配置文件。

a=java.util.Dateb=javax.swing.JFrame

編譯、運行上面的 ObjectPoolFactory 程序,執(zhí)行到 main 方法中的①號代碼處,將看到輸出系統(tǒng)當前時間——這表明對象池中已經(jīng)有了一個名為a的對象,該對象是一個 java.util.Date 對象。執(zhí)行到②號代碼處,將看到輸出一個 JFrame 對象。

提示:這種使用配置文件來配置對象,然后由程序根據(jù)配置文件來創(chuàng)建對象的方式非常有用,大名鼎鼎的 Spring 框架就采用這種方式大大簡化了 Java EE 應(yīng)用的開發(fā)。當然,Spring 采用的是 XML 配置文件——畢竟屬性文件能配置的信息太有限了,而 XML 配置文件能配置的信息就豐富多。

如果不想利用默認構(gòu)造器來創(chuàng)建 Java 對象,而想利用指定的構(gòu)造器來創(chuàng)建 Java 對象,則需要利用 Constructor 對象,每個 Constructor 對應(yīng)一個構(gòu)造器。為了利用指定的構(gòu)造器來創(chuàng)建 Java 對象,需要如下三個步驟。

獲取該類的 Class 對象。 利用 Class 對象的 getConstructor() 方法來獲取指定的構(gòu)造器。 調(diào)用 Constructor 的 newInstance() 方法來創(chuàng)建 Java 對象。

下面程序利用反射來創(chuàng)建一個 JFrame 對象,而且使用指定的構(gòu)造器。

public class CreateJFrame { public static void main(String[] args) throws Exception { // 獲取JFrame對應(yīng)的Class對象 Class<?> jframeClazz = Class.forName('javax.swing.JFrame'); // 獲取JFrame中帶一個字符串參數(shù)的構(gòu)造器 Constructor ctor = jframeClazz.getConstructor(String.class); // 調(diào)用Constructor的newInstance方法創(chuàng)建對象 Object obj = ctor.newInstance('測試窗口'); // 輸出JFrame對象 System.out.println(obj); }}

上面程序中第一行粗休字代碼用于獲取 JFrame 類的指定構(gòu)造器,前面已經(jīng)提到:如果要唯一地確定某類中的構(gòu)造器,只要指定構(gòu)造器的形參列表即可。第一行粗體字代碼獲取構(gòu)造器時傳入了一個 String 類型,即表明想獲取只有一個字符串參數(shù)的構(gòu)造器。

程序中第二行粗體字代碼使用指定構(gòu)造器的 newInstance() 方法來創(chuàng)建一個 Java 對象,當調(diào)用 Constructor 對象的 newInstance() 方法時通常需要傳入?yún)?shù),因為調(diào)用 Constructor 的 newInstance() 方法實際上等于調(diào)用它對應(yīng)的構(gòu)造器,傳給 newInstance() 方法的參數(shù)將作為對應(yīng)構(gòu)造器的參數(shù)。

對于上面的 CreateFrame.java 中已知 java.swing.JFrame 類的情形,通常沒有必要使用反射來創(chuàng)建該對象,畢竟通過反射創(chuàng)建對象時性能要稍低一些。實際上,只有當程序需要動態(tài)創(chuàng)建某個類的對象時才會考慮使用反射,通常在開發(fā)通用性比較廣的框架、基礎(chǔ)平臺時可能會大量使用反射。

調(diào)用方法

當獲得某個類對應(yīng)的 Class 對象后,就可以通過該 Class 對象的 getMethods() 方法或者 getMethod()方法來獲取全部方法或指定方法——這兩個方法的返回值是 Method 數(shù)組,或者 Method 對象。

每個 Method 對象對應(yīng)一個方法,獲得 Method 對象后,程序就可通過該 Method 來調(diào)用它對應(yīng)的方法。在 Method 里包含一個 Invoke() 方法,該方法的簽名如下。

Object invoke(Object obj, Object...args):該方法中的 obj 是執(zhí)行該方法的主調(diào),后面的 args 是執(zhí)行該方法時傳入該方法的實參。

下面程序?qū)η懊娴膶ο蟪毓S進行加強,允許在配置文件中增加配置對象的成員變量的值,對象池工廠會讀取為該對象配置的成員變量值,并利用該對象對應(yīng)的 setter 方法設(shè)置成員變量的值。

public class ExtendedObjectPoolFactory { // 定義一個對象池,前面是對象名,后面是實際對象 private Map<String, Object> objectPool = new HashMap<>(); private Properties config = new Properties(); // 從指定屬性文件中初始化Properties對象 public void init(String fileName) { try (FileInputStream fis = new FileInputStream(fileName)) { config.load(fis); } catch (IOException ex) { System.out.println('讀取' + fileName + '異常'); } } // 定義一個創(chuàng)建對象的方法 // 該方法只要傳入一個字符串類名,程序可以根據(jù)該類名生成Java對象 private Object createObject(String clazzName) throws Exception { // 根據(jù)字符串來獲取對應(yīng)的Class對象 Class<?> clazz = Class.forName(clazzName); // 使用clazz對應(yīng)類的默認構(gòu)造器創(chuàng)建實例 return clazz.getConstructor().newInstance(); } // 該方法根據(jù)指定文件來初始化對象池 // 它會根據(jù)配置文件來創(chuàng)建對象 public void initPool() throws Exception { for (String name : config.stringPropertyNames()) { // 每取出一個key-value對,如果key中不包含百分號(%) // 這就表明是根據(jù)value來創(chuàng)建一個對象 // 調(diào)用createObject創(chuàng)建對象,并將對象添加到對象池中 if (!name.contains('%')) { objectPool.put(name, createObject(config.getProperty(name))); } } } // 該方法將會根據(jù)屬性文件來調(diào)用指定對象的setter方法 public void initProperty() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { for (String name : config.stringPropertyNames()) { // 每取出一對key-value對,如果key中包含百分號(%) // 即可認為該key用于控制調(diào)用對象的setter方法設(shè)置值 // %前半為對象名字,后半控制setter方法名 if (name.contains('%')) { // 將配置文件中的key按%分割 String[] objAndProp = name.split('%'); // 取出調(diào)用setter方法的參數(shù)值 Object target = getObject(objAndProp[0]); // 獲取setter方法名:set + '首字母大寫' + 剩下部分 String mtdName = 'set' + objAndProp[1].substring(0, 1).toUpperCase() + objAndProp[1].substring(1); // 通過target的getClass()獲取它的實現(xiàn)類所對應(yīng)的Class對象 Class<?> targetClass = target.getClass(); // 獲取希望調(diào)用的setter方法 Method mtd = targetClass.getMethod(mtdName, String.class); // 通過Method的invoke方法執(zhí)行setter方法 // 將config.getProperty(name)的值作為調(diào)用setter方法的參數(shù) mtd.invoke(target, config.getProperty(name)); } } } public Object getObject(String name) { // 從objectPool中取出指定name對應(yīng)的對象 return objectPool.get(name); } public static void main(String[] args) throws Exception { ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory(); epf.init('extObj.txt'); epf.initPool(); epf.initProperty(); System.out.println(epf.getObject('a')); }}

上面程序中 initProperty() 方法里的第一行粗體字代碼獲取目標類中包含一個 String 參數(shù)的 setter 方法,第二行粗體字代碼通過調(diào)用 Method 的 invoke() 方法來執(zhí)行該 setter 方法,該方法執(zhí)行完成后,就相當于執(zhí)行了目標對象的 setter 方法。為上面程序提供如下配置文件。

a=javax.swing.JFrameb=javax.swing.JLabel#set the title of aa%title=Test Title

上面配置文件中的 a%title 行表明希望調(diào)用a對象的 setTitle() 方法,調(diào)用該方法的參數(shù)值為 Test Title。編譯、運行上面的 ExtendedObjectPoolFactory.java 程序,可以看到輸出一個 JFrame 窗口,該窗口的標題為 Test Title。

提示:Spring 框架就是通過這種方式將成員變量值以及依賴對象等都放在配置文件中進行管理的,從而實現(xiàn)了較好的解耦。這也是 Spring 框架的 IOC 的秘密。

當通過 Method 的 invoke() 方法來調(diào)用對應(yīng)的方法時,Java 會要求程序必須有調(diào)用該方法的權(quán)限。如果程序確實需要調(diào)用某個對象的 private 方法,則可以先調(diào)用 Method 對象的如下方法。

setAccessible(boolean flag):將 Method 對象的 accessible 設(shè)置為指定的布爾值。值為true,指示該 Method 在使用時應(yīng)該取消 Java 語言的訪問權(quán)限檢查:值為false,則指示該 Method 在使用時要實施 Java 語言的訪問權(quán)限檢查。

注意:實際上,setAccessible() 方法并不屬于 Method,而是屬于它的父類 AccessibleObject。因此 Method、Constructor、Field 都可調(diào)用該方法,從而實現(xiàn)通過反射來調(diào)用 private 方法、private 構(gòu)造器和成員變量,下一節(jié)將會讓讀者看到這種示例。也就是說,它們可以通過調(diào)用該方法來取消訪問權(quán)限檢查,通過反射即可訪問 private 成員。

訪問成員變量值

通過 Class 對象的 getFields() 或 getField() 方法可以獲取該類所包括的全部成員變量或指定成員變量。Field 提供了如下兩組方法來讀取或設(shè)置成員變量值。

getXxx(Object obj):獲取 obj 對象的該成員變量的值。此處的 Xxx 對應(yīng)8種基本類型,如果該成員變量的類型是引用類型,則取消 get 后面的Xxx。 setXxx(Object obj, Xxx val):將 obj 對象的該成員變量設(shè)置成值。此處的 Xxx 對應(yīng)8種基本類型,如果該成員變量的類型是引用類型,則取消 set 后面的Xxx。

使用這兩個方法可以隨意地訪問指定對象的所有成員變量,包括 private 修飾的成員變量。

class Person { private String name; private int age; public String toString() { return 'Person[name:' + name + ' , age:' + age + ' ]'; }}public class FieldTest { public static void main(String[] args) throws Exception { // 創(chuàng)建一個Person對象 Person p = new Person(); // 獲取Person類對應(yīng)的Class對象 Class<Person> personClazz = Person.class; // 獲取Person的名為name的成員變量 // 使用getDeclaredField()方法表明可獲取各種訪問控制符的成員變量 Field nameField = personClazz.getDeclaredField('name'); // 設(shè)置通過反射訪問該成員變量時取消訪問權(quán)限檢查 nameField.setAccessible(true); // 調(diào)用set()方法為p對象的name成員變量設(shè)置值 nameField.set(p, 'Yeeku.H.Lee'); // 獲取Person類名為age的成員變量 Field ageField = personClazz.getDeclaredField('age'); // 設(shè)置通過反射訪問該成員變量時取消訪問權(quán)限檢查 ageField.setAccessible(true); // 調(diào)用setInt()方法為p對象的age成員變量設(shè)置值 ageField.setInt(p, 30); System.out.println(p); }}

上面程序中先定義了一個 Person 類,該類里包含兩個 private 成員變量:name 和 age,在通常情況下,這兩個成員變量只能在 Person 類里訪問。但本程序 FieldTest 的 main() 方法中6行粗體字代碼通過反射修改了 Person 對象的 name、age 兩個成員變量的值。

第一行粗體字代碼使用 getDeclaredField() 方法獲取了名為 name 的成員變量,注意此處不是使用 getField()方法,因為 getField() 方法只能獲取 public 訪問控制的成員變量,而 getDeclaredField() 方法則可以獲取所有的成員變量;第二行粗體字代碼則通過反射訪問該成員變量時不受訪問權(quán)限的控制;第三行粗體字代碼修改了 Person 對象的 name 成員變量的值。修改 Person 對象的 age 成員變量的值的方式與此完全相同。

編譯、運行上面程序,會看到如下輸出:

Person[name:Yeeku.H.Lee , age:30 ]

操作數(shù)組

在 java.lang.reflect 包下還提供了一個 Array 類,Array 對象可以代表所有的數(shù)組。程序可以通過使用 Array 來動態(tài)地創(chuàng)建數(shù)組,操作數(shù)組元素等。

Array 提供了如下幾類方法。

static Object newInstance(Class<?> componentType,int...length):創(chuàng)建一個具有指定的元素類型、指定維度的新數(shù)組。 static xxx getXxx(Object array, int index):返回 array 數(shù)組中第 index 個元素。其中是各種基本數(shù)據(jù)類型,如果數(shù)組元素是引用類型,則該方法變?yōu)?get(Object array, int index)。 static void setXxx(Object array, int index, xxx val):將 array 數(shù)組中第 index 個元素的值設(shè)為 val。其中 xxx 是各種基本數(shù)據(jù)類型,如果數(shù)組元素是引用類型,則該方法變成 set(Object array, int index, Object val)。

下面程序示范了如何使用 Array 來生成數(shù)組,為指定數(shù)組元素賦值,并獲取指定數(shù)組元素的方式。

public class ArrayTest1 { public static void main(String args[]) { try { // 創(chuàng)建一個元素類型為String ,長度為10的數(shù)組 Object arr = Array.newInstance(String.class, 10); // 依次為arr數(shù)組中index為5、6的元素賦值 Array.set(arr, 5, '瘋狂Java講義'); Array.set(arr, 6, '輕量級Java EE企業(yè)應(yīng)用實戰(zhàn)'); // 依次取出arr數(shù)組中index為5、6的元素的值 Object book1 = Array.get(arr, 5); Object book2 = Array.get(arr, 6); // 輸出arr數(shù)組中index為5、6的元素 System.out.println(book1); System.out.println(book2); } catch (Throwable e) { System.err.println(e); } }}

上面程序中三行粗體字代碼分別是通過 Array 創(chuàng)建數(shù)組,為數(shù)組元素設(shè)置值,訪問數(shù)組元素的值的示例代碼,程序通過使用 Array 就可以動態(tài)地創(chuàng)建并操作數(shù)組。

下面程序比上面程序稍微復(fù)雜一點,下面程序使用 Array 類創(chuàng)建了一個三維數(shù)組。

public class ArrayTest2 { public static void main(String args[]) { /* * 創(chuàng)建一個三維數(shù)組。 根據(jù)前面介紹數(shù)組時講的:三維數(shù)組也是一維數(shù)組, 是數(shù)組元素是二維數(shù)組的一維數(shù)組, * 因此可以認為arr是長度為3的一維數(shù)組 */ Object arr = Array.newInstance(String.class, 3, 4, 10); // 獲取arr數(shù)組中index為2的元素,該元素應(yīng)該是二維數(shù)組 Object arrObj = Array.get(arr, 2); // 使用Array為二維數(shù)組的數(shù)組元素賦值。二維數(shù)組的數(shù)組元素是一維數(shù)組, // 所以傳入Array的set()方法的第三個參數(shù)是一維數(shù)組。 Array.set(arrObj, 2, new String[] { '瘋狂Java講義', '輕量級Java EE企業(yè)應(yīng)用實戰(zhàn)' }); // 獲取arrObj數(shù)組中index為3的元素,該元素應(yīng)該是一維數(shù)組。 Object anArr = Array.get(arrObj, 3); Array.set(anArr, 8, '瘋狂Android講義'); // 將arr強制類型轉(zhuǎn)換為三維數(shù)組 String[][][] cast = (String[][][]) arr; // 獲取cast三維數(shù)組中指定元素的值 System.out.println(cast[2][3][8]); System.out.println(cast[2][2][0]); System.out.println(cast[2][2][1]); }}

上面程序的第一行粗體字代碼使用 Array 創(chuàng)建了一個三維數(shù)組,程序中較難理解的地方是第二段粗體字代碼部分,使用 Array 為 arrObj 的指定元素賦值,相當于為二維數(shù)組的元素賦值。由于二維數(shù)組的元素是一維數(shù)組,所以程序傳入的參數(shù)是一個一維數(shù)組對象。

運行上面程序,將看到 cast[2][3][8]、cast[2][2][0]、cast[2][2][1] 元素都有值,這些值就是剛才程序通過反射傳入的數(shù)組元素值。

以上就是java使用反射創(chuàng)建并操作對象的方法的詳細內(nèi)容,更多關(guān)于JAVA 反射創(chuàng)建并操作對象的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標簽: Java
相關(guān)文章:
主站蜘蛛池模板: 集装袋吨袋生产厂家-噸袋廠傢-塑料编织袋-纸塑复合袋-二手吨袋-太空袋-曹县建烨包装 | 软装设计-提供软装装饰和软装配饰及软装陈设的软装设计公司 | 酒店厨房设计_中央厨房设计_北京商用厨房设计公司-奇能商厨 | 废气处理_废气处理设备_工业废气处理_江苏龙泰环保设备制造有限公司 | 时代北利离心机,实验室离心机,医用离心机,低速离心机DT5-2,美国SKC采样泵-上海京工实业有限公司 工业电炉,台车式电炉_厂家-淄博申华工业电炉有限公司 | 上海三信|ph计|酸度计|电导率仪-艾科仪器 | 短信通106短信接口验证码接口群发平台_国际短信接口验证码接口群发平台-速度网络有限公司 | 加盟店-品牌招商加盟-创业项目商机平台 | pos机办理,智能/扫码/二维码/微信支付宝pos机-北京万汇通宝商贸有限公司 | 吹塑加工_大型吹塑加工_滚塑代加工-莱力奇吹塑加工有限公司 | 隐形纱窗|防护纱窗|金刚网防盗纱窗|韦柏纱窗|上海青木装潢制品有限公司|纱窗国标起草单位 | 定量包装机,颗粒定量包装机,粉剂定量包装机,背封颗粒包装机,定量灌装机-上海铸衡电子科技有限公司 | 软文发布平台 - 云软媒网络软文直编发布营销推广平台 | 彭世修脚_修脚加盟_彭世修脚加盟_彭世足疗加盟_足疗加盟连锁_彭世修脚技术培训_彭世足疗 | 螺杆泵_中成泵业| 济南品牌设计-济南品牌策划-即合品牌策划设计-山东即合官网 | 铁盒_铁罐_马口铁盒_马口铁罐_铁盒生产厂家-广州博新制罐 | 拉力机-拉力试验机-万能试验机-电子拉力机-拉伸试验机-剥离强度试验机-苏州皖仪实验仪器有限公司 | 贵阳用友软件,贵州财务软件,贵阳ERP软件_贵州优智信息技术有限公司 | 深圳货架厂家_金丽声精品货架_广东金丽声展示设备有限公司官网 | 横河变送器-横河压力变送器-EJA变送器-EJA压力变送器-「泉蕴仪表」 | 红立方品牌应急包/急救包加盟,小成本好项目代理_应急/消防/户外用品加盟_应急好项目加盟_新奇特项目招商 - 中红方宁(北京) 供应链有限公司 | 盘煤仪,盘料仪,盘点仪,堆料测量仪,便携式激光盘煤仪-中科航宇(北京)自动化工程技术有限公司 | 液压油缸生产厂家-山东液压站-济南捷兴液压机电设备有限公司 | 薪动-人力资源公司-灵活用工薪资代发-费用结算-残保金优化-北京秒付科技有限公司 | 多米诺-多米诺世界纪录团队-多米诺世界-多米诺团队培训-多米诺公关活动-多米诺创意广告-多米诺大型表演-多米诺专业赛事 | EPDM密封胶条-EPDM密封垫片-EPDM生产厂家| 解放卡车|出口|济南重汽|报价大全|山东三维商贸有限公司 | 深圳公司注册-工商注册代理-注册公司流程和费用_护航财税 | 衬塑管道_衬四氟管道厂家-淄博恒固化工设备有限公司 | 山东信蓝建设有限公司官网 | 烟台螺纹,烟台H型钢,烟台钢材,烟台角钢-烟台市正丰金属材料有限公司 | 泰州物流公司_泰州货运公司_泰州物流专线-东鑫物流公司 | 湖南成人高考报名-湖南成考网| 膜片万向弹性联轴器-冲压铸造模具「沧州昌运模具」 | 智能气瓶柜(大型气瓶储存柜)百科 | 盘装氧量分析仪-防爆壁挂氧化锆分析仪-安徽吉帆仪表有限公司 | 厦门网站建设_厦门网站设计_小程序开发_网站制作公司【麦格科技】 | 沙盘模型公司_沙盘模型制作公司_建筑模型公司_工业机械模型制作厂家 | 深圳宣传片制作-企业宣传视频制作-产品视频拍摄-产品动画制作-短视频拍摄制作公司 | 复合土工膜厂家|hdpe防渗土工膜|复合防渗土工布|玻璃纤维|双向塑料土工格栅-安徽路建新材料有限公司 |