java中的反射總結
Java中的反射總結
剛開始學習java的時候真的很難理解反射到底是個什麼東西
一些書籍,哪怕是很經典的書籍都解釋的讓人感覺懵懵的,或許的確是我太笨
況且,網上說在將來學習框架的時候需要經常應用到反射機制,這樣一來總讓人心裏有些不安
就方纔偶然又把講解反射的章節和視頻看了一點,覺得能理解一些了
現在決定一鼓作氣,邊看邊寫,順便把一些主要的內容和操作都記載到這裏
我想,對於我這麼一個笨笨的人來說,學習的最好方法也許就是不斷重複
遇到不懂的知識就停下來把以往的重新學一遍,雖然浪費了很多時間,但對我也有些效果
我的理解是:所謂反射,就是根據一個已經實例化了的對象來還原類的完整信息
至少對我而言,我認爲它帶給我的好處是,讓我從下往上的又瞭解了一遍面向對象
x_x 在此又痛恨一邊那些厚部頭們,把我的腦細胞搞死一片
Class類
如果要完成反射,那麼必須瞭解Class類
實例1:通過對象取得包名和類名
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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( )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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類的無參構造對象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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類的有參構造對象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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:取得類的構造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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:取得類所實現的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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:取得父類
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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:取得類的全部方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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:取得本類的全部屬性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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:獲取本類中屬性的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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:通過反射修改屬性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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:通過反射調用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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:通過反射操作數組
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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