一. 什麼是JAVA反射
java反射機制是在程序運行狀態中,對於任何一個類都可以知道這個類的屬性和方法。對於任何一個對象都可以調用它的任意一個方法。Java反射機制運行程序判斷分析任何一個類的結構,包括成員方法和變量,並調用任意一個對象的方法。java
二. JAVA反射的實例
在JDK中,主要由如下類來實現Java反射機制,這些類(除了第一個)都位於java.lang.reflect包中編程
Class類:表明一個類,位於java.lang包下。設計模式
Field類:表明類的成員變量(成員變量也稱爲類的屬性)。api
Method類:表明類的方法。數組
Constructor類:表明類的構造方法。app
Class類ide
Class類是用來保存運行時類型信息的類。每一個類(型)都有一個Class對象。運行程序時,Java虛擬機(JVM)首先檢查是否所要加載的類對應的Class對象是否已經加載。若是沒有加載,JVM就會根據類名查找.class文件,並將其Class對象載入。基本的 Java 類型(boolean、byte、char、short、int、long、float 和 double)和關鍵字 void 也都對應一個 Class 對象。通常當某個類的Class對象被載入內存時,它就能夠用來建立這個類的全部對象。this
經常使用的獲取Class對象的3種方式:url
1.使用Class類的靜態方法。例如: spa
Class.forName("java.lang.String");
2.使用類的.class語法。如:
String.class;
3.使用對象的getClass()方法。如:
String str = "aa";
Class<?> classType1 = str.getClass();
Field類
再來看看經過class對象來獲取類的屬性
private
String strName;
private
int
sum;
public
String strTest;
public
static
void
main(String[] args)
throws
NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
ReflectTest rTest=
new
ReflectTest();
Class<?> clsType = ReflectTest.
class
;
//得到當前類和父類中的public類型的全部屬性
Field[] fields=clsType.getFields();
for
(Field field:fields)
System.out.println(field);
}
|
經過反射來設置屬性值
public
class
ReflectTest {
private
String strName;
private
int
sum;
public
String strTest;
public
void
show(){
System.out.println(strName);
System.out.println(
""
+sum);
System.out.println(strTest);
}
public
static
void
main(String[] args)
throws
NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
ReflectTest rTest=
new
ReflectTest();
Class<?> clsType = ReflectTest.
class
;
//獲取三個屬性值
Field field1=clsType.getDeclaredField(
"strName"
);
Field field2=clsType.getDeclaredField(
"sum"
);
Field field3=clsType.getField(
"strTest"
);
//反射設置對象的屬性值
field1.set(rTest,
"james"
);
field2.set(rTest,
10
);
field3.set(rTest,
"reflect field"
);
rTest.show();
}
}
|
Method類
在獲取Class對象後,咱們還能夠獲取類所對應的方法,這時就要用到咱們的Method類
public
class
ReflectTest {
private
void
fun() {
System.out.println(
"this is fun()"
);
}
private
void
add(
int
a,
int
b) {
System.out.println(
"the sum is: "
+ (a + b) +
" "
);
}
public
static
void
main(String[] args) {
Class<?> clsType = ReflectTest.
class
;
// 返回class對象所對應的類或接口中,所聲明的全部方法的數組(包括私有方法)
Method[] methods = clsType.getDeclaredMethods();
// 打印出全部的方法名
for
(Method method : methods) {
System.out.println(method);
}
}
}
|
再來看看怎麼反射調用對象的方法
public
class
ReflectTest {
private
void
fun() {
System.out.println(
"this is fun()"
);
}
public
int
add(
int
a,
int
b) {
System.out.println(
"the sum is: "
+ (a + b) +
" "
);
return
a+b;
}
public
static
void
main(String[] args)
throws
NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
ReflectTest rTest=
new
ReflectTest();
Class<?> clsType = ReflectTest.
class
;
// 返回對應public類型的方法,第一個參數爲方法名,第二個參數爲方法的參數列表
Method method=clsType.getMethod(
"add"
,
new
Class<?>[]{
int
.
class
,
int
.
class
});
System.out.println(
""
+method.invoke(rTest,
new
Object[]{
5
,
6
}));
}
}
|
Constructor類
在獲取Class對象後,咱們就能夠經過它建立類的對象啦。這就地用到咱們的Constructor類啦。有三種建立對象的方式:
1.先得到Class對象,而後經過該Class對象的newInstance()方法直接生成便可:
Class<?> classType = StringBuffer.class;
Object obj = classType.newInstance();
2.固然也能夠再得到Class對象後,經過該Class對象得到類的Constructor對象,再經過該Constructor對象的newInstance()方法建立對象:
Class<?> clsType=StringBuffer.class;
// 得到Constructor對象,此處獲取一個無參數的構造方法的
Constructor<?> constructor=clsType.getConstructor(new Class<?>[]{});
// 經過Constructor對象的構造方法來生成一個對象
Object obj=constructor.newInstance(new Object[]{});
三、若是構造器帶有參數,那就不能有上面兩個方法來建立對象了,可以使用下面這一種方式:
Class<?> clsType = StringBuffer.class;
Constructor<?> constructor = clsType.getConstructor(new Class<?>[] { String.class });
Object obj = constructor.newInstance(new Object[] { "hello, classtest" });
三. JAVA反射的原理
Class.forName(classname)的執行過程:
其實是調用了Class類中的 Class.forName(classname, true, currentLoader)方法。參數:name - 所需類的徹底限定名;initialize - 是否必須初始化類;loader - 用於加載類的類加載器。currentLoader則是經過調用ClassLoader.getCallerClassLoader()獲取當前類加載器的。類要想使用,必須用類加載器加載,因此須要加載器。反射機制,不是每次都去從新反射,而是提供了cache,每次都會須要類加載器去本身的cache中查找,若是能夠查到,則直接返回該類。
java的類加載器,它分爲BootStrap Class Loader(引導類加載器),Extensions Class Loader (擴展類加載器),App ClassLoader(或System Class Loader),固然少不了Custom ClassLoader(用戶自定義類加載器)。其加載過程當中會先檢查類是否被已加載,檢查順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查,只要某個classloader已加載就視爲已加載此類,保證此類只全部ClassLoader加載一次。而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。
流程圖:
類加載器的類加載過程,先檢查本身是否已經加載過該類,若是加載過,則直接返回該類,若沒有則調用父類的loadClass方法,若是父類中沒有,則執行findClass方法去嘗試加載此類,也就是咱們一般所理解的片面的"反射"了。這個過程主要經過ClassLoader.defineClass方法來完成。defineClass 方法將一個字節數組轉換爲 Class 類的實例(任何類的對象都是Class類的對象)。這種新定義的類的實例須要使用 Class.newInstance 來建立,而不能使用new來實例化。
在運行期間,若是咱們要產生某個類的對象,Java虛擬機(JVM)會檢查該類型的Class對象是否已被加載。若是沒有被加載,JVM會根據類的名稱找到.class文件並加載它。一旦某個類型的Class對象已被加載到內存,就能夠用它來產生該類型的全部對象。
四. JAVA反射的應用-動態代理
說動態代理以前,必須先講講代理的設計模式。代理模式爲其餘對象提供一種代理以控制對這個對象的訪問。在某些狀況下,一個客戶不想或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。代理模式的有三個角色定義:
這就須要使用咱們的動態代理了。
動態代理(Dynamic Proxy)在系統運行時動態建立代理類,可讓系統可以根據實際須要來動態建立代理類,讓同一個代理類可以代理多個不一樣的真實主題類。動態代理是一種較爲高級的代理模式,它在事務管理、AOP(Aspect-OrientedProgramming,面向方面編程)等領域都發揮了重要的做用。Java語言提供了對動態代理的支持,在Java的動態代理機制中,有兩個重要的類或接口,一個是 InvocationHandler接口、另外一個則是 Proxy類。Proxy這個類的做用就是用來動態建立一個代理對象,一般使用newProxyInstance 方法建立代理對象。Proxy代理類動態建立代理對象都須要關聯到一個InvocationHandler接口,當咱們經過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由InvocationHandler這個接口的 invoke方法來進行調用。
動態代理的步驟
(1).建立一個實現接口InvocationHandler的類,它必須實現invoke方法
(2).建立被代理的類以及接口
(3).經過Proxy的靜態方法
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 建立一個代理
(4).經過代理調用方法
類圖
1
)抽象接口
public
interface
Subject
{
public
void
doSomething(String str);
}
2
)真實對象
public
class
RealSubject
implements
Subject
{
@Override
public
void
doSomething(String str)
{
// TODO Auto-generated method stub
System.out.println(
"do something: "
+ str);
}
}
3
)動態代理Handler類
public
class
MyInvocationHandler
implements
InvocationHandler
{
//被代理的對象
private
Object target=
null
;
public
MyInvocationHandler(Object obj)
{
this
.target=obj;
}
@Override
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable
{
// TODO Auto-generated method stub
return
method.invoke(target, args);
}
}
4
)場景類Client
public
class
Client
{
public
static
void
main(String[] args)
{
//real object
Subject subject=
new
RealSubject();
//handler
InvocationHandler handler=
new
MyInvocationHandler(subject);
//得到代理proxy
Subject proxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
System.out.println(proxy.getClass().getName());
proxy.doSomething(
"yes"
);
}
}
|