java反射原來是這麼玩的(反射一開,誰都不愛)
反射的發展歷史
1996年01月23日,jdk 1。0版本釋出,代號為Oak(橡樹)。
這個代號為Oak(橡樹)的版本,在釋出後的第二年,1997年02月19日,釋出jdk 1。1版本,這次版本釋出中引入了
反射
機制。
關於反射機制,由於年代久遠,能搜尋到對於反射機制的記載少之又少,能找到最為久遠的是一篇題為《Using Java Reflection》的文章,發表於 1998年1月,文中提到:
反射是一個可以獲取java類、屬性的一個工具,因為它是動態載入的
。
而在另外一篇文章《A Button is a Bean》裡解釋道,反射是為了能把一個類的屬性視覺化的展示給使用者,如下圖所示:
通俗的解釋就是:無論是公有還是私有的方法、屬性、構造方法,全都可以用反射進行獲取、進行賦值、呼叫。聽到這個解釋,是不是感覺反射很強。
正因為反射的強大,在java世界裡運用的地方有很多,比如:Java類載入和初始化、Java中RTTI、Spring的IOC,。
如此廣泛的運用,只能說反射除了強,用起來肯定很爽。我想起我的同事,IT界的刁民,總是熱衷於反射。
他在講解他是如何運用反射時,嘴角總是壓抑不住的微笑,這種迷戀反射的樣子,像極了愛情。
正所謂:
反射一開,誰都不愛
。
下面就看看反射究竟是如何在程式中使用的。
反射的概述和使用
反射的概述
JAVA反射機制是在執行狀態中, 對於任意一個類,都能夠知道這個類的所有屬性和方法; 對於任意一個物件,都能夠呼叫它的任意一個方法和屬性; 這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。
我們知道class檔案是在編譯的時候生成的,Class物件是將class檔案讀入記憶體,併為之建立一個Class物件。
Class 沒有公共構造方法。Class 物件是在載入類時由 Java 虛擬機器以及透過呼叫類載入器中的defineClass 方法自動構造的。也就是這不需要我們自己去處理建立,JVM已經幫我們建立好了。
Class類裡面,包含了一個類應有的所有描述,包括: 欄位:Field。java 方法:Method。java 構造方法:Constructor。java 等等。。。
知道了Class類裡面包含了哪些內容之後,再看一下new一個物件的究竟會發生那些過程:
反射的使用
這裡使用一個Animal類來作為示範,可以看到這個類裡的成員變數、方法、構造方法的訪問修飾符既有public、也有private的。下面就將使用反射獲取不同修飾符修飾的成員變數、方法、構造方法。
package
com。shuai。ioc。ref
;
public
class
Animal
{
/**
* 動物名字
*/
public
String
name
;
/**
* 動物年齡
*/
protected
int
age
;
@Override
public
String
toString
()
{
return
“Animal{”
+
“name=‘”
+
name
+
’\‘’
+
“, age=”
+
age
+
‘}’
;
}
/**
* 預設的構造方法
*
* @param name
*/
Animal
(
String
name
)
{
System
。
out
。
println
(
“執行了”
+
“預設的構造方法 ”
+
name
);
}
/**
* 無參構造方法
*/
public
Animal
()
{
System
。
out
。
println
(
“執行了”
+
“無參構造方法 ”
);
}
/**
* 有一個引數的構造方法
*
* @param name
*/
public
Animal
(
char
name
)
{
System
。
out
。
println
(
“執行了”
+
“有一個引數的構造方法 name:”
+
name
);
}
/**
* 有多個引數的構造方法
*
* @param name
* @param age
*/
public
Animal
(
String
name
,
int
age
)
{
System
。
out
。
println
(
“執行了”
+
“有多個引數的構造方法 name:”
+
name
+
“age:”
+
age
);
}
/**
* protected的構造方法
*
* @param n
*/
protected
Animal
(
boolean
n
)
{
System
。
out
。
println
(
“執行了”
+
“受保護的構造方法 n:”
+
n
);
}
/**
* 私有構造方法
*
* @param age
*/
private
Animal
(
int
age
)
{
System
。
out
。
println
(
“執行了”
+
“私有構造方法 age:”
+
age
);
this
。
name
=
“私有構造方法呼叫成功”
;
this
。
age
=
age
;
}
/**
* 公有方法
*
* @param s
*/
public
void
public1
(
String
s
)
{
System
。
out
。
println
(
“呼叫了”
+
“公有的方法”
+
“: public1 , s:”
+
s
);
}
/**
* protected的方法
*/
protected
void
protected2
()
{
System
。
out
。
println
(
“呼叫了”
+
“protected的方法”
+
“: protected2 ”
);
}
/**
* 友好的方法
*/
void
friendly1
()
{
System
。
out
。
println
(
“呼叫了”
+
“友好的方法”
+
“: friendly1 ”
);
}
/**
* 私有方法
*
* @param age
* @return
*/
private
String
private1
(
int
age
)
{
System
。
out
。
println
(
“呼叫了”
+
“私有方法”
+
“: private1 ,age:”
+
age
);
return
age
+
“”
;
}
}
用反射獲取類的構造方法
在Class類中,提供一系列獲取被反射類構造方法的方法。
批次獲取構造方法的方法
public Constructor[] getConstructors()
:所有“公有的”構造方法
public Constructor[] getDeclaredConstructors()
:獲取所有的構造方法(包括私有、受保護、預設、公有)
獲取單個的方法,並呼叫
public Constructor getConstructor(Class。。。 parameterTypes)
:獲取單個的“公有的”構造方法
public Constructor getDeclaredConstructor(Class。。。 parameterTypes)
:獲取“某個構造方法”可以是私有的,或受保護、預設、公有;
呼叫構造方法
newInstance(Object。。。 initargs)
package
com。shuai。ioc。ref
;
import
com。shuai。ioc。Book
;
import
java。lang。reflect。Constructor
;
public
class
ConstructorsTest
{
public
static
void
main
(
String
[]
args
)
throws
Exception
{
//1。載入Class物件
Class
clazz
=
Class
。
forName
(
“com。shuai。ioc。ref。Animal”
);
//2。獲取所有公有構造方法
System
。
out
。
println
(
“所有公有構造方法”
);
Constructor
[]
conArray
=
clazz
。
getConstructors
();
for
(
Constructor
c
:
conArray
)
{
System
。
out
。
println
(
c
);
}
// 所有的構造方法,公有、私有都行
System
。
out
。
println
(
“”
);
System
。
out
。
println
(
“所有的構造方法,包括:私有、受保護、預設、公有”
);
conArray
=
clazz
。
getDeclaredConstructors
();
for
(
Constructor
c
:
conArray
)
{
System
。
out
。
println
(
c
);
}
// 獲取公有、無參的構造方法
System
。
out
。
println
(
“”
);
System
。
out
。
println
(
“獲取公有、無參的構造方法”
);
Constructor
con
=
clazz
。
getConstructor
(
null
);
System
。
out
。
println
(
“con = ”
+
con
);
//呼叫構造方法
Object
obj
=
con
。
newInstance
();
// 獲取私有構造方法
System
。
out
。
println
(
“”
);
System
。
out
。
println
(
“獲取私有構造方法,並呼叫”
);
con
=
clazz
。
getDeclaredConstructor
(
int
。
class
);
System
。
out
。
println
(
con
);
//暴力訪問,忽略掉訪問修飾符
con
。
setAccessible
(
true
);
//呼叫構造方法
Animal
animal
=
(
Animal
)
con
。
newInstance
(
1
);
System
。
out
。
println
(
animal
。
toString
());
}
}
用反射獲取類的方法
在Class類中,提供一系列獲取被反射類構造方法的方法。
批次的
public Method[] getMethods()
:獲取所有“公有方法”;(包含了父類的方法也包含Object類)
public Method[] getDeclaredMethods()
:獲取所有的成員方法,包括私有的(不包括繼承的)
獲取單個的
public Method getMethod(String name,Class<?>。。。 parameterTypes)
,
name
: 方法名;
Class 。。。
:形參的Class型別物件
public Method getDeclaredMethod(String name,Class<?>。。。 parameterTypes)
,
obj
:要呼叫方法的物件;
args
:呼叫方式時所傳遞的實參;
呼叫方法
public Object invoke(Object obj,Object。。。 args)
,
obj
:要呼叫方法的物件;
args
:呼叫方式時所傳遞的實參;
package
com。shuai。ioc。ref
;
import
java。lang。reflect。Method
;
public
class
MethodClassTest
{
public
static
void
main
(
String
[]
args
)
throws
Exception
{
//1。獲取Class物件
Class
stuClass
=
Class
。
forName
(
“com。shuai。ioc。ref。Animal”
);
//2。獲取所有公有方法
System
。
out
。
println
(
“獲取所有 公有 方法”
);
stuClass
。
getMethods
();
Method
[]
methodArray
=
stuClass
。
getMethods
();
for
(
Method
m
:
methodArray
)
{
System
。
out
。
println
(
m
);
}
System
。
out
。
println
();
System
。
out
。
println
(
“獲取所有的方法,包括私有的”
);
methodArray
=
stuClass
。
getDeclaredMethods
();
for
(
Method
m
:
methodArray
)
{
System
。
out
。
println
(
m
);
}
System
。
out
。
println
();
System
。
out
。
println
(
“獲取公有的public1()方法”
);
Method
m
=
stuClass
。
getMethod
(
“public1”
,
String
。
class
);
System
。
out
。
println
(
m
);
//例項化一個Student物件
Object
obj
=
stuClass
。
getConstructor
()。
newInstance
();
m
。
invoke
(
obj
,
“this is name value”
);
System
。
out
。
println
();
System
。
out
。
println
(
“獲取私有的private1()方法”
);
m
=
stuClass
。
getDeclaredMethod
(
“private1”
,
int
。
class
);
System
。
out
。
println
(
m
);
m
。
setAccessible
(
true
);
//解除私有限定
Object
result
=
m
。
invoke
(
obj
,
20
);
//需要兩個引數,一個是要呼叫的物件(獲取有反射),一個是實參
System
。
out
。
println
(
“返回值:”
+
result
);
}
}
用反射獲取類的欄位
在Class類中,提供一系列獲取被反射類構造方法的方法。
批次的
Field[] getFields()
:獲取所有的“公有欄位”
Field[] getDeclaredFields()
:獲取所有欄位,包括:私有、受保護、預設、公有;
獲取單個的
public Field getField(String fieldName)
:獲取某個“公有的”欄位;
public Field getDeclaredField(String fieldName)
:獲取某個欄位(可以是私有的)
設定欄位的值
public void set(Object obj,Object value)
:
obj
:要設定的欄位所在的物件;
value
:要為欄位設定的值;
package
com。shuai。ioc。ref
;
import
java。lang。reflect。Field
;
public
class
FieldsTest
{
public
static
void
main
(
String
[]
args
)
throws
Exception
{
//1。獲取Class物件
Class
animalClass
=
Class
。
forName
(
“com。shuai。ioc。ref。Animal”
);
//2。獲取欄位
System
。
out
。
println
(
“獲取所有公有的欄位”
);
Field
[]
fieldArray
=
animalClass
。
getFields
();
for
(
Field
f
:
fieldArray
)
{
System
。
out
。
println
(
f
);
}
System
。
out
。
println
();
System
。
out
。
println
(
“獲取所有的欄位(包括私有、受保護、預設的)”
);
fieldArray
=
animalClass
。
getDeclaredFields
();
for
(
Field
f
:
fieldArray
)
{
System
。
out
。
println
(
f
);
}
System
。
out
。
println
();
System
。
out
。
println
(
“獲取公有欄位並呼叫”
);
Field
f
=
animalClass
。
getField
(
“name”
);
System
。
out
。
println
(
f
);
//獲取一個物件
Object
obj
=
animalClass
。
getConstructor
()。
newInstance
();
//產生Student物件——》Student stu = new Student();
//為欄位設定值
f
。
set
(
obj
,
“dog”
);
//為Student物件中的name屬性賦值——》stu。name = “劉德華”
//驗證
Animal
stu
=
(
Animal
)
obj
;
System
。
out
。
println
(
“驗證name:”
+
stu
。
name
);
System
。
out
。
println
();
System
。
out
。
println
(
“獲取私有欄位並呼叫”
);
f
=
animalClass
。
getDeclaredField
(
“name”
);
System
。
out
。
println
(
f
);
f
。
setAccessible
(
true
);
//暴力反射,解除私有限定
f
。
set
(
obj
,
“this is name value”
);
System
。
out
。
println
(
“驗證name:”
+
stu
);
}
}
反射越過泛型檢查
編寫程式碼時,如果我們設定容器list為String型別,在呼叫add方法插入資料時入參傳了其他型別,編譯時會無法成功,但是透過反射卻可以執行,例項程式碼:
package
com。shuai。ioc。ref
;
import
java。lang。reflect。Method
;
import
java。util。ArrayList
;
import
java。util。List
;
/*
* 透過反射越過泛型檢查
*
*/
public
class
IgnoreType
{
public
static
void
main
(
String
[]
args
)
throws
Exception
{
List
<
String
>
list
=
new
ArrayList
<>();
list
。
add
(
“one”
);
//反射獲取list物件
Class
listClass
=
list
。
getClass
();
// 呼叫list物件的add方法
Method
m
=
listClass
。
getMethod
(
“add”
,
Object
。
class
);
m
。
invoke
(
list
,
100
);
//輸出驗證
for
(
Object
obj
:
list
)
{
System
。
out
。
println
(
obj
);
}
}
}
歡迎關注我的公眾號:java之旅。