剛開始學習java的時候真的很難理解反射到底是什麼東西
有些書籍,即使是很經典的書籍都解釋的讓人感覺懵懵的,或許的確是我太笨
況且,網路上說將來學習框架的時候需要經常應用到反射機制,這樣一來總讓人心裡有些不安
就方才偶然又把講解反射的章節和影片看了一點,覺得能理解一些了
現在決定一鼓作氣,邊看邊寫,順便把一些主要的內容和操作都記載到這裡
我想,對我這麼一個笨笨的人來說,學習的最好方法也許就是不斷重複
遇到不懂的知識就停下來把以往的重新學一遍,雖然浪費了很多時間,但對我來說也有些效果
我的理解是:所謂反射,就是根據一個已經實例化了的物件來還原類別的完整訊息
至少對我而言,我認為它帶給我的好處是,讓我從下往上的又了解了一次面向對象
x_x 在此又痛恨一邊那些厚部頭們,把我的腦細胞搞死一片
Class類如果要完成反射,那麼就必須了解Class類
實例1:透過物件取得包名和類別名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( )}
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);
}
}
Class類別中有一個方法叫做newInstance( ),它可以用來建立一個Class類別物件的新實例
怎麼說呢? Class物件包含的內容就是反射好的那個類,我們要建構那個類別的新實例(新物件)
實例3:Class類別的無參構造對象 //產生一個字串的引用
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類別的有參構造對象 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:取得類別的建構方法 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));
}
}
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));
}
}
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]);
}
}
}
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:取得本類中屬性的值 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);
}
}
坦白說,java學到現在我還沒發現什麼能亮瞎我鈦金眼的知識在裡邊
每次都是寫一堆繁瑣的語法實作個小玩意兒,不然就是拼命呼叫API,拼命的拋異常
讓本身顯得不夠緊湊的程式碼變得愈發累贅
如果我喜歡一門語言,在我利用它做出東西來之前,它本身的特性必須能夠打動我
顯然,java並不讓我快樂,也許很多程式設計師跟我一樣是被迫使用java的
僅以此來安撫我那顆孤獨編碼的心,以下接著看內容
反射的應用實例11:透過反射修改屬性 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);
}
}
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:透過反射操作數組 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));
}
}
暫時寫這麼多,我看的書中還有反射在工廠模式的應用
無非是用forName()方法替換一下,沒什麼好說的
我是個java初級黑,我恨java那種噁心的文法和設計
這都是為了Android,為了打基礎,為了適應以後的工作