kotlin入門潛修之特性及其原理篇—反射

本文收錄于 kotlin入門潛修專題系列,歡迎學(xué)習(xí)交流卫枝。

創(chuàng)作不易煎饼,如有轉(zhuǎn)載,還請備注校赤。

反射

java中反射占有舉足輕重的地位吆玖,很多優(yōu)秀的框架都離不開反射。那么什么是反射马篮?為什么反射被視為java語言具有動態(tài)性的關(guān)鍵沾乘?kotlin中的反射又是什么樣的?這就是本篇文章要闡述的主題浑测。

反射是指翅阵,在運(yùn)行期間能夠動態(tài)獲取類信息的一種機(jī)制歪玲,java提供了一套api,可以滿足這種需求掷匠。

為什么說反射使得java具有了動態(tài)性滥崩?這就需要先了解下什么靜態(tài)語言,什么是動態(tài)語言了讹语。

我們都知道钙皮,編程語言大都具備一些共有的行為操作,比如添加新代碼顽决、擴(kuò)展對象或者定義株灸、修改系統(tǒng)類型等等,而動態(tài)語言和靜態(tài)語言最主要的區(qū)分就是在上述行為的執(zhí)行時機(jī)上擎值,動態(tài)語言會在運(yùn)行時執(zhí)行上述行為慌烧,而靜態(tài)語言上述行為則發(fā)生在編譯期。

對于java而言鸠儿,其數(shù)據(jù)類型等在編譯期間就已經(jīng)確定屹蚊,也無法在運(yùn)行的時候使用諸如動態(tài)的增加代碼、擴(kuò)展對象等操作进每,所以認(rèn)為其是靜態(tài)語言汹粤,而反射的出現(xiàn)改變了這種情況。

反射機(jī)制可以讓java具備在運(yùn)行時動態(tài)獲取類信息的能力田晚,包括一些類型元數(shù)據(jù)信息嘱兼、泛型信息等,同時可以動態(tài)的修改這些編碼信息贤徒,諸如此類的芹壕,所以說反射機(jī)制使java具備了動態(tài)語言的特性。

java中的反射

上個小節(jié)提到接奈,反射機(jī)制讓java具備了在運(yùn)行時獲取類信息的能力踢涌,因此想了解反射,需要先了解下類是什么序宦。

正常我們所說的類其實(shí)是指java中的所有類的統(tǒng)稱睁壁,而在反射機(jī)制中,java為我們專門提供了一個類互捌,那就是首字母大寫的Class類(具體類型是java.lang.Class)潘明,Class類保存了一個普通java類(可以是任何類)對應(yīng)的元數(shù)據(jù)信息。示例如下:

//定義了一個Person類
public class Person {
    public Person() {
    }
    private Person(String name) {
    }
    private int age;
    public String name;
    public static void nothing() {
    }
    public int getAge() {
        return age;
    }
    private void test() {
        System.out.println("test");
    }
}
//測試類
public class Test {
    public static void main(String[] args) {
        //我們可以通過這種方式獲取Person對應(yīng)的Class類引用
        Class<Person> clazz = Person.class;
        System.out.println("getName : " + clazz.getName());
        for (Constructor constructor : clazz.getConstructors()){
            System.out.println("constructors: " + constructor);
        }
        for (Constructor constructor : clazz.getDeclaredConstructors()){
            System.out.println("declared constructors: " + constructor);
        }
        for (Field field : clazz.getDeclaredFields()) {
            System.out.println("declared field: " + field);
        }
        for (Field field : clazz.getFields()) {
            System.out.println("field: " + field);
        }
        for (Method method : clazz.getDeclaredMethods()){
            System.out.println("declare method: " + method);
        }
        for (Method method : clazz.getMethods()){
            System.out.println("method: " + method);
        }
    }
}

上面代碼定義了一個普通的類Person秕噪,為了便于觀察其對應(yīng)的Class信息钳降,我們在Person中特意定義了多個有不同訪問權(quán)限組成的成員變量和成員方法,代碼執(zhí)行完后打印如下:

getName : test.Person
constructors: public test.Person()
declared constructors: public test.Person()
declared constructors: private test.Person(java.lang.String)
declared field: private int test.Person.age
declared field: public java.lang.String test.Person.name
field: public java.lang.String test.Person.name
declare method: private void test.Person.test()
declare method: public int test.Person.getAge()
declare method: public static void test.Person.nothing()
method: public int test.Person.getAge()
method: public static void test.Person.nothing()
method: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
method: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
method: public final void java.lang.Object.wait() throws java.lang.InterruptedException
method: public boolean java.lang.Object.equals(java.lang.Object)
method: public java.lang.String java.lang.Object.toString()
method: public native int java.lang.Object.hashCode()
method: public final native java.lang.Class java.lang.Object.getClass()
method: public final native void java.lang.Object.notify()
method: public final native void java.lang.Object.notifyAll()

結(jié)合打印結(jié)果很容易知道上述代碼的含義巢价,主要是要注意區(qū)分一些相似方法的不同點(diǎn)牲阁,現(xiàn)將相似方法的區(qū)別整理如下(橫向區(qū)分):

方法 方法 區(qū)分
getConstructors getDeclaredConstructors 二者都是用于獲取類的構(gòu)造方法固阁,但getConstructors只能獲取公有的構(gòu)造方法,而getDeclaredConstructors則可以獲取該類定義的全部構(gòu)造方法
getFields getDeclaredFields 二者是用于獲取類中的字段城菊,getFields只能獲取公有字段备燃,getDeclaredFields則可以獲取全部字段
getMethods getDeclaredMethods 二者都是用于獲取類中定義的方法,getDeclaredMethods只能獲取當(dāng)前類中定義的方法凌唬,不包括超類方法并齐,而getMethods則還能獲取父類方法

上面簡單展示了Class的用法,反射正是基于上述信息來完成運(yùn)行時的一些動態(tài)操作的客税,下面來看個使用反射動態(tài)操作類信息的例子况褪。如下所示:

    public static void main(String[] args) {
        Person person = new Person();
        person.setAge(100);
        System.out.println("before: " + person.getAge());
        Field[] fields = person.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if (field.getName().equals("age")) {
                try {
                    field.set(person, 1);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("after: " + person.getAge());
    }
}

我們首先來闡述下我們想要完成的工作:我們定義了一個Person類,在其中定義了一個私有的age字段更耻,其默認(rèn)值是20〔舛猓現(xiàn)在我們想動態(tài)修改其值,但是由于age是私有字段秧均,所以我們無法通過常規(guī)的編碼來進(jìn)行修改食侮。那么,此時我們就可以在運(yùn)行時利用反射機(jī)制來解決該問題目胡。

首先锯七,我們遍歷Person類的所有成員,把a(bǔ)ge字段的權(quán)限訪問符修改為public誉己,然后通過java為我們提供的field對象的set方法完成屬性變更眉尸,上述代碼執(zhí)行結(jié)果打印如下:

before: 20
after: 1

這就是反射的使用案例之一,我們在運(yùn)行時通過修改類信息來完成我們想要做的工作巨双。反射的使用案例有很多噪猾,這個案例只是冰山一角。java相關(guān)的反射暫時闡述至此炉峰,主要來看下kotlin中的反射畏妖。

kotlin中的反射

上節(jié)大概闡述了java中的反射,本節(jié)將闡述kotlin中的反射機(jī)制疼阔,kotlin的反射原理和java基本一致,但語法與java中的有點(diǎn)不太一樣半夷。

下面婆廊,結(jié)合案例來看下kotlin關(guān)于類信息的一些操作。

首先我們定義一個Person類巫橄,如下所示:

open class Person {
    private val age = 20
    internal var name = "name"
    private fun getAge(): Int {
        return age
    }
    fun personName(): String {
        return name
    }
    fun String.charAtIndexInPerson(index: Int): Char {
        return ' '
    }
    val String.lastCharInPerson: String
        get() = "last char"
}

接著淘邻,我們再定義一個Person類的子類Student,如下所示:

class Student : Person() {
    private val grade = "一年級"
    val credit = 90
    private fun getGrade(): String {
        return grade
    }
    fun studentCredit(): Int {
        return credit
    }
    fun String.charAtIndexInStudent(index: Int): Char {
        return ' '
    }
    val String.lastCharInStudent: String
        get() = "last char"
}

這里定義了一個父類湘换,又定義了一個子類宾舅,目的是為了后邊可以方便的驗(yàn)證不同方法所表達(dá)的不同功能统阿。來看下測試代碼:

fun main(args: Array<String>) {
    val student = Student()
    val clazz = student::class
    println(clazz)
    clazz.memberProperties.forEach {
        println("memberProperties: " + it)
    }
    clazz.declaredMemberProperties.forEach {
        println("declaredMemberProperties: " + it)
    }
    clazz.memberFunctions.forEach {
        println("memberFunctions: " + it)
    }
    clazz.declaredMemberFunctions.forEach {
        println("declaredMemberFunctions: " + it)
    }
    clazz.memberExtensionProperties.forEach {
        println("memberExtensionProperties: " + it)
    }
    clazz.declaredMemberExtensionProperties.forEach {
        println("declaredMemberExtensionProperties: " + it)
    }
    clazz.memberExtensionFunctions.forEach {
        println("memberExtensionFunctions: " + it)
    }
    clazz.declaredMemberExtensionFunctions.forEach {
        println("declaredMemberExtensionFunctions: " + it)
    }
}

上面代碼演示了kotlin為我們暴露的常見的一些獲取類信息的接口,當(dāng)然還有很多獲取類其他信息的接口筹我,這里沒有羅列出來扶平,可以自己去驗(yàn)證。上面代碼執(zhí)行完成之后蔬蕊,打印如下:

class Student
memberProperties: val Student.credit: kotlin.Int
memberProperties: val Student.grade: kotlin.String
memberProperties: var Person.name: kotlin.String
declaredMemberProperties: val Student.credit: kotlin.Int
declaredMemberProperties: val Student.grade: kotlin.String
memberFunctions: fun Student.getGrade(): kotlin.String
memberFunctions: fun Student.studentCredit(): kotlin.Int
memberFunctions: fun kotlin.Any.equals(kotlin.Any?): kotlin.Boolean
memberFunctions: fun kotlin.Any.hashCode(): kotlin.Int
memberFunctions: fun Person.personName(): kotlin.String
memberFunctions: fun kotlin.Any.toString(): kotlin.String
declaredMemberFunctions: fun Student.getGrade(): kotlin.String
declaredMemberFunctions: fun Student.studentCredit(): kotlin.Int
memberExtensionProperties: val Student.(kotlin.String.)lastCharInStudent: kotlin.String
memberExtensionProperties: val Person.(kotlin.String.)lastCharInPerson: kotlin.String
declaredMemberExtensionProperties: val Student.(kotlin.String.)lastCharInStudent: kotlin.String
memberExtensionFunctions: fun Student.(kotlin.String.)charAtIndexInStudent(kotlin.Int): kotlin.Char
memberExtensionFunctions: fun Person.(kotlin.String.)charAtIndexInPerson(kotlin.Int): kotlin.Char
declaredMemberExtensionFunctions: fun Student.(kotlin.String.)charAtIndexInStudent(kotlin.Int): kotlin.Char

相信大多數(shù)人都不會仔細(xì)看上面的代碼及打印的結(jié)果结澄,這里貼出來只是來證明下面我們結(jié)論的正確性,所以你完全可以略過上述測試代碼岸夯,直接來看下面的對比列表(橫向比較):

屬性 屬性 區(qū)分
memberProperties declaredMemberProperties 二者都是用于獲取類的屬性成員信息麻献,但memberProperties用于獲取當(dāng)前類中的所有屬性及其超類中的非私有屬性,而declaredMemberProperties則只能用于獲取該類定義的全部屬性
memberFunctions declaredMemberFunctions 二者都是用于獲取類中定義的成員方法猜扮,memberFunctions可用于獲取公當(dāng)前類的所有方法以及其超類的非私有成員方法勉吻,declaredMemberFunctions則只能獲取當(dāng)前類中的所有成員方法
memberExtensionProperties declaredMemberExtensionProperties 二者都是用于獲取在當(dāng)前類中定義的擴(kuò)展屬性,memberExtensionProperties可以獲取在當(dāng)前類以及在其父類中定義的擴(kuò)展屬性旅赢,而declaredMemberExtensionProperties則只能獲取在當(dāng)前類中定義的屬性
memberExtensionFunctions declaredMemberExtensionFunctions 二者都是用于獲取在當(dāng)前類中定義的擴(kuò)展方法餐曼,memberExtensionFunctions可以獲取在當(dāng)前類以及在其父類中定義的擴(kuò)展方法,而declaredMemberExtensionFunctions則只能獲取在當(dāng)前類中定義的方法

從表格中可以看出鲜漩,實(shí)際上kotlin暴露的獲取類信息的接口和java中基本一致源譬,至于一些相似接口的區(qū)別完全可以通過其命名進(jìn)行區(qū)分。

但是有一點(diǎn)要特別注意孕似,那就是在kotlin中我們使用:: class這種方法實(shí)際上獲取的是kotlin中的類信息踩娘,其對應(yīng)的類定義是kotlin.reflect.KClass,而不是java中的java.lang.Class喉祭,但很多時候我們需要通過kotlin代碼獲取其在java語言中所對應(yīng)的類信息(比如android開發(fā)中常見的activity跳轉(zhuǎn)养渴,就需要獲取目標(biāo)類在java語言中對應(yīng)的Class類),這個時候我們可以通過以下方法獲确豪印:

//獲取對應(yīng)的java語言中的類信息理卑,注意,這里是通過Student的
//實(shí)例來獲取Student對應(yīng)的類信息的蔽氨。
    val javaClazz = student::class.java//

上面我們獲取KClass類信息的時候藐唠,是基于類對象來獲取的。我們還可以直接通過類名來獲取對應(yīng)的KClass類信息鹉究,如下所示:

   val javaClazz = Student::class//我們通過類名來獲取對應(yīng)的類信息

KClass

前面我們演示了獲取kotlin中類對應(yīng)的類信息宇立,這些信息都保存在KClass這個類中,本節(jié)將探索下這個類自赔。

首先妈嘹,通過查找源碼我們發(fā)現(xiàn)KClass位于kotlin.reflect包中,是個接口绍妨,如下所示:

package kotlin.reflect
public interface KClass<T : Any> : KDeclarationContainer, KAnnotatedElement, KClassifier {
    public val simpleName: String?
    override val members: Collection<KCallable<*>>
//省略其他一些內(nèi)容...
}

從該接口的定義可以發(fā)現(xiàn)润脸,我們常用的獲取類成員的入口members柬脸、獲取類名稱的入口simpleName等都在這個接口中,但是并沒有找到上面我們用到的諸如declaredFunctions毙驯、memberProperties等屬性入口倒堕,那么我們?yōu)槭裁茨苁褂眠@些屬性?他們到底定義在了哪里尔苦?答案是顯然的涩馆,那就是這些屬性是擴(kuò)展屬性!

確實(shí)如此允坚,通過編譯器生成的KClass.class這個類文件魂那,我們可以發(fā)現(xiàn),這些屬性全部是擴(kuò)展屬性稠项,如下所示:

@kotlin.SinceKotlin public val kotlin.reflect.KClass<*>.declaredFunctions: kotlin.collections.Collection<kotlin.reflect.KFunction<*>> /* compiled code */
@kotlin.SinceKotlin public val kotlin.reflect.KClass<*>.declaredMemberExtensionFunctions: kotlin.collections.Collection<kotlin.reflect.KFunction<*>> /* compiled code */
//此處省略其他擴(kuò)展屬性的定義...

找到了這些擴(kuò)展屬性的定義之后涯雅,更重要的是關(guān)注這些屬性的類型,因?yàn)檫@在使用反射的時候往往需要用到展运。分別摘錄如下(結(jié)合源代碼即編譯器為我們生成的代碼):

//獲取該類的所有成員
 override val members: Collection<KCallable<*>>
//獲取該類的所有成員屬性
@kotlin.SinceKotlin public val <T : kotlin.Any> kotlin.reflect.KClass<T>.memberProperties: kotlin.collections.Collection<kotlin.reflect.KProperty1<T, *>> /* compiled code */
//獲取該類的所有成員方法
@kotlin.SinceKotlin public val kotlin.reflect.KClass<*>.memberFunctions: kotlin.collections.Collection<kotlin.reflect.KFunction<*>> /* compiled code */

上面摘錄了三個不同類型的屬性活逆,一個是獲取類所有成員的members,其集合中的元素類型是KCallable<*>拗胜,另一個是獲取當(dāng)前類定義的成員屬性的memberProperties蔗候,其集合中的元素類型是kotlin.reflect.KProperty1<T, >,而最后一個是獲取當(dāng)前類定義的方法的memberFunctions屬性埂软,其集合中的元素類型是KFunction<>锈遥。

既然members能夠獲取所有成員,那么顯然可以推斷KCallable應(yīng)該是KProperty1以及KFunction的超類型勘畔!通過查找源碼我們發(fā)現(xiàn)所灸,確實(shí)如此,如下所示:

//KFunction類型炫七,繼承自KCallable
public interface KFunction<out R> : KCallable<R>, Function<R> {}
//從下面可以看出爬立,KProperty1繼承自KProperty
public interface KProperty1<T, out R> : KProperty<R>, (T) -> R {}
//而KProperty繼承自KCallable
public interface KProperty<out R> : KCallable<R> {}

上面代碼驗(yàn)證了我們的推斷,實(shí)際上KCallable可以用于表示任何成員類型万哪。而KFunction<out R>則用于表示方法類型侠驯。最后的KProperty1則是用于表示屬性成員類型,不過我們發(fā)現(xiàn)在kotlin中壤圃,KProperty的子類型不止一個陵霉,還有KProperty0類型以及KProperty2類型,分別表示獲取類靜態(tài)成員屬性對應(yīng)的屬性類型和獲取類內(nèi)部定義的擴(kuò)展屬性對應(yīng)的屬性類型伍绳,摘錄如下所示:

//獲取類靜態(tài)屬性成員對應(yīng)的屬性類型
@kotlin.SinceKotlin public val kotlin.reflect.KClass<*>.staticProperties: kotlin.collections.Collection<kotlin.reflect.KProperty0<*>> /* compiled code */
//獲取類內(nèi)定義的擴(kuò)展屬性成員對應(yīng)的屬性類型
@kotlin.SinceKotlin public val <T : kotlin.Any> kotlin.reflect.KClass<T>.declaredMemberExtensionProperties: kotlin.collections.Collection<kotlin.reflect.KProperty2<T, *, *>> /* compiled code */

但這三者都是繼承自KProperty,即KProperty表達(dá)的就是屬性成員類型乍桂。

了解具體返回的類型之后冲杀,我們就可以做更多的操作了效床,比如我們可以針對KProperty的屬性做做以下判斷:

//定義了一個Person類
 class Person {
     private val age = 20
     var name = "name"
     open val address = "address"
 }
//測試方法
fun main(args: Array<String>) {
    var clazz: KClass<Person> = Person::class
    clazz.memberProperties.forEach {
//這里我們刻意顯示將屬性對應(yīng)的類型標(biāo)識出來
        var property = it as KProperty<Person>
//在這里可以進(jìn)行各種判斷
        println(property.toString() + " isFinal: " + property.isFinal + " isOpen: " + property.isOpen)
    }
}

方法的操作也是同樣道理了,這里不再闡述权谁。

最后來看一個kotlin中使用反射的經(jīng)典案例剩檀。

動態(tài)代理

代理模式應(yīng)該都比較清楚,代理模式屏蔽了目標(biāo)對象的實(shí)現(xiàn)細(xì)節(jié)旺芽,可分為靜態(tài)代理和動態(tài)代理沪猴。靜態(tài)代理由于其無法動態(tài)的進(jìn)行擴(kuò)展,只能在編碼層次無限增加采章,所以有很大的局限性运嗜。而動態(tài)代理則可以進(jìn)行動態(tài)擴(kuò)展,可以擁有一份代理邏輯悯舟,卻能完成不同的業(yè)務(wù)邏輯担租。

代理模式是使用反射的很好的案例,下面我們通過kotlin代碼來實(shí)現(xiàn)該模式抵怎。

首先奋救,在沒有第三方庫支持的情況下,代理模式中的被代理者需要實(shí)現(xiàn)一個接口反惕,這里我們先來定義一個被代理者尝艘,如下所示:

//首先為被代理者定義一個接口
interface IOperation {
    fun operation()
}
//然后定義被代理者,實(shí)現(xiàn)了IOperation接口
class RealObject : IOperation {
    override fun operation() {
        println("real object operation...")
    }
}

定義完被代理者之后姿染,我們來看下動態(tài)代理中最重要的代理邏輯背亥,如下所示:

//代理邏輯代碼
class ProxyFactory {
    companion object {
        fun getInstance(targetObj: Any): Any? {
            return Proxy.newProxyInstance(targetObj.javaClass.classLoader, targetObj.javaClass.interfaces, { proxy, method, args ->
                println("proxy start, you can do some work here")
                val newArgs = args ?: arrayOfNulls(0)
                method.invoke(targetObj, *newArgs)
                println("proxy end, you can also do some work here")
            })
        }
    }
}

測試代碼如下所示:

fun main(args: Array<String>) {
    val operation: IOperation = ProxyFactory.getInstance(RealObject()) as IOperation
    operation.operation()
}

執(zhí)行代碼,打印如下所示:

proxy start, you can do some work here
real object operation...
proxy end, you can also do some work here

關(guān)于上述代碼盔粹,需要注意以下幾點(diǎn):

  1. 我們提供了一個工廠類隘梨,用于獲取代理實(shí)例,這個代理實(shí)例代理了我們的目標(biāo)對象即被代理者舷嗡。
  2. newProxyInstance方法需要三個參數(shù)轴猎,第一個是目標(biāo)類的類加載器,第二個是目標(biāo)類實(shí)現(xiàn)的接口进萄,第三個是InvocationHandler接口捻脖,該接口有個invoke方法。
  3. 代理邏輯實(shí)際上就是invoke方法中的邏輯中鼠,在這個方法中可婶,我們首先打印了標(biāo)識代理執(zhí)行開始的語句,結(jié)果利用kotlin反射機(jī)制調(diào)用了被代理者的目標(biāo)方法援雇,最后我們打印了標(biāo)識代理執(zhí)行結(jié)束的語句矛渴。從這個流程可以看出,我們既可以在執(zhí)行被代理者業(yè)務(wù)邏輯之前做一些工作,也可以在其之后做一些工作具温,這也是代理模式的另一部分優(yōu)勢蚕涤!面向切面編程(AOP)就是利用了動態(tài)代理這一思想來實(shí)現(xiàn)的。
  4. 在invoke方法中铣猩,我們對參數(shù)args進(jìn)行了處理揖铜,主要是kotlin中method.invoke方法接收兩個參數(shù),第一個就是我們的被代理對象达皿,第二個則是被代理的方法參數(shù)天吓,但是由于這個參數(shù)是可變參數(shù),而我們傳入的實(shí)際上是個數(shù)組峦椰,這如果放在java中龄寞,編譯器可以幫我們自動處理,但是kotlin中我們還需要顯示進(jìn)行處理们何,處理方法就是在數(shù)組之前加上星號(*)萄焦,這樣數(shù)組就被轉(zhuǎn)換為了可變參數(shù)。
  5. 上述提到了method冤竹,那么這個到底是什么拂封?實(shí)際上這個就是我們的接口方法,這也是動態(tài)代理為什么要求目標(biāo)者實(shí)現(xiàn)接口的原因鹦蠕。打印method的值如下所示:
public abstract void proxy.IOperation.operation()

最后冒签,前面我們說過,動態(tài)代理能夠在一套代理代碼下代理各種對象的執(zhí)行钟病,那么假如現(xiàn)在我們新增一個代理對象萧恕,并且要代理該對象中的有參方法,該怎么實(shí)現(xiàn)肠阱?

很簡單票唆,我們首先定義下我們的代理目標(biāo),如下所示:

//定義一個接口
interface IOperation2 {
//定義了一個包含有1個入?yún)⒌姆椒?    fun operation(operationDesc: String)
}
//目標(biāo)對象
class RealObject2 : IOperation2 {
    override fun operation(operationDesc: String) {
        println("operation in Real object: " + operationDesc)
    }
}
//測試方法
 val operation: IOperation2 = ProxyFactory.getInstance(RealObject2()) as IOperation2
    operation.operation(" just do it!")

通過上面的代碼我們就完成了重新代理一個新對象的需求屹徘,我們發(fā)現(xiàn)沒有對ProxyFactory進(jìn)行任何變更走趋!這就是動態(tài)代理相對于靜態(tài)代理的優(yōu)勢,上面代碼執(zhí)行完后噪伊,打印如下所示:

proxy start, you can do some work here
operation in Real object:  just do it!
proxy end, you can also do some work here

至此簿煌,本篇文章闡述完畢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鉴吹,一起剝皮案震驚了整個濱河市姨伟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌豆励,老刑警劉巖夺荒,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡般堆,警方通過查閱死者的電腦和手機(jī)在孝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門诚啃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淮摔,“玉大人,你說我怎么就攤上這事始赎『统龋” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵造垛,是天一觀的道長魔招。 經(jīng)常有香客問我,道長五辽,這世上最難降的妖魔是什么办斑? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮杆逗,結(jié)果婚禮上乡翅,老公的妹妹穿的比我還像新娘。我一直安慰自己罪郊,他們只是感情好蠕蚜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著悔橄,像睡著了一般靶累。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上癣疟,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天挣柬,我揣著相機(jī)與錄音,去河邊找鬼睛挚。 笑死邪蛔,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的竞川。 我是一名探鬼主播店溢,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼委乌!你這毒婦竟也來了床牧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤遭贸,失蹤者是張志新(化名)和其女友劉穎戈咳,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡著蛙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年删铃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踏堡。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡猎唁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出顷蟆,到底是詐尸還是另有隱情诫隅,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布帐偎,位于F島的核電站逐纬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏削樊。R本人自食惡果不足惜豁生,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漫贞。 院中可真熱鬧甸箱,春花似錦、人聲如沸绕辖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仪际。三九已至围小,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間树碱,已是汗流浹背肯适。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留成榜,地道東北人框舔。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像赎婚,于是被迫代替她去往敵國和親刘绣。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容