剛開始學習java的時候真的很難理解反射到底是個什么東西
一些書籍,哪怕是很經典的書籍都解釋的讓人感覺懵懵的,或許的確是我太笨
況且,網上說在將來學習框架的時候需要經常應用到反射機制,這樣一來總讓人心里有些不安
就方才偶然又把講解反射的章節和視頻看了一點,覺得能理解一些了
現在決定一鼓作氣,邊看邊寫,順便把一些主要的內容和操作都記載到這里
我想,對於我這么一個笨笨的人來說,學習的最好方法也許就是不斷重復
遇到不懂的知識就停下來把以往的重新學一遍,雖然浪費了很多時間,但對我也有些效果
我的理解是:所謂反射,就是根據一個已經實例化了的對象來還原類的完整信息
至少對我而言,我認為它帶給我的好處是,讓我從下往上的又了解了一遍面向對象
x_x 在此又痛恨一邊那些厚部頭們,把我的腦細胞搞死一片
Class類
如果要完成反射,那么必須了解Class類
實例1:通過對象取得包名和類名
package org.siu; class Test { } public class Demo { public static void main(String[] args) { Test t = new Test(); System.out.println(t.getClass()); System.out.println(t.getClass().getName()); } }
編譯結果如下,注意包的編譯方式即可
此處的getClass()方法是默認繼承自Object類的
在java中,Object類是所有類的父類,同樣,所有類的實例化對象也都是Class類的實例
因此,這樣一來就會牽扯到向上轉型和向下轉型的概念
由於向下轉型的不安全因素,在這里泛型也會接踵而來
(不過我想說的是,此處的泛型設計很刺眼!尼瑪,整個java的語法設計同樣刺眼,超惡心!!!)
實例2:Class類的實例化
由於Class類沒有構造方法,所以實例化Class類的方式有點特殊,有三種方式:
- 對象.getClass( )
- 類.Class
- forName( )
class Test { } public class Demo { public static void main(String[] args) { //方式一: Test t = new Test(); Class<? extends Test> c1 = t.getClass(); System.out.println(c1); //方式二: //為了避免特殊性,這里不用Test類,而用java庫中的String類 Class<String> c2 = String.class; System.out.println(c2); //方式三: //forName()方法會拋出異常 Class<?> c3 = null; try { c3 = Class.forName("Test"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(c3); } }
其中,forName( )方法需要重點掌握,因為它可以在類不確定的情況下實例化Class,更具靈活性
Class類的應用
Class類中有一個方法叫做newInstance( ),它可以用來創建一個Class類對象的新實例
怎么說呢?Class對象包含的內容就是反射好的那個類,我們要構造那個類的新實例(新對象)
實例3:Class類的無參構造對象
public class Demo { public static void main(String[] args) { //實例化Class對象,forName()方法會拋異常 Class<?> c = null; try { //這里需要完整的包名和類名 c = Class.forName("java.lang.String"); } catch (ClassNotFoundException e) { e.printStackTrace(); } //生成一個字符串的引用 String s = null; try { //將構造好的對象向下轉型為String類 //newInstance()方法會拋異常 s = (String) c.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } System.out.println("字符串長度: " + s.length()); } }
這樣就通過無參數的形式構造了一個新的對象,如同正常模式中
通過無參構造方法來構造新對象一樣
我們知道,類中除了有無參構造方法,還會存在有參數的構造方法
那在反射中如何通過有參數的形式構造對象呢?接着看
實例4:Class類的有參構造對象
import java.lang.reflect.Constructor; public class Demo { //下面的幾個方法拋出來的異常太多,為了代碼的緊湊性,這里就直接拋給虛擬機了 public static void main(String[] args) throws Exception { Class<?> c = null; try { c = Class.forName("java.lang.String"); } catch (ClassNotFoundException e) { e.printStackTrace(); } char[] ch = {'h','e','l','l','o'}; String s = null; //獲得Class類對象的有參構造方法,括號里面參數的寫法是:類型.class Constructor<?> con = c.getConstructor(char[].class); //用此構造方法構造一個新的字符串對象,參數為一個char數組 s = (String) con.newInstance(ch); System.out.println("構造的字符串:" + s); } }
我們還是使用String類做例,因為String類用的比較多,便於理解
這里需要注意的是,構造方法需要使用getConstructor( )方法獲得
至於參數類型則是:原有類型.class
還有一點,無論是有參還是無參,這里所使用的構造方法,原本的類里面必須對應存在
那么,如何才能知道原有類里面的構造方法,普通方法,繼承的父類等詳細信息呢?接着看
獲取類的結構
要通過反射獲取類的結構我們這里要導入一個新的包java.lang.reflect
實例5:取得類的構造方法
import java.lang.reflect.Constructor; import java.util.Arrays; public class Demo { //下面的幾個方法拋出來的異常太多,為了代碼的緊湊性,這里就直接拋給虛擬機了 public static void main(String[] args) throws Exception { Class<?> c = null; try { c = Class.forName("java.lang.Boolean"); } catch (ClassNotFoundException e) { e.printStackTrace(); } //這里的getConstructors()方法返回的是一個Constructor數組 Constructor<?>[] cons = c.getConstructors(); //打印的方式你可以自己寫,為了方便我用Arrays.toString(),湊合着看 System.out.println(Arrays.toString(cons)); } }
我選擇了Boolean類來做例,因為Boolean類的構造方法就兩個,方便看
實例6:取得類所實現的接口
import java.util.Arrays; public class Demo { public static void main(String[] args) throws Exception { Class<?> c = null; try { c = Class.forName("java.lang.Boolean"); } catch (ClassNotFoundException e) { e.printStackTrace(); } Class<?>[] in = c.getInterfaces(); System.out.println(Arrays.toString(in)); } }
沒什么好說的,看結果
實例7:取得父類
public class Demo { public static void main(String[] args) throws Exception { Class<?> c = null; try { c = Class.forName("java.lang.Boolean"); } catch (ClassNotFoundException e) { e.printStackTrace(); } //注意了,這里不會是數組,why? Class<?> su = c.getSuperclass(); System.out.println(su); } }
別忘了,java中是單繼承,父類只有一個
實例8:取得類的全部方法
import java.lang.reflect.Method; public class Demo { public static void main(String[] args) throws Exception { Class<?> c = null; try { c = Class.forName("java.lang.Boolean"); } catch (ClassNotFoundException e) { e.printStackTrace(); } Method[] m = c.getMethods(); //好吧,這次我就大發慈悲的寫個打印列表出來 for (int i = 0; i < m.length; i++) { System.out.println(m[i]); } } }
截取一部分,看看,意思下就行了……這幾個例子都比較簡單
實例9:取得本類的全部屬性
import java.lang.reflect.Field; class Person { private String name; private int age; } public class Demo { public static void main(String[] args) throws Exception { Class<?> c = null; try { c = Class.forName("Person"); } catch (ClassNotFoundException e) { e.printStackTrace(); } Field[] f = c.getDeclaredFields(); for (int i = 0; i < f.length; i++) { System.out.println(f[i]); } } }
getDeclaredFielsd()方法可以獲取全部屬性,getFields()只能獲取公共屬性
實例10:獲取本類中屬性的值
import java.lang.reflect.Field; class Person { public String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } } public class Demo { public static void main(String[] args) throws Exception { Person p = new Person("zhangsan",12); Class<?> c = p.getClass(); //獲取公共屬性的值 Field f1 = c.getField("name"); //get(p)表明要獲取是哪個對象的值 String str = (String) f1.get(p); System.out.println("姓名: " + str); //獲取私有屬性的值 Field f2 = c.getDeclaredField("age"); //age是私有屬性,所以要設置安全檢查為true f2.setAccessible(true); int age = (int) f2.get(p); System.out.println("年齡: " + age); } }
要注意的是:setAccessible()方法可以設置是否訪問和修改私有屬性
坦白說,java學到現在我還沒發現什么能亮瞎我鈦金眼的知識在里邊
每次都是寫一堆繁瑣的語法實現個小玩意兒,不然就是拼命調用API,拼命的拋異常
讓本身顯得不夠緊湊的代碼變得愈發累贅
如果我喜歡一門語言,在我利用它做出東西來之前,它本身的特性必須能夠打動我
顯然,java並不讓我快樂,也許很多程序員跟我一樣是被迫使用java的
僅以此來安撫我那顆孤獨編碼的心,下面接着看內容
反射的應用
實例11:通過反射修改屬性
import java.lang.reflect.Field; class Person { private String name; public Person(String name) { this.name = name; } public String toString() { return "姓名: " + this.name; } } public class Demo { public static void main(String[] args) throws Exception { Person p = new Person("王二狗"); System.out.println(p); Class<?> c = p.getClass(); //定義要修改的屬性 Field f = c.getDeclaredField("name"); f.setAccessible(true); //修改屬性,傳入要設置的對象和值 f.set(p, "張二蛋"); System.out.println(p); } }
幾個方法都是有聯系的,如果看不懂就先熟悉上面幾個例子
實例12:通過反射調用方法
import java.lang.reflect.Method; class Person { public void print(int i) { System.out.println("我在寫數字: " + i); } public static void say(String str) { System.out.println("我在說: " + str); } } public class Demo { public static void main(String[] args) throws Exception { Person p = new Person(); Class<?> c = p.getClass(); //getMethod()方法需要傳入方法名,和參數類型 Method m1 = c.getMethod("print", int.class); //invoke()表示調用的意思,需要傳入對象和參數 m1.invoke(p, 10); Method m2 = c.getMethod("say", String.class); //這里的null表示不由對象調用,也就是靜態方法 m2.invoke(null, "你妹"); } }
這里演示了一個普通的有參方法和一個靜態方法
既然有參數的都寫出來了,那么無參的就更簡單了,直接傳入一個對象即可
實例13:通過反射操作數組
import java.lang.reflect.Array; public class Demo { public static void main(String[] args) throws Exception { int[] arr = {1,2,3,4,5}; Class<?> c = arr.getClass().getComponentType(); System.out.println("數組類型: " + c.getName()); int len = Array.getLength(arr); System.out.println("數組長度: " + len); System.out.print("遍歷數組: "); for (int i = 0; i < len; i++) { System.out.print(Array.get(arr, i) + " "); } System.out.println(); //修改數組 System.out.println("修改前的第一個元素: " + Array.get(arr, 0)); Array.set(arr, 0, 3); System.out.println("修改后的第一個元素: " + Array.get(arr, 0)); } }
這里要注意一點,getComponentType( )返回的是數組元素的Class
暫時就寫這么多,我看的書中還有反射在工廠模式中的應用
無非是用forName()方法替換一下,沒什么可說的
我是個java初級黑,我恨java那種惡心的語法和設計
這都是為了Android,為了打基礎,為了適應以后的工作
Fuck java……