好程式設計師Java培訓分享Java之反射技術
好程式設計師 Java 培訓分享Java 之 反射技術
一、前期概要
1、 什麼是反射
Java 反射機制在程式
執行時
,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。這種
動態的獲取資訊
以及
動態呼叫物件的方法
的功能稱為
java 的反射機制
。
反射中的反的理解:在使用的之前,提前不知道需要使用什麼型別的物件。只是在呼叫的時候,才知道要呼叫的物件型別。這種反其道而行的就是反射中反的理解。
程式執行分為編譯器和執行期,編譯時刻載入一個類就稱為靜態載入類,執行時刻載入類稱為動態載入類,
核心思想
讓你在寫程式碼的時候可以更加靈活,降低耦合,提高程式碼的自適應能力。
反射框架提供如下常用的核心功能:
1.在執行時判斷任意物件所屬的類;
2.在執行時構造任意一個類的物件;
3.在執行時判斷任意一個類所具有的成員變數和方法(透過反射甚至可以呼叫private方法);
4.在執行時呼叫任意一個物件的方法;
2、反射的主要用途
通用框架 ,很多框架都是配置化的(比如Spring透過xml配置Bean), 為了保證框架的通用性,可能需要根據不同的配置檔案載入不同的物件或者類,呼叫不同的方法,這個時候就需要反射,執行時動態載入需要載入的物件。
3、缺點
· 效能不佳 - 由於java反射動態解析型別,因此涉及掃描類路徑以查詢要載入的類的處理,從而導致效能降低。
· 安全限制 - Reflection需要執行時許可權,這些許可權可能不適用於在安全管理器下執行的系統。由於安全管理器,這可能導致應用程式在執行時失敗。
· 安全問題 - 使用反射,我們可以訪問我們不應該訪問的部分程式碼,例如,我們可以訪問類的私有欄位並更改它的值。這可能是嚴重的安全威脅,並導致您的應用程式出現異常行為。
· 高維護 - 反射程式碼很難理解和除錯,在編譯時也無法找到程式碼的任何問題,因為這些類可能不可用,使其不太靈活且難以維護。
00001. Class 物件
00002. 類名
00003. 修飾符
00004. 包資訊
00005. 父類
00006. 實現的介面
00007. 構造器
00008. 方法
00009. 變數
00010. 註解
二、獲得 Class 物件
在執行期間,一個類,只有一個Class物件產生
1、類的靜態方法(常用) :
00001.
說明
獲取指定的類完整的路徑相關聯
類
或
介面
的Class物件。
00002.
方法
// 掌握
public static Class<?> forName(String className)
// 瞭解
public static Class<?> forName(String className, boolean initialize,ClassLoader loader)
00003.
舉個例子
Class clazz = Class.forName("
")
// 或者
Class clazz = Class.forName("
",initialize,this.getClass().getClassLoader)
1.
說明
任何資料型別(包括基本資料型別)都有一個“靜態”的class屬性
2.
方法
Class<?> cls = 型別.class;
3.
舉個例子
Class<String> cls = String.class;
System.out.println(cls.toString());
1.
說明
透過物件的例項來返回Class物件
2.
方法
Class<?> cls = instance.getClass()
3.
舉個例子
public class User {
}
User user = new User();
Class<? extends User> clz = user.getClass();
1.
方法
// 透過欄位名,返回一個具體的具有public屬性的成員變數(包括父類的)
Field getField(String name)
// 透過欄位名所有已宣告的所有成員變數(私有的 預設的 共有的),但不能得到其父類的成員變數
Field getDeclaredField(String name)
2.
舉個例子
public class User {
private String name;
public String detail;
}
public static void main(String[] args) {
try {
Class<?> cls = Class.forName("com.wener.reflect.demo1.User");
/**
* 獲取類的指定名稱公開的屬性
*/
Field detail = cls.getField("detail");
System.out.println(detail);
/**
* 獲取類的指定名稱的的屬性(包括私有的屬性)
*/
Field name = cls.getDeclaredField("name");
System.out.println(name);
} catch (ClassNotFoundException | NoSuchFieldException e) {
e.printStackTrace();
}
}
1.
說明
反射非常強大,但是學習了之後,會不知道該如何使用,反而覺得還不如直接呼叫方法來的直接和方便。
但等我們後面接觸到一些框架之後才會有一些感觸
2.
測試類
package com.wener.reflect.demo2; public class ReflectDemo1 { public void say() { System.out.println("ReflectDemo1"); } } public class ReflectDemo2 { public void say() { System.out.println("ReflectDemo2"); } }
3.
配置檔案reflect.properties
class=ReflectDemo2. method=say
4.
測試程式碼
public static void main(String[] args) { //從spring.txt中獲取類名稱和方法名稱 File springConfigFile = new File("/Users/zhangwei/work/IdeaProjects/JavaExample/ReflectExample/src/reflect.properties"); Properties properties = new Properties(); try { properties.load(new FileInputStream(springConfigFile)); String className = (String) properties.get("class"); String methodName = (String) properties.get("method"); //根據類名稱獲取類物件 Class cls = Class.forName(className); //根據方法名稱,獲取方法物件 Method m = cls.getMethod(methodName); //根據構造器,例項化出物件 Object service = cls.newInstance(); //呼叫物件的指定方法 m.invoke(service); } catch (IOException | InvocationTargetException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException | InstantiationException e) { e.printStackTrace(); } }
5.
優點
當需要從呼叫第一個類的方法,切換到呼叫第二類的方法的時候,不需要修改一行程式碼
1、透過配置檔案動態切換
六、綜合案例
返回值
方法說明
String getName()獲取方法的名稱int getModifiers()獲取方法的修飾符
Class<?> getReturnType()
Type getGenericReturnType
返回方法的返回值型別Class<?>[] getParameterTypes() Type[] getGenericParameterTypes()返回方法的引數(列表)Class<?>[] getExceptionTypes() Type[] getGenericExceptionTypes()返回方法的異常資訊
4、其它API
1.
舉個例子
public class User { private String name = "木木"; public String detail = "hello"; private int age; private BigDecimal balance; public void increment() { this.age++; System.out.println(age); } public BigDecimal getBalance() { return balance; } public void setBalance(BigDecimal balance) { this.balance = balance; } private void say(int num) { System.out.println(num + "號技師"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", detail='" + detail + '\'' + '}'; } } public class TestReflectUser { public static void main(String[] args) { reflectMethod(); } public static void reflectMethod() { try { // 1.例項化class物件 Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); // 2.例項化User物件 Object o = cls.newInstance(); // 3.獲取set方法 Method method = cls.getMethod("increment"); // 4 執行方法 method.invoke(o); // 有引數無返回值 Method setBalance = cls.getMethod("setBalance", BigDecimal.class); Object methodSet = setBalance.invoke(o, new BigDecimal(100.00)); System.out.println(methodSet); // 有返回值值無引數 Method methodGet = cls.getMethod("getBalance"); Object invoke = methodGet.invoke(o); System.out.println(invoke); // 獲取私有的方法 Method say = cls.getDeclaredMethod("say", int.class); // 執行時取消訪問許可權檢測機制 say.setAccessible(true); // 執行方法 say.invoke(o, 1); } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } }
使用引數 args 在 obj 上指派該物件所表示方法的結果
1.
方法
Object invoke(Object obj, Object... args)
2. 引數說明
· obj - 從中呼叫底層方法的物件,必須是例項化的物件
· args - 用於方法呼叫的引數,是個Object陣列,因為引數有可能有多個
1. 返回值
3、呼叫方法
1.
方法
// 返回類或介面宣告的所有方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法。 public Method[] getDeclaredMethods() throws SecurityException
// 返回某個類的所有公用(public)方法,包括其繼承類的公用方法。 public Method[] getMethods() throws SecurityException
2.
舉個例子
public class TestReflectUser { public static void main(String[] args) { reflectMethod(); } public static void reflectMethod() { try { // 1.例項化class物件 Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); // 2.例項化User物件 Object o = cls.newInstance(); // 獲取所有的共有的方法(包括父類的方法) Method[] methods = cls.getMethods(); for (Method method1 : methods) { System.out.println(method1.getName()); } // 獲取所有的方法(包括私有的,共有的,預設的) Method[] declaredMethods = cls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod.getName()); } } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } }
2、獲取所有的方法
1.
方法
// 方法返回一個特定的方法,其中第一個引數為方法名稱,後面的引數為方法的引數對應Class的物件 public Method getMethod(String name, Class<?>... parameterTypes)
2.
舉個例子
public class User { private String name = "木木"; public String detail = "hello"; private int age; private BigDecimal balance; public void increment() { this.age++; System.out.println(age); } public BigDecimal getBalance() { return balance; } public void setBalance(BigDecimal balance) { this.balance = balance; } private void say(int num) { System.out.println(num + "號技師"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", detail='" + detail + '\'' + '}'; } } public class TestReflectUser { public static void main(String[] args) { reflectMethod(); } public static void reflectMethod() { try { // 1.例項化class物件 Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); // 2.例項化User物件 Object o = cls.newInstance(); // 3.獲取increment方法 Method method = cls.getMethod("increment"); // 有引數無返回值 Method setBalance = cls.getMethod("setBalance", BigDecimal.class); // 有返回值值無引數 Method methodGet = cls.getMethod("getBalance"); // 獲取私有的方法 Method say = cls.getDeclaredMethod("say", int.class); } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } }
1、獲取單個方法
五、方法操作
1.
方法
// 將指定物件引數上的此Field物件表示的欄位設定為指定的新值 field.set(Object obj,Object value)
2. 引數說明
· Object obj: 欄位所在的類的例項物件
· Object value : 新值
1.
注意
// 如果要給私有變數賦值必須取消許可權的訪問控制 field.setAccessible(true);
2.
舉個例子
public static void main(String[] args) { try { // 1 例項化Class物件 Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); /** * 獲取類的指定名稱公有的屬性 */ Field detail = cls.getField("detail"); System.out.println(detail); /** * 獲取類的所有的屬性(包括私有 預設的 公有的) */ Field name = cls.getDeclaredField("name"); // 2.建立物件 Object o = cls.newInstance(); // 3 透過欄位的set方法設定 name.set(o, "嬌嬌"); System.out.println(o.toString()); } catch (ClassNotFoundException | NoSuchFieldException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } }
4、欄位賦值
1.
方法
// 獲取所有的”公有欄位” Field[] getFields() // 獲取所有欄位(私有、受保護、預設、公有) Field[] getDeclaredFields()
2.
舉個例子
public class TestReflectUserField { public static void main(String[] args) { try { Class<?> cls = Class.forName("com.wener.reflect.demo1.User"); Field[] fields = cls.getFields(); for (Field field : fields) { System.out.println("型別: " + field.getType() + "方法名: " + field.getName()); } Field[] declaredFields = cls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("型別: " + declaredField.getType() + "方法名: " + declaredField.getName()); } } catch (ClassNotFoundException | NoSuchFieldException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } }
3、獲取所有成員欄位
2、獲取單個成員欄位
類的成員變數也是一個物件,它是java.lang.reflect.Field的一個物件,所以我們透過java.lang.reflect.Field裡面封裝的方法來獲取這些資訊並且操作這些屬性
1、說明
四、屬性操作
1. cls.newInstance()方法返回的是一個泛型T,我們要強轉成自定義類
2. cls.newInstance()預設返回的是類的無引數構造物件
3. 被反射機制載入的類必須有無引數構造方法,否者執行會丟擲異常
1.4、注意
三、建立例項
1. 常用的是 類的靜態方法 ,
2. getClass()的話一般在繼承的時候用的比較多一點,比如Android裡的註解框架
3. .class 靜態語法: 需要匯入類的包,依賴太強,不導包就拋編譯錯誤
4、總結
3、使用類物件的 getClass()
2、使用 .class 靜態語法。
00001.
區別
第一個預設進行初始化操作,
第二個可以指定是否進行初始化操作。當
initialize=false
不進行初始化操作,即不會執行靜態程式碼塊。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69913864/viewspace-2701845/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 好程式設計師Java培訓Java程式設計師必學技術程式設計師Java
- 好程式設計師Java培訓分享Java程式設計技巧程式設計師Java
- 好程式設計師Java培訓分享Java程式設計師技能提升指南程式設計師Java
- 好程式設計師Java培訓分享註解與反射原理說明程式設計師Java反射
- 好程式設計師Java培訓分享SpringBoot -YAML程式設計師JavaSpring BootYAML
- 好程式設計師Java培訓分享MySQL算術運算子程式設計師JavaMySql
- Java程式設計師技術培訓需要培訓哪些?Java程式設計師
- 好程式設計師Java培訓分享Java面試題之Java集合篇三程式設計師Java面試題
- 好程式設計師Java培訓分享如何快速入門Java程式設計程式設計師Java
- 好程式設計師Java培訓分享如何快速入門Java程式設計師Java
- 好程式設計師Java培訓分享Java包是什麼?程式設計師Java
- 好程式設計師Java培訓分享Java多執行緒程式設計師Java執行緒
- 好程式設計師Java培訓分享maven-概述程式設計師JavaMaven
- 好程式設計師Java培訓分享BigDecimal的用法程式設計師JavaDecimal
- 好程式設計師Java培訓分享Java之命名、標示符、變數程式設計師Java變數
- 好程式設計師Java培訓分享Java程式設計師常用的工具類庫程式設計師Java
- 好程式設計師Java培訓分享20個Java程式設計師基礎題程式設計師Java
- 好程式設計師Java培訓分享Java EE與Java的區別程式設計師Java
- 好程式設計師Java培訓分享學Java程式設計要注意什麼程式設計師Java
- 好程式設計師Java培訓分享Java和HTML的區別?程式設計師JavaHTML
- 好程式設計師Java培訓分享Java物件導向概念解析程式設計師Java物件
- 好程式設計師Java培訓分享Java初學者必讀程式設計師Java
- 好程式設計師Java培訓分享SpringBoot -啟動流程程式設計師JavaSpring Boot
- 好程式設計師Java培訓分享For迴圈詳解程式設計師Java
- 好程式設計師Java培訓分享Spring Ioc的原理程式設計師JavaSpring
- 好程式設計師Java培訓分享SpringBoot入門篇程式設計師JavaSpring Boot
- 好程式設計師Java培訓分享本地快取如何設計程式設計師Java快取
- 好程式設計師Java培訓分享之RMI與RPC的區別程式設計師JavaRPC
- 好程式設計師Java培訓分享Java面試題集合篇一程式設計師Java面試題
- 好程式設計師Java培訓分享Java面試題集合篇二程式設計師Java面試題
- 好程式設計師Java培訓分享學習Java需要哪些基礎程式設計師Java
- 好程式設計師Java培訓分享面試Java的注意事項程式設計師Java面試
- 好程式設計師Java培訓分享Java中級面試題合集程式設計師Java面試題
- 好程式設計師Java培訓分享Java集合的兩種排序方法程式設計師Java排序
- 好程式設計師Java培訓分享Java讀寫Properties配置檔案程式設計師Java
- 好程式設計師Java培訓分享學Java需要什麼基礎?程式設計師Java
- 好程式設計師Java培訓分享Java基礎知識總結程式設計師Java
- 好程式設計師Java培訓分享Java效能常見命令有哪些程式設計師Java