java反射机制+继承设计技巧
【0】README
0.1) 本文描述+源代碼均 轉自 core java volume 1, 旨在理解 java反射機制 ;最后還順帶提出了 繼承設計的技巧;
【1】反射相關
1)反射定義:能夠分析類能力的程序稱為反射;
2)反射機制可以用來:
- 在運行中分析類的能力;
- 在運行中查看對象, 如,編寫一個 toString() 方法供所有類使用;
- 實現通用的數組操作代碼;
- 利用 Method對象,這個對象很像 C++ 中的函數指針;
【2】Class 類
2.1) Java 運行時系統始終為 所有對象維護一個稱為運行時的類型標志;虛擬機 利用運行時類型信息選擇相應的方法執行;(干貨——Java 運行時系統始終為 所有對象維護一個稱為運行時的類型標志,對象在虛擬機中的運行時類信息保存在Class對象中)
2.2)Class: 可以通過專門的java類訪問這些信息, 保存這些信息的類被稱為 Class;Object.getClass() 將 返回一個Class 類型的實例;
2.3) 如同用一個Employee 對象表示一個特定的雇員屬性一樣, 一個 Class 對象將表示一個特定類的屬性;最常用的 Class 方法是 getName方法(類名);(Class類的定義 的良好詮釋)
2.4)還可以調用靜態方法forName 獲得類名對于的Class對象;
String className = "java.util.Date"; Class c1 = Class.forName(className);Hint)在啟動時,main方法需要加載所有的類, 對于一個大型的應用程序來說, 這將會消耗很多時間, 用戶會因此感到不耐煩;我們可以通過 Class.forName 手動地加載其他的類;
2.5)獲得Class 類對象的第3種方法:
1)第一種方法:
2)第二種方法:
String className = "java.util.Date"; Class c1 = Class.forName(className);3)第三種方法:
如果T是 java類型, T.class 將代表匹配的類對象, 如
Attention)一個 Class 對象實際上表示的是一個類型, 而這個類型未必一定是一種類;例如 int 不是類, 但 int.class 是一個Class類型的對象;(干貨——一個 Class 對象實際上表示的是一個類型, 而這個類型未必一定是一種類,如int不是類型,而int.class是一個Class類型的對象,那我們就可以人為 obj.class 是obj在jvm中運行時相關信息的封裝對象)
Annotation)從 Java SE 5.0 開始, Class類已經參數化了,如, Class 的類型是 Employee.class;
Warning)由于歷史原因, getName 應用于數組類型的時候會返回一個 很奇怪的名字:
Double[].class.getName() 返回 ”[Ljava.lang.Double“;
int[].class.getName() 返回 ”[I“;
2.6)虛擬機為每個類型管理一個 Class對象: 因此,可以用 == 運算符實現兩個類對象的比較操作;
2.7)newInstance()方法: 可以用來快速創建一個類的實例;.getClass().getInstance(); 它將創建一個與 e 具有相同類型的實例, newInstance 調用默認構造器, 初始化新創建的對象,如果這個類沒有默認的構造器,就會拋出一個異常;
Annotation)
- A1)如果需要以以上這種方式向構造器提供參數的話, 那么就不要使用上面那條語句,而使用 構造器類中的newInstance 方法;
- A2)(key)通過反射創建新類示例的兩種方式及比較: http://blog.csdn.net/fenglibing/article/details/4531033 , 我的荔枝博文: http://blog.csdn.net/PacosonSWJTU/article/details/49886429
【3】捕獲異常(未檢查異常+已檢查異常)
3.1)當程序運行過程中發生錯誤時, 就會拋出異常。拋出異常比終止程序要靈活得多, 這是因為可以提供一個捕獲異常的處理器(handler)對異常情況進行處理;
3.2)異常有兩種類型(注意區別):未檢查異常和已檢查異常;
- 3.2.1)對于已檢查異常: 編譯器將會檢查是否提供了處理器(try-cath or throws Exception);
- 3.2.2)對于未檢查異常: 例如訪問null引用,都屬于未檢查異常,編譯器不會查看是否為這些錯誤提供了處理器;
3.3)最簡單的異常處理器
try catch塊;3.4)如果類名不存在, 則將跳過try 塊中的剩余代碼, 程序直接進入catch 子句, 這里,利用Throwable 類的 printStackTrace 方法打印出棧的軌跡;
【4】利用反射分析類的能力
4.1)反射機制最重要的內容——檢查類的結構;
4.2)在java.lang.reflect 包中有3個類 Field、Method 和 Constructor 分別用于描述類的域、方法 和 構造器;
4.3)這3個類都有一個 getName的方法, 用來返回項目名稱
- 4.3.1)Field類: Field類有一個 getType方法, 用來返回描述域所屬類型的 Class對象;
- 4.3.2)Method 和 Constructor 類: Method 和 Constructor 類有能夠報告參數類型的方法,Method類還可以有一個報告返回類型的方法;
- 4.3.3) 這3個類還有一個叫做 getModifiers的方法,它將返回一個整型數值,用不同的位開關描述public 和 static這樣的修飾符使用狀況;
- 4.3.4) 而且,還可以用 java.lang.reflect 包中的Modifier類的靜態方法分析 getModifiers 返回的整型數值;如, 可以使用 Modifier 類中的 isPublic、isPrivate 或 isFinal 判斷方法或構造器 是否是 public, private , final;我們需要做的工作就是調用 Modifier 類的相應方法, 并對返回的整型數值進行分析;
4.4) Class類中的 getField、getMethod 和 getConstructor 方法將分別返回類提供的public 域、方法和構造器數值,其中包括超類的公有成員;
4.5) Class 類的 getDeclareFields、getDeclareMethods、getDeclaredConstructors 方法將分別返回類中聲明的全部域、方法和構造器;其中包括私有方法和受保護成員,但不包括超類成員;
【5】在運行時使用反射分析對象
5.1)本節將進一步查看數據域的實際內容;
5.2)利用反射機制可以查看在編譯時還不清楚的對象域;
5.3)查看對象域的關鍵方法是 Field類中的 get方法;如果 f 是一個 Field 類型的 對象, obj 是某個包含 f 域的類對象, f.get(obj) 將返回一個對象,其值為obj 域的當前值;
5.4)看個荔枝:
對以上代碼的分析(Analysis):
- A1)由于name 是一個 private, 所以get方法會拋出一個異常;
- A2)只有利用get 方法才能得到可訪問域的值;除非擁有訪問權限,否則 java 安全機制只允許查看任意對象有哪些域, 而不允許讀取他們的值;
5.5)引入setAccessible 方法(打印結果與5.4做比較,這個很有必要): 反射機制的默認行為受限于 Java的訪問控制, 然而,如果一個 java 程序沒有收到安全管理器的控制,就可以覆蓋訪問控制。為了達到這個目的,需要調用 Field、Method、Constructor對象 的 setAccessible 方法;如,f.setAccessible(true);
5.6)get方法 所遇到的問題 + 解決方法
- 5.6.1)get遇到的問題: name域是一個 String,因此把它作為 Object 返回沒有什么問題;但是,假定我們想要查看salary 域, 它屬于double 類型, 而java中數值類型不是對象;
- 5.6.2)解決方法:可以使用 Field 類中的 getDouble 方法,也可以調用get 方法, 此時, 反射機制將會自動地將這個域值打包到相應的對象包裝器中, 這里將打包成 Double;
5.6.3)當然, 可以獲得就可以設置; 調用 f.set(obj, value) 可以將 obj 對象的 f域設置成新值;
5.6.4)如何編寫一個可供任意類使用的通用 toString()方法;(P203,非常重要)
【6】使用反射編寫泛型數組代碼
6.1)java.lang.reflect 包中的Array 類允許動態地創建數組。例如, Array類中的copyOf 方法,用于擴充數組容量:
int[] a = new int[100]; a = Arrays.copyOf(a, 2*a.length());6.2)如何編寫這樣一個通用的方法?
- 6.2.1)將Employee[] 數組轉換為 Object[] 數組, 這讓人感覺很有希望;
- 6.2.2)使用以上代碼遇到的問題:一個對象數組不能轉換為 雇員數組(Employee[]);
Attention)
- A1) Java 數組會記住每個元素的類型, 即創建數組時 new 表達式中使用的元素類型。將一個 Employee[] 臨時轉換為 Object[] 數組, 然后再把它轉換回來是可以的, 但一個從開始就是Object[] 的數組卻永遠不會轉換成Employee[] 數組。所以, 為了能夠編寫這類通用的數組代碼, 需要能夠創建與 原數組類型相同的新數組;
- A2)所以,我們需要java.lang.reflect 包中Array類的一些方法。 其中最關鍵的是 Array 類中的靜態方法 newInstance, 它能夠構造新數組。在調用它時 必須提供兩個參數, 一個是數組的元素類型, 一個是數組的長度;
6.3)為了能夠實際運行, 需要獲得新數組的長度和元素類型: 可以通過 調用 Array.getLength(a) 獲得數組的長度, 也可以通過 Array類的靜態方法 getLength 方法的返回值得到任意數組 的長度。 而要獲得新數組元素類型, 就需要進行以下工作(Work):
- W1)首先獲得a 數組的類對象;
- W2)確認它是一個數組;
- W3)使用 Class類(只能定義表示數組的類對象)的 getComponentType 方法確定數組對應的類型;
【7】調用任意方法
7.1)在Method類中有一個invoke 方法, 它允許調用包裝在當前 Method 對象中的方法, invoke方法的簽名是: Object invoke(Object obj, Object… args), 如 String n = (String)m1.invoke(harry);
7.2)如何得到 Method 對象呢?
7.2.1)通過 getDeclaredMethods 方法 , 然后對返回的Method對象數組進行查找, 直到發現想要的方法為止;
7.2.2)通過調用 Class 類中的getMethod 方法得到想要的方法;它與 getField 方法類似, getField 方法根據表示域名的字符串, 返回一個 Field對象;然而,有可能存在著若干個相同名字的方法, 因此要格外小心, 以確保準確地得到想要的方法;所以, 我們需要提供想要方法的參數類型;
7.3)getMethod的方法簽名是: Method getMethod(String name, Class… parameterTypes) ;
7.4)看個荔枝(如何獲得 Employee類的 getName 方法 和 raiseSalary 方法的方法指針):
7.5)再看個荔枝
這里, f是一個Method類型的對象,由于正在調用的 方法是一個靜態方法,所以 invokle 的第一個參數是null;
為了將 Math.sqrt 函數表格化, 需要將f 設置為: Math.class.getMethod(“sqrt”, double.class);這是 Math類中的一個方法, 通過參數向它提供了一個函數名 sqrt 和 一個double類型參數;
對以上代碼的分析(Analysis):
- A1) invoke 的參數和返回值必須是 Object類型的, 這就意味著必須進行多次的類型轉換;
- A2)使用反射獲得方法指針的代碼要比僅僅直接調用方法明顯 慢一些;
Attention)
- A1) 建議僅在必要的時候才使用 Method對象,而最好使用接口和內部類;
- A2)特別重申: 建議 Java 開發者不要使用 Method 對象的回調功能, 使用接口進行回調會使得代碼的執行速度更快, 更易于維護;
【8】繼承設計的技巧
8.1)將公共操作和域放在超類;
8.2)不要使用受保護的域:
- 8.2.1)第一:子類集合是無限制的, 任何一個人都能夠由某個類派生一個子類,并編寫代碼以直接訪問 protected 的實例域, 從而破壞了封裝性;
- 8.2.2)第二:在java程序設計語言中,在同一個包中的所有類都可以訪問 protected域, 而不管它是否為這個類的子類;
8.3)使用繼承實現 “is-a” 關系;
8.4)除非所有繼承的方法都有意義, 否則不要使用繼承;
8.5)在覆蓋方法時, 不要改變預期的行為;
8.6)使用多態,而非類型信息:無論什么時候 , 碰到這種代碼,都應該使用 多態性;
8.7)不要過多地使用反射:
- 8.7.1)反射機制使得人們可以通過在運行時查看與核方法,讓個人編寫出更具有通用性的程序;這種功能對于編寫系統程序來說極為實用, 但是通常不適用于編寫應用程序;
- 8.7.2)反射是很脆弱的, 即編譯器很難幫助人們發現程序中的錯誤,因此只有在運行時才發現錯誤并導致異常;
總結
以上是生活随笔為你收集整理的java反射机制+继承设计技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 追风者推出新款 140mm D30-14
- 下一篇: 万网主机怎么远程(万网主机怎么远程开机)