剛開始學習java的時候真的很難理解反射究竟是個什麼東西java
一些書籍,哪怕是很經典的書籍都解釋的讓人感受懵懵的,或許的確是我太笨程序員
何況,網上說在未來學習框架的時候須要常常應用到反射機制,這樣一來總讓人內心有些不安數組
就方纔偶然又把講解反射的章節和視頻看了一點,以爲能理解一些了安全
如今決定一氣呵成,邊看邊寫,順便把一些主要的內容和操做都記載到這裏框架
我想,對於我這麼一個笨笨的人來講,學習的最好方法也許就是不斷重複學習
遇到不懂的知識就停下來把以往的從新學一遍,雖然浪費了不少時間,但對我也有些效果this
個人理解是:所謂反射,就是根據一個已經實例化了的對象來還原類的完整信息編碼
至少對我而言,我認爲它帶給個人好處是,讓我從下往上的又瞭解了一遍面向對象spa
x_x 在此又痛恨一邊那些厚部頭們,把個人腦細胞搞死一片設計
若是要完成反射,那麼必須瞭解Class類
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的語法設計一樣刺眼,超噁心!!!)
因爲Class類沒有構造方法,因此實例化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
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類中有一個方法叫作newInstance( ),它能夠用來建立一個Class類對象的新實例
怎麼說呢?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());
}
}
|
這樣就經過無參數的形式構造了一個新的對象,如同正常模式中
經過無參構造方法來構造新對象同樣
咱們知道,類中除了有無參構造方法,還會存在有參數的構造方法
那在反射中如何經過有參數的形式構造對象呢?接着看
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
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類的構造方法就兩個,方便看
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));
}
}
|
沒什麼好說的,看結果
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中是單繼承,父類只有一個
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]);
}
}
}
|
截取一部分,看看,意思下就好了……這幾個例子都比較簡單
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()只能獲取公共屬性
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的
僅以此來安撫我那顆孤獨編碼的心,下面接着看內容
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);
}
}
|
幾個方法都是有聯繫的,若是看不懂就先熟悉上面幾個例子
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
,
"你妹"
);
}
}
|
這裏演示了一個普通的有參方法和一個靜態方法
既然有參數的都寫出來了,那麼無參的就更簡單了,直接傳入一個對象便可
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
暫時就寫這麼多,我看的書中還有反射在工廠模式中的應用
無非是用forName()方法替換一下,沒什麼可說的
我是個java初級黑,我恨java那種噁心的語法和設計
這都是爲了Android,爲了打基礎,爲了適應之後的工做
Fuck java……