生活随笔
收集整理的這篇文章主要介紹了
反射及代理
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一、反射的概述和初體驗(yàn)
Reflection(反射)是被視為動(dòng)態(tài)語言的關(guān)鍵,反射機(jī)制允許程序在執(zhí)行期借助于Reflection API取得任何類的內(nèi)部信息,并能直接操作任意對(duì)象的內(nèi)部屬性及方法。
體會(huì)反射機(jī)制的“動(dòng)態(tài)性”
@Test
public void test2 ( ) { for ( int i
= 0 ; i
< 100 ; i
++ ) { int num
= new Random ( ) . nextInt ( 3 ) ; String classPath
= "" ; switch ( num
) { case 0 : classPath
= "java.util.Date" ; break ; case 1 : classPath
= "java.lang.Object" ; break ; case 2 : classPath
= "com.atguigu.java.Person" ; break ; } try { Object obj
= getInstance ( classPath
) ; System
. out
. println ( obj
) ; } catch ( Exception e
) { e
. printStackTrace ( ) ; } }
}
public Object
getInstance ( String classPath
) throws Exception
{ Class
clazz = Class
. forName ( classPath
) ; return clazz
. newInstance ( ) ;
}
反射機(jī)制能提供的功能
import org
. junit
. Test
; import java
. lang
. annotation
. ElementType
;
import java
. lang
. reflect
. Constructor
;
import java
. lang
. reflect
. Field
;
import java
. lang
. reflect
. Method
;
public class ReflectionTest { @Test public void test1 ( ) { Person p1
= new Person ( "Tom" , 12 ) ; p1
. age
= 10 ; System
. out
. println ( p1
. toString ( ) ) ; p1
. show ( ) ; } @Test public void test2 ( ) throws Exception
{ Class
clazz = Person
. class ; Constructor cons
= clazz
. getConstructor ( String
. class , int . class ) ; Object obj
= cons
. newInstance ( "Tom" , 12 ) ; Person p
= ( Person
) obj
; System
. out
. println ( p
. toString ( ) ) ; Field age
= clazz
. getDeclaredField ( "age" ) ; age
. set ( p
, 10 ) ; System
. out
. println ( p
. toString ( ) ) ; Method show
= clazz
. getDeclaredMethod ( "show" ) ; show
. invoke ( p
) ; System
. out
. println ( "*******************************" ) ; Constructor cons1
= clazz
. getDeclaredConstructor ( String
. class ) ; cons1
. setAccessible ( true ) ; Person p1
= ( Person
) cons1
. newInstance ( "Jerry" ) ; System
. out
. println ( p1
) ; Field name
= clazz
. getDeclaredField ( "name" ) ; name
. setAccessible ( true ) ; name
. set ( p1
, "HanMeimei" ) ; System
. out
. println ( p1
) ; Method showNation
= clazz
. getDeclaredMethod ( "showNation" , String
. class ) ; showNation
. setAccessible ( true ) ; String nation
= ( String
) showNation
. invoke ( p1
, "中國" ) ; System
. out
. println ( nation
) ; } @Test public void test3 ( ) throws ClassNotFoundException
{ Class
clazz1 = Person
. class ; System
. out
. println ( clazz1
) ; Person p1
= new Person ( ) ; Class
clazz2 = p1
. getClass ( ) ; System
. out
. println ( clazz2
) ; Class
clazz3 = Class
. forName ( "com.atguigu.java.Person" ) ;
System
. out
. println ( clazz3
) ; System
. out
. println ( clazz1
== clazz2
) ; System
. out
. println ( clazz1
== clazz3
) ; ClassLoader classLoader
= ReflectionTest
. class . getClassLoader ( ) ; Class
clazz4 = classLoader
. loadClass ( "com.atguigu.java.Person" ) ; System
. out
. println ( clazz4
) ; System
. out
. println ( clazz1
== clazz4
) ; } @Test public void test4 ( ) { Class
c1 = Object
. class ; Class
c2 = Comparable
. class ; Class
c3 = String
[ ] . class ; Class
c4 = int [ ] [ ] . class ; Class
c5 = ElementType
. class ; Class
c6 = Override
. class ; Class
c7 = int . class ; Class
c8 = void . class ; Class
c9 = Class
. class ; int [ ] a
= new int [ 10 ] ; int [ ] b
= new int [ 100 ] ; Class
c10 = a
. getClass ( ) ; Class
c11 = b
. getClass ( ) ; System
. out
. println ( c10
== c11
) ; }
}
疑問1:通過直接new的方式或反射的方式都可以調(diào)用公共的結(jié)構(gòu),開發(fā)中到底用那個(gè)? 建議:直接new的方式。 什么時(shí)候會(huì)使用:反射的方式。 反射的特征:動(dòng)態(tài)性 答:當(dāng)在編譯的時(shí)候就可以確定創(chuàng)建哪個(gè)對(duì)象,此時(shí)就可以使用new的方式來創(chuàng)建對(duì)象, 如果在編譯時(shí)不知道創(chuàng)建什么對(duì)象,比如說我們后端代碼已經(jīng)跑起來部署到服務(wù)器了, 此時(shí)前端發(fā)送請(qǐng)求來調(diào)用后端接口, 此時(shí)前端具體要操作什么,創(chuàng)建什么對(duì)象, 后端接收到請(qǐng)求后,才回動(dòng)態(tài)的創(chuàng)建這個(gè)對(duì)象; 此時(shí)使用反射就更好,體現(xiàn)了動(dòng)態(tài)性! 疑問2:反射機(jī)制與面向?qū)ο笾械姆庋b性是不是矛盾的?如何看待兩個(gè)技術(shù)? 答:不矛盾。比如說單例模式,構(gòu)造器已經(jīng)私有化起來了,不建議我們?nèi)?chuàng)建對(duì)象,已經(jīng)提供了一個(gè)對(duì)外創(chuàng)建對(duì)象的公共方法; 可能比我們自己要?jiǎng)?chuàng)建對(duì)象的操作要更好,但是如果你非要自己去通過反射來調(diào)用私有構(gòu)造器來創(chuàng)建對(duì)象,也是允許的!
二、Class類的理解與獲取Class的實(shí)例
Class類的理解 類的加載過程: 程序經(jīng)過javac.exe命令以后,會(huì)生成一個(gè)或多個(gè)字節(jié)碼文件(.class結(jié)尾)。接著我們使用java.exe命令對(duì)某個(gè)字節(jié)碼文件進(jìn)行解釋運(yùn)行。相當(dāng)于將某個(gè)字節(jié)碼文件加載到內(nèi)存中。此過程就稱為類的加載。加載到內(nèi)存中的類,我們就稱為運(yùn)行時(shí)類,此運(yùn)行時(shí)類,就作為Class的一個(gè)實(shí)例。 換句話說,Class的實(shí)例就對(duì)應(yīng)著一個(gè)運(yùn)行時(shí)類。 加載到內(nèi)存中的運(yùn)行時(shí)類,會(huì)緩存一定的時(shí)間。在此時(shí)間之內(nèi),我們可以通過不同的方式來獲取此運(yùn)行時(shí)類。 獲取Class實(shí)例的幾種方式(前三種方式需要掌握)
Class
clazz1 = Person
. class ; System
. out
. println ( clazz1
) ; Person p1
= new Person ( ) ; Class
clazz2 = p1
. getClass ( ) ; System
. out
. println ( clazz2
) ; Class
clazz3 = Class
. forName ( "com.atguigu.java.Person" ) ;
System
. out
. println ( clazz3
) ; System
. out
. println ( clazz1
== clazz2
) ; System
. out
. println ( clazz1
== clazz3
) ; ClassLoader classLoader
= ReflectionTest
. class . getClassLoader ( ) ; Class
clazz4 = classLoader
. loadClass ( "com.atguigu.java.Person" ) ; System
. out
. println ( clazz4
) ; System
. out
. println ( clazz1
== clazz4
) ;
總結(jié):創(chuàng)建類的對(duì)象的方式?
方式一:new + 構(gòu)造器 方式二:要?jiǎng)?chuàng)建Xxx類的對(duì)象,可以考慮:Xxx、Xxxs、XxxFactory、XxxBuilder類中查看是否有靜態(tài)方法的存在。可以調(diào)用其靜態(tài)方法,創(chuàng)建Xxx對(duì)象。 方式三:通過反射
Class實(shí)例可以是哪些結(jié)構(gòu)的說明
三、 使用Classloader加載src目錄下的配置文件
類的加載過程----了解
類的加載器的作用
類的加載器的分類
Java類編譯、運(yùn)行的執(zhí)行的流程
使用Classloader加載src目錄下的配置文件
@Test public void test2 ( ) throws Exception
{ Properties pros
= new Properties ( ) ;
ClassLoader classLoader
= ClassLoaderTest
. class . getClassLoader ( ) ; InputStream is
= classLoader
. getResourceAsStream ( "jdbc1.properties" ) ; pros
. load ( is
) ; String user
= pros
. getProperty ( "user" ) ; String password
= pros
. getProperty ( "password" ) ; System
. out
. println ( "user = " + user
+ ",password = " + password
) ; }
四、創(chuàng)建運(yùn)行時(shí)類的對(duì)象
代碼舉例
Class
< Person> clazz
= Person
. class ;
Person obj
= clazz
. newInstance ( ) ;
System
. out
. println ( obj
) ;
說明 newInstance():調(diào)用此方法(JDK1.9過期),創(chuàng)建對(duì)應(yīng)的運(yùn)行時(shí)類的對(duì)象。內(nèi)部調(diào)用了運(yùn)行時(shí)類的空參的構(gòu)造器。
要想此方法正常的創(chuàng)建運(yùn)行時(shí)類的對(duì)象,要求:
運(yùn)行時(shí)類必須提供空參的構(gòu)造器
空參的構(gòu)造器的訪問權(quán)限得夠。通常,設(shè)置為public。 在javabean中要求提供一個(gè)public的空參構(gòu)造器。原因:
便于通過反射,創(chuàng)建運(yùn)行時(shí)類的對(duì)象
便于子類繼承此運(yùn)行時(shí)類時(shí),默認(rèn)調(diào)用super()時(shí),保證父類此構(gòu)造器
五、獲取運(yùn)行時(shí)類的完整結(jié)構(gòu)
我們可以通過反射,獲取對(duì)應(yīng)的運(yùn)行時(shí)類中所有的屬性、方法、構(gòu)造器、父類、接口、父類的泛型、包、注解、異常等
獲取class對(duì)象功能:
1. 獲取成員變量們
* Field
[ ] getFields ( ) :獲取所有
public 修飾的成員變量
* Field
getField ( String name
) 獲取指定名稱的
public 修飾的成員變量
* Field
[ ] getDeclaredFields ( ) 獲取所有的成員變量,不考慮修飾符
* Field
getDeclaredField ( String name
) 2. 獲取構(gòu)造方法們
* Constructor
< ? > [ ] getConstructors ( ) * Constructor
< T> getConstructor ( 類
< ? > . . . parameterTypes
) * Constructor
< T> getDeclaredConstructor ( 類
< ? > . . . parameterTypes
) * Constructor
< ? > [ ] getDeclaredConstructors ( ) 3. 獲取成員方法們:
* Method
[ ] getMethods ( ) * Method
getMethod ( String name
, 類
< ? > . . . parameterTypes
) * Method
[ ] getDeclaredMethods ( ) * Method
getDeclaredMethod ( String name
, 類
< ? > . . . parameterTypes
) 4. 獲取全類名
* String
getName ( )
Field:成員變量
* 操作:
1. 設(shè)置值
* void set ( Object obj
, Object value
) 2. 獲取值
* get ( Object obj
) 3. 忽略訪問權(quán)限修飾符的安全檢查
* setAccessible ( true ) : 暴力反射
Constructor:構(gòu)造方法
* 創(chuàng)建對(duì)象:
* T
newInstance ( Object
. . . initargs
) * 如果使用空參數(shù)構(gòu)造方法創(chuàng)建對(duì)象,操作可以簡(jiǎn)化:Class對(duì)象的newInstance方法
* 執(zhí)行方法:
* Object
invoke ( Object obj
, Object
. . . args
)
* 獲取方法名稱:
* String getName
: 獲取方法名
代碼演示
@Test
public void test1 ( ) { Class
clazz = Person
. class ; Field
[ ] fields
= clazz
. getFields ( ) ; for ( Field f
: fields
) { System
. out
. println ( f
) ; } System
. out
. println ( ) ; Field
[ ] declaredFields
= clazz
. getDeclaredFields ( ) ; for ( Field f
: declaredFields
) { System
. out
. println ( f
) ; }
} @Test
public void test1 ( ) { Class
clazz = Person
. class ; Method
[ ] methods
= clazz
. getMethods ( ) ; for ( Method m
: methods
) { System
. out
. println ( m
) ; } System
. out
. println ( ) ; Method
[ ] declaredMethods
= clazz
. getDeclaredMethods ( ) ; for ( Method m
: declaredMethods
) { System
. out
. println ( m
) ; }
}
@Test
public void test1 ( ) { Class
clazz = Person
. class ; Constructor
[ ] constructors
= clazz
. getConstructors ( ) ; for ( Constructor c
: constructors
) { System
. out
. println ( c
) ; } System
. out
. println ( ) ; Constructor
[ ] declaredConstructors
= clazz
. getDeclaredConstructors ( ) ; for ( Constructor c
: declaredConstructors
) { System
. out
. println ( c
) ; } }
@Test
public void test2 ( ) { Class
clazz = Person
. class ; Class
superclass = clazz
. getSuperclass ( ) ; System
. out
. println ( superclass
) ;
}
@Test
public void test3 ( ) { Class
clazz = Person
. class ; Type genericSuperclass
= clazz
. getGenericSuperclass ( ) ; System
. out
. println ( genericSuperclass
) ;
}
@Test
public void test4 ( ) { Class
clazz = Person
. class ; Type genericSuperclass
= clazz
. getGenericSuperclass ( ) ; ParameterizedType paramType
= ( ParameterizedType
) genericSuperclass
; Type
[ ] actualTypeArguments
= paramType
. getActualTypeArguments ( ) ;
System
. out
. println ( ( ( Class
) actualTypeArguments
[ 0 ] ) . getName ( ) ) ;
}
@Test
public void test5 ( ) { Class
clazz = Person
. class ; Class
[ ] interfaces
= clazz
. getInterfaces ( ) ; for ( Class
c : interfaces
) { System
. out
. println ( c
) ; } System
. out
. println ( ) ; Class
[ ] interfaces1
= clazz
. getSuperclass ( ) . getInterfaces ( ) ; for ( Class
c : interfaces1
) { System
. out
. println ( c
) ; } }
@Test
public void test6 ( ) { Class
clazz = Person
. class ; Package pack
= clazz
. getPackage ( ) ; System
. out
. println ( pack
) ;
}
@Test
public void test7 ( ) { Class
clazz = Person
. class ; Annotation
[ ] annotations
= clazz
. getAnnotations ( ) ; for ( Annotation annos
: annotations
) { System
. out
. println ( annos
) ; }
}
六、調(diào)用運(yùn)行時(shí)類的指定結(jié)構(gòu)
調(diào)用指定的屬性
@Test
public void testField1 ( ) throws Exception
{ Class
clazz = Person
. class ; Person p
= ( Person
) clazz
. newInstance ( ) ; Field name
= clazz
. getDeclaredField ( "name" ) ; name
. setAccessible ( true ) ; name
. set ( p
, "Tom" ) ; System
. out
. println ( name
. get ( p
) ) ;
}
調(diào)用指定的方法 重點(diǎn)
@Test public void testMethod ( ) throws Exception
{ Class
clazz = Person
. class ; Person p
= ( Person
) clazz
. newInstance ( ) ; Method show
= clazz
. getDeclaredMethod ( "show" , String
. class ) ; show
. setAccessible ( true ) ; Object returnValue
= show
. invoke ( p
, "CHN" ) ; System
. out
. println ( returnValue
) ; System
. out
. println ( "*************如何調(diào)用靜態(tài)方法*****************" ) ; Method showDesc
= clazz
. getDeclaredMethod ( "showDesc" ) ; showDesc
. setAccessible ( true ) ;
Object returnVal
= showDesc
. invoke ( Person
. class ) ; System
. out
. println ( returnVal
) ; }
調(diào)用指定的構(gòu)造器:
@Test
public void testConstructor ( ) throws Exception
{ Class
clazz = Person
. class ; Constructor constructor
= clazz
. getDeclaredConstructor ( String
. class ) ; constructor
. setAccessible ( true ) ; Person per
= ( Person
) constructor
. newInstance ( "Tom" ) ; System
. out
. println ( per
) ; }
七、完整的反射例子
需求:寫一個(gè)"框架",不能改變?cè)擃惖娜魏未a的前提下,可以幫我們創(chuàng)建任意類的對(duì)象,并且執(zhí)行其中任意方法
* 實(shí)現(xiàn):
1. 配置文件
2. 反射
* 步驟:
1. 將需要?jiǎng)?chuàng)建的對(duì)象的全類名和需要執(zhí)行的方法定義在配置文件中
2. 在程序中加載讀取配置文件
3. 使用反射技術(shù)來加載類文件進(jìn)內(nèi)存
4. 創(chuàng)建對(duì)象
5. 執(zhí)行方法
代碼:
public class ReflectTest { public static void main ( String
[ ] args
) throws Exception
{ Properties pro
= new Properties ( ) ; ClassLoader classLoader
= ReflectTest
. class . getClassLoader ( ) ; InputStream is
= classLoader
. getResourceAsStream ( "pro.properties" ) ; pro
. load ( is
) ; String className
= pro
. getProperty ( "className" ) ; String methodName
= pro
. getProperty ( "methodName" ) ; Class
cls = Class
. forName ( className
) ; Object obj
= cls
. newInstance ( ) ; Method method
= cls
. getMethod ( methodName
) ; method
. invoke ( obj
) ; }
}
pro.properties
className
= cn
. itcast
. domain
. Student
methodName
= sleep
八、靜態(tài)代理,動(dòng)態(tài)代理,CGLIB代理
https://blog.csdn.net/cristianoxm/article/details/106199974
九、總結(jié)
除了int等基本類型外,Java的其他類型全部都是class(包括interface,void,注解,組數(shù)) 而class是由JVM在執(zhí)行過程中動(dòng)態(tài)加載(用到才加載,可以在運(yùn)行期根據(jù)條件來控制加載class)的。JVM在第一次讀取到一種class類型時(shí),將其加載進(jìn)內(nèi)存。每加載一種class,JVM就為其創(chuàng)建一個(gè)Class類型的實(shí)例,并關(guān)聯(lián)起來。
public final class Class { private Class ( ) { }
}
可以發(fā)現(xiàn)Class類的構(gòu)造方法是private,只有JVM能創(chuàng)建Class實(shí)例,我們自己的Java程序是無法創(chuàng)建Class實(shí)例的。
由于JVM為每個(gè)加載的class創(chuàng)建了對(duì)應(yīng)的Class實(shí)例,并在實(shí)例中保存了該class的所有信息,包括類名、包名、父類、實(shí)現(xiàn)的接口、所有方法、字段等,因此,如果獲取了某個(gè)Class實(shí)例,我們就可以通過這個(gè)Class實(shí)例獲取到該實(shí)例對(duì)應(yīng)的class的所有信息
方法一:直接通過一個(gè)
class 的靜態(tài)變量
class 獲取:
Class
cls = String
. class ; 方法二:如果我們有一個(gè)實(shí)例變量,可以通過該實(shí)例變量提供的
getClass ( ) 方法獲取:
String s
= "Hello" ;
Class
cls = s
. getClass ( ) ; 方法三:如果知道一個(gè)
class 的完整類名,可以通過靜態(tài)方法Class
. forName ( ) 獲取:
Class
cls = Class
. forName ( "java.lang.String" ) ;
因?yàn)镃lass實(shí)例在JVM中是唯一的,所以,上述方法獲取的Class實(shí)例是同一個(gè)實(shí)例。可以用==比較兩個(gè)Class實(shí)例:
Class
cls1 = String
. class ;
String s
= "Hello" ;
Class
cls2 = s
. getClass ( ) ;
boolean sameClass
= cls1
== cls2
;
如果獲取到了一個(gè)Class實(shí)例,我們就可以通過該Class實(shí)例來創(chuàng)建對(duì)應(yīng)類型的實(shí)例:
Class
cls = String
. class ;
String s
= ( String
) cls
. newInstance ( ) ;
上述代碼相當(dāng)于new String()。通過Class.newInstance()可以創(chuàng)建類實(shí)例,它的局限是:只能調(diào)用public的無參數(shù)構(gòu)造方法。帶參數(shù)的構(gòu)造方法,或者非public的構(gòu)造方法都無法通過Class.newInstance()被調(diào)用。
訪問字段,獲取字段值,修改字段值
對(duì)任意的一個(gè)Object實(shí)例,只要我們獲取了它的Class,就可以獲取它的一切信息。 Class類提供了以下幾個(gè)方法來獲取字段:
Field getField(name):根據(jù)字段名獲取某個(gè)public的field(包括父類) Field getDeclaredField(name):根據(jù)字段名獲取當(dāng)前類的某個(gè)field(不包括父類) Field[] getFields():獲取所有public的field(包括父類) Field[] getDeclaredFields():獲取當(dāng)前類的所有field(不包括父類)
進(jìn)而可以通過Field實(shí)例可以獲取字段信息:getName(),getType(),getModifiers()
Object p
= new Person ( "Xiao Ming" ) ; Class
c = p
. getClass ( ) ; Field f
= c
. getDeclaredField ( "name" ) ; f
. setAccessible ( true ) ; Object value
= f
. get ( p
) ;
疑問:如果使用反射可以獲取private字段的值,那么類的封裝還有什么意義?答案是正常情況下,我們總是通過p.name來訪問Person的name字段,編譯器會(huì)根據(jù)public、protected和private決定是否允許訪問字段,這樣就達(dá)到了數(shù)據(jù)封裝的目的。而反射是一種非常規(guī)的用法,使用反射,首先代碼非常繁瑣,其次,它更多地是給工具或者底層框架來使用,目的是在不知道目標(biāo)實(shí)例任何信息的情況下,獲取特定字段的值。此外,setAccessible(true)可能會(huì)失敗。如果JVM運(yùn)行期存在SecurityManager,那么它會(huì)根據(jù)規(guī)則進(jìn)行檢查,有可能阻止setAccessible(true)。例如,某個(gè)SecurityManager可能不允許對(duì)java和javax開頭的package的類調(diào)用setAccessible(true),這樣可以保證JVM核心庫的安全。
修改字段值 修改字段值是通過Field.set(Object, Object)實(shí)現(xiàn)的,其中第一個(gè)Object參數(shù)是指定的實(shí)例,第二個(gè)Object參數(shù)是待修改的值
獲取方法、調(diào)用方法
獲取方法 我們已經(jīng)能通過Class實(shí)例獲取所有Field對(duì)象,同樣的,可以通過Class實(shí)例獲取所有Method信息。Class類提供了以下幾個(gè)方法來獲取Method
Method getMethod(name, Class…):獲取某個(gè)public的Method(包括父類) Method getDeclaredMethod(name, Class…):獲取當(dāng)前類的某個(gè)Method(不包括父類) Method[] getMethods():獲取所有public的Method(包括父類) Method[] getDeclaredMethods():獲取當(dāng)前類的所有Method(不包括父類)
一個(gè)Method對(duì)象包含一個(gè)方法的所有信息:
getName():返回方法名稱,例如:“getScore”; getReturnType():返回方法返回值類型,也是一個(gè)Class實(shí)例,例如:String.class; getParameterTypes():返回方法的參數(shù)類型,是一個(gè)Class數(shù)組,例如:{String.class, int.class}; getModifiers():返回方法的修飾符,它是一個(gè)int,不同的bit表示不同的含義。
public class Main { public static void main ( String
[ ] args
) throws Exception
{ Class
stdClass = Student
. class ; System
. out
. println ( stdClass
. getMethod ( "getScore" , String
. class ) ) ; System
. out
. println ( stdClass
. getMethod ( "getName" ) ) ; System
. out
. println ( stdClass
. getDeclaredMethod ( "getGrade" , int . class ) ) ; }
}
class Student extends Person { public int getScore ( String type
) { return 99 ; } private int getGrade ( int year
) { return 1 ; }
}
class Person { public String
getName ( ) { return "Person" ; }
}
調(diào)用方法invoke(Object,Object):invoke的第一個(gè)參數(shù)是對(duì)象實(shí)例,即在哪個(gè)實(shí)例上調(diào)用該方法,后面的可變參數(shù)要與方法參數(shù)一致,否則將報(bào)錯(cuò)
public class Main { public static void main ( String
[ ] args
) throws Exception
{ String s
= "Hello world" ; Method m
= String
. class . getMethod ( "substring" , int . class ) ; String r
= ( String
) m
. invoke ( s
, 6 ) ; System
. out
. println ( r
) ; }
}
調(diào)用靜態(tài)方法:如果獲取到的Method表示一個(gè)靜態(tài)方法,調(diào)用靜態(tài)方法時(shí),由于無需指定實(shí)例對(duì)象,所以invoke方法傳入的第一個(gè)參數(shù)永遠(yuǎn)為null
public class Main { public static void main ( String
[ ] args
) throws Exception
{ Method m
= Integer
. class . getMethod ( "parseInt" , String
. class ) ; Integer n
= ( Integer
) m
. invoke ( null
, "12345" ) ; System
. out
. println ( n
) ; }
}
調(diào)用非public方法:跟獲取非pubilc屬性相似,設(shè)置Method.setAccessible(true)允許其調(diào)用 多態(tài)性:一個(gè)Person類定義了hello()方法,并且它的子類Student也覆寫了hello()方法,那么,從Person.class獲取的Method,作用于Student實(shí)例時(shí),調(diào)用的方法到底是哪個(gè)?
public class Main { public static void main ( String
[ ] args
) throws Exception
{ Method h
= Person
. class . getMethod ( "hello" ) ; h
. invoke ( new Student ( ) ) ; }
} class Person { public void hello ( ) { System
. out
. println ( "Person:hello" ) ; }
} class Student extends Person { public void hello ( ) { System
. out
. println ( "Student:hello" ) ; }
}
運(yùn)行上述代碼,發(fā)現(xiàn)打印出的是Student:hello,因此,使用反射調(diào)用方法時(shí),仍然遵循多態(tài)原則:即總是調(diào)用實(shí)際類型的覆寫方法(如果存在)
調(diào)用構(gòu)造方法
我們上面知道,可以通過newInstance()創(chuàng)建對(duì)象,調(diào)用Class.newInstance()的局限是,它只能調(diào)用該類的public無參數(shù)構(gòu)造方法。如果構(gòu)造方法帶有參數(shù),或者不是public,就無法直接通過Class.newInstance()來調(diào)用。為了調(diào)用任意的構(gòu)造方法,Java的反射API提供了Constructor對(duì)象,它包含一個(gè)構(gòu)造方法的所有信息,可以創(chuàng)建一個(gè)實(shí)例。Constructor對(duì)象和Method非常類似,不同之處僅在于它是一個(gè)構(gòu)造方法,并且,調(diào)用結(jié)果總是返回實(shí)例
public class Main { public static void main ( String
[ ] args
) throws Exception
{ Constructor cons1
= Integer
. class . getConstructor ( int . class ) ; Integer n1
= ( Integer
) cons1
. newInstance ( 123 ) ; System
. out
. println ( n1
) ; Constructor cons2
= Integer
. class . getConstructor ( String
. class ) ; Integer n2
= ( Integer
) cons2
. newInstance ( "456" ) ; System
. out
. println ( n2
) ; }
}
通過Class實(shí)例獲取Constructor的方法如下:
getConstructor(Class…):獲取某個(gè)public的Constructor; getDeclaredConstructor(Class…):獲取某個(gè)Constructor; getConstructors():獲取所有public的Constructor; getDeclaredConstructors():獲取所有Constructor。
注意Constructor總是當(dāng)前類定義的構(gòu)造方法,和父類無關(guān),因此不存在多態(tài)的問題。調(diào)用非public的Constructor時(shí),必須首先通過setAccessible(true)設(shè)置允許訪問。setAccessible(true)可能會(huì)失敗。
獲取父類和實(shí)現(xiàn)接口
public class Main { public static void main ( String
[ ] args
) throws Exception
{ Class
i = Integer
. class ; Class
n = i
. getSuperclass ( ) ; System
. out
. println ( n
) ; Class
o = n
. getSuperclass ( ) ; System
. out
. println ( o
) ; System
. out
. println ( o
. getSuperclass ( ) ) ; }
}
public class Main { public static void main ( String
[ ] args
) throws Exception
{ Class
s = Integer
. class ; Class
[ ] is
= s
. getInterfaces ( ) ; for ( Class
i : is
) { System
. out
. println ( i
) ; } }
}
是否可以向上轉(zhuǎn)型isAssignableFrom()
Object
. class . isAssignableFrom ( Integer
. class ) ;
Integer
. class . isAssignableFrom ( Number
. class ) ;
靜態(tài)代理VSJDK動(dòng)態(tài)代理VS CGLib動(dòng)態(tài)代理 靜態(tài)代理與動(dòng)態(tài)代理的區(qū)別主要在: 靜態(tài)代理在編譯時(shí)就已經(jīng)實(shí)現(xiàn),編譯完成后代理類是一個(gè)實(shí)際的class文件 動(dòng)態(tài)代理是在運(yùn)行時(shí)動(dòng)態(tài)生成的,即編譯完成后沒有實(shí)際的class文件,而是在運(yùn)行時(shí)動(dòng)態(tài)生成類字節(jié)碼,并加載到JVM中。JDK動(dòng)態(tài)代理模式只能代理接口,如果要代理類那么就不行了。而CGLIB則是代理類。使用動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口,使用cglib代理的對(duì)象則無需實(shí)現(xiàn)接口,達(dá)到代理類無侵入。同時(shí)CGLIB也有其缺陷,那就是必須目標(biāo)類必須是可以繼承的,如果目標(biāo)類不可繼承,那么我們就無法使用CGLIB來增強(qiáng)該類。
定義接口:
public interface Hello { void morning ( String name
) ;
}
編寫實(shí)現(xiàn)類:
public class HelloWorld implements Hello { public void morning ( String name
) { System
. out
. println ( "Good morning, " + name
) ; }
}
創(chuàng)建實(shí)例,轉(zhuǎn)型為接口并調(diào)用:
Hello hello
= new HelloWorld ( ) ;
hello
. morning ( "Bob" ) ;
public class HelloDynamicProxy implements Hello { InvocationHandler handler
; public HelloDynamicProxy ( InvocationHandler handler
) { this . handler
= handler
; } public void morning ( String name
) { handler
. invoke ( this , Hello
. class . getMethod ( "morning" , String
. class ) , new Object [ ] { name
} ) ; }
}
一個(gè)較復(fù)雜的動(dòng)態(tài)代理例子
import java
. lang
. reflect
. InvocationHandler
;
import java
. lang
. reflect
. Proxy
;
public class DynamicProxy { public static void main ( String
[ ] args
) { Student ordinaryStudents
= new OrdinaryStudents ( ) ; ordinaryStudents
. eat ( ) ; ordinaryStudents
. write ( ) ; InvocationHandler handler
= ( proxy
, method
, handlerArgs
) - > { if ( "eat" . equals ( method
. getName ( ) ) ) { System
. out
. println ( "我可以吃香喝辣!" ) ; return null
; } if ( "write" . equals ( method
. getName ( ) ) ) { System
. out
. println ( "我的作文題目是《我的區(qū)長父親》。" ) ; method
. invoke ( ordinaryStudents
, handlerArgs
) ; System
. out
. println ( "我的作文拿了區(qū)作文競(jìng)賽一等獎(jiǎng)!so easy!" ) ; return null
; } return null
; } ; Student sonOfDistrict
= ( Student
) Proxy
. newProxyInstance ( ordinaryStudents
. getClass ( ) . getClassLoader ( ) , ordinaryStudents
. getClass ( ) . getInterfaces ( ) , handler
) ; sonOfDistrict
. eat ( ) ; sonOfDistrict
. write ( ) ; }
}
interface Student { void eat ( ) ; void run ( ) ; void write ( ) ;
}
class OrdinaryStudents implements Student { @Override public void eat ( ) { System
. out
. println ( "我在吃飯!" ) ; } @Override public void run ( ) { System
. out
. println ( "我在跑步!" ) ; } @Override public void write ( ) { System
. out
. println ( "我在寫作文!" ) ; }
}
實(shí)現(xiàn)動(dòng)態(tài)代理的步驟:
1.創(chuàng)建接口,定義目標(biāo)類要完成的功能. 2.創(chuàng)建目標(biāo)類實(shí)現(xiàn)接口 3.創(chuàng)建InvocationHandler 接口的實(shí)現(xiàn)類,在invoke方法中完成代理的功能. 3.1 調(diào)用目標(biāo)方法 3.2 增強(qiáng)功能 4.使用Proxy類靜態(tài)方法創(chuàng)建代理對(duì)象,并把返回值轉(zhuǎn)為接口類型.
public interface SayGoodbye { void sayGoodbye ( String name
) ;
}
public interface SayHello { void sayHello ( String name
) ;
}
public class goodByeImpl implements SayGoodbye { @Override public void sayGoodbye ( String name
) { System
. out
. println ( "GoodBye" + name
) ; }
}
public class HelloImpl implements SayHello { @Override public void sayHello ( String name
) { System
. out
. println ( "Hello" + name
) ; }
}
創(chuàng)建InvocationHandler 接口的實(shí)現(xiàn)類
public class ServiceProxy implements InvocationHandler { private Object target
; public void InvokeBefore ( ) { System
. out
. println ( "調(diào)用前" ) ; } public void InvokeAfter ( ) { System
. out
. println ( "調(diào)用后" ) ; } public Object
getInstance ( Object target
) { this . target
= target
; return Proxy
. newProxyInstance ( target
. getClass ( ) . getClassLoader ( ) , target
. getClass ( ) . getInterfaces ( ) , this ) ; } @Override public Object
invoke ( Object proxy
, Method method
, Object
[ ] args
) throws Throwable
{ Object result
; InvokeBefore ( ) ; result
= method
. invoke ( target
, args
) ; InvokeAfter ( ) ; return result
; }
}
使用Proxy類靜態(tài)方法創(chuàng)建代理對(duì)象,并把返回值轉(zhuǎn)為接口類型.
public class ProxyTest { public static void main ( String
[ ] args
) { ServiceProxy proxy
= new ServiceProxy ( ) ; SayGoodbye sgServiceProxy
= ( SayGoodbye
) proxy
. getInstance ( new goodByeImpl ( ) ) ; sgServiceProxy
. sayGoodbye ( " lisi" ) ; SayHello shServiceProxy
= ( SayHello
) proxy
. getInstance ( new HelloImpl ( ) ) ; shServiceProxy
. sayHello ( " zhangsan" ) ; }
}
參考文章
總結(jié)
以上是生活随笔 為你收集整理的反射及代理 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。