老司機帶你深入淺出Java反射

反射,它就像是一種魔法捧毛,引入運行時自省能力,賦予了 Java 語言令人意外的活力,通過運行時操作元數(shù)據或對象呀忧,Java 可以靈活地操作運行時才能確定的信息

這里筆者就深入淺出總結下Java反射师痕,若有不正確地方,感謝評論區(qū)指正交流~ 建議打開idea而账,寫一個Java反射的demo胰坟,跟著調試,效果會更好 :)

反射的概念是由Smith在1982年首次提出的福扬,主要是指程序可以訪問腕铸、檢測和修改它本身狀態(tài)或行為的一種能力惜犀。有了反射铛碑,使Java相對于C、C++等語言就有了很強大的操作對象屬性及其方法的能力虽界,注意汽烦,反射與直接調用對象方法和屬性相比,性能有一定的損耗莉御,但是如果不是用在對性能有很強的場景下撇吞,反射都是一個很好且靈活的選擇。

說到反射礁叔,首先要了解什么是Class牍颈。每個類都會產生一個對應的Class對象,一般保存在.class文件中琅关。所有類都是在對其第一次使用時煮岁,動態(tài)加載到JVM的,當程序創(chuàng)建一個對類的靜態(tài)成員的引用時涣易,就會加載這個類画机。Class對象僅在需要的時候才會加載,static初始化是在類加載時進行的新症。類加載時步氏,類加載器首先會檢查這個類的Class對象是否已被加載過,如果尚未加載徒爹,默認的類加載器就會根據類名查找對應的.class文件荚醒。

class文件

任何一個Class文件都對應著唯一一個類或接口的信息(這里的類包括抽象類哈),但反過來隆嗅,類或接口信息并不一定都定義在文件里(比如類或接口可能動態(tài)生成界阁,Spring中AOP的實現(xiàn)中就有可能動態(tài)生成代理類)。Class文件是一組以8字節(jié)為基礎單位的二進制文件榛瓮,各個數(shù)據項嚴格按照順序緊湊著排列铺董,中間幾乎沒有任何分隔符,也就是說整個Class文件存儲的幾乎都是程序運行所需的必要數(shù)據。

想在運行時使用類型信息精续,必須獲取對象(比如類Base對象)的Class對象的引用坝锰,使用Class.forName(“Base”)可以實現(xiàn)該目的,或者使用Base.class重付。注意顷级,有一點很有趣,使用”.class”來創(chuàng)建Class對象的引用時确垫,不會自動初始化該Class對應類弓颈,使用forName()會自動初始化該Class對應類。使用”.class”不會自動初始化是因為被延遲到了對靜態(tài)方法(構造器隱私地是靜態(tài)的)或者非常數(shù)靜態(tài)域進行首次引用時才進行删掀。

為了使用類而做的準備工作一般有以下3個步驟:

加載:由類加載器完成翔冀,找到對應的字節(jié)碼,創(chuàng)建一個Class對象

鏈接:驗證類中的字節(jié)碼披泪,為靜態(tài)域分配空間

初始化:如果該類有超類纤子,則對其初始化,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊

如果不知道某個對象的確切類型款票,RTTI可以告訴你瑟押,但是有一個前提:這個類型在編譯時必須已知遂赠,這樣才能使用RTTI來識別它步做。而Class類與java.lang.reflect類庫一起對反射進行了支持夭苗,該類庫包含F(xiàn)ield、Method和Constructor類缚够,這些類的對象由JVM在啟動時創(chuàng)建幔妨,用以表示未知類里對應的成員。

反射機制并沒有什么神奇之處潮瓶,當通過反射與一個未知類型的對象打交道時陶冷,JVM只是簡單地檢查這個對象,看它屬于哪個特定的類毯辅。因此埂伦,那個類的.class對于JVM來說必須是可獲取的,要么在本地機器上思恐,要么從網絡獲取沾谜。所以對于RTTI和反射之間的真正區(qū)別只在于:

RTTI:編譯器在編譯時打開和檢查.class文件

反射:運行時打開和檢查.class文件

反射應用實踐

反射獲取對象中所有屬性值:

public class Person {

? ? private String name;

? ? private int age;

? ? // ...

}

Person person = new Person();

person.setName("luo");

person.setAge(25);

try {

? ? Class clazz = person.getClass();

? ? Field[] fields = clazz.getDeclaredFields();

? ? for (Field field : fields) {

? ? ? ? field.setAccessible(true);

? ? ? ? System.out.println(field.getType() + " | " + field.getName() + " = " + field.get(person));

? ? }

? ? // 通過反射獲取某一個方法

? ? Method method = clazz.getMethod("setName", String.class);

? ? method.invoke(person, "bei");

} catch (Exception e) {

? ? e.printStackTrace();

}

平常的項目開發(fā)基本很少與反射打交道,因為框架已經幫我們做了很多的事情了胀莹。但這不能說明反射機制沒有用基跑,實際上有很多設計、開發(fā)都與反射機制有關描焰,例如模塊化的開發(fā)媳否,通過反射去調用對應的字節(jié)碼栅螟;動態(tài)代理設計模式也采用了反射機制,還有我們日常使用的 Spring/Hibernate 等框架篱竭,也是利用CGLIB 反射機制才得以實現(xiàn)力图。

反射技術在在框架和中間件技術應用較多,有一句老話就是反射是Java框架的基石掺逼。典型的使用就是Spring的IoC實現(xiàn)吃媒,不管對象誰管理創(chuàng)建,只要我能用就行吕喘。再比如RPC技術可以借助于反射來實現(xiàn)赘那,本地主機將要遠程調用的對象方法等信息發(fā)送給遠程主機,這些信息包括class名氯质、方法名募舟、方法參數(shù)類型、方法入參等病梢,遠程主機接收到這些信息后就可以借助反射來獲取并執(zhí)行對象方法胃珍,然后將結果返回即可梁肿。

說了那么多蜓陌,那么Java反射是如何實現(xiàn)的呢?簡單來說Java反射就是靠JVM和Class相關類來實現(xiàn)的吩蔑,Class相關類包括Field钮热、Method和Constructor類等。類加載器加載完成一個類之后烛芬,會生成類對應的Class對象隧期、Field對象、Method對象赘娄、Constructor對象仆潮,這些對象都保存在JVM(方法區(qū))中,這也說明了反射必須在加載類之后進行的原因遣臼。使用反射時性置,其實就是與上述所說的這幾個對象打交道呀(貌似Java反射也就這么一回事哈)。

既然了解了Java反射原理揍堰,可以試想一下C++為什么沒有反射呢鹏浅,想讓C++擁有反射該如何做呢?Java相對于C++實現(xiàn)反射最重要的差別就是Java可以依靠JVM這一悍將屏歹,可以由JVM保存對象的相關信息隐砸,然后應用程序使用時直接從JVM中獲取使用。但是C++編譯后直接變成了機器碼了蝙眶,貌似類或者對象的啥信息都沒了季希。。。 其實想讓C++有用反射能力式塌,就需要保存能夠操作類方法武通、類構造方法、類屬性的這些信息珊搀,這些信息要么由應用程序自己來做冶忱,要么由第三方工具來保存,然后應用程序使用從它那里獲取境析,這些信息可以通過(函數(shù))指針來記錄囚枪,使用時通過指針來調用。

反射機制

這里我們以Method.invoke流程來分析反射流程:

public class Person {

? ? private String name;

? ? private int age;

? ? public String getName() {

? ? ? ? return name;

? ? }

? ? // 其他setter/getter方法

}

public static void main(String[] args) throws Exception {

? ? Person person = new Person();

? ? person.setName("luo");

? ? person.setAge(26);

? ? for (int i = 0; i < 20; i++) {

? ? ? ? Method method = Person.class.getMethod("getName");

? ? ? ? System.out.println(method.invoke(person));

? ? }

}

以上代碼通過反射調用person對象中的方法劳淆,下面跟著源碼看下Method.invoke的執(zhí)行流程:

// Method

public Object invoke(Object obj, Object... args)

? ? ? ? throws IllegalAccessException, IllegalArgumentException,

? ? ? ? ? InvocationTargetException

{

? ? MethodAccessor ma = methodAccessor;? ? ? ? ? ? // read volatile

? ? if (ma == null) {

? ? ? ? // 這里會調用reflectionFactory.newMethodAccessor(this)創(chuàng)建一個新的MethodAccessor

? ? ? ? // 并賦值給methodAccessor链沼,下次就不會進入到這里了

? ? ? ? // ma實際類型是DelegatingMethodAccessorImpl,代理對目標方法的調用

? ? ? ? ma = acquireMethodAccessor();

? ? }

? ? return ma.invoke(obj, args);

}

class DelegatingMethodAccessorImpl extends MethodAccessorImpl {

? ? private MethodAccessorImpl delegate;

? ? DelegatingMethodAccessorImpl(MethodAccessorImpl var1) {

? ? ? ? this.setDelegate(var1);

? ? }

? ? public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {

? ? ? ? return this.delegate.invoke(var1, var2);

? ? }

? ? void setDelegate(MethodAccessorImpl var1) {

? ? ? ? this.delegate = var1;

? ? }

}

// NativeMethodAccessorImpl

public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {

? ? // ReflectionFactory.inflationThreshold()默認15沛鸵,如果某一個Method反射調用超過15次括勺,

? ? // 則自動生成GeneratedMethodAccessor賦值給DelegatingMethodAccessorImpl.delegate

? ? if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {

? ? ? ? // 通過asm自動生成MethodAccessorImpl的實現(xiàn)類GeneratedMethodAccessor

? ? ? ? MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());

? ? ? ? this.parent.setDelegate(var3);

? ? }

? ? return invoke0(this.method, var1, var2);

}

// native方法,jni方式調用對應方法曲掰,調用的是對應的java方法

private static native Object invoke0(Method var0, Object var1, Object[] var2);

下面分別看下NativeMethodAccessorImpl和GeneratedMethodAccessor的調用棧信息:

Java默認在執(zhí)行Method.invoke超過15次時(通過-Dsun.reflect.inflationThreshold可更改次數(shù)值疾捍,每個方法對應一個Method對象),JVM會通過asm生成GeneratedMethodAccessor類栏妖,由該類調用對應的method方法乱豆。執(zhí)行NativeMethodAccessorImpl.invoke是通過調用JNI方法,在JNI方法中再調用對應的java method方法吊趾,這種方式相對于使用GeneratedMethodAccessor.invoe方法來說宛裕,前者性能較弱,原因有以下幾點:

針對本地方法论泛,jvm無法優(yōu)化揩尸,無法動態(tài)inline,其他高級的優(yōu)化方案都無法優(yōu)化jni屁奏。

執(zhí)行native涉及到運行棧切換(虛擬機棧切換到本地方法棧)岩榆,如果本地方法中再調用java方法是有一定的開銷的,肯定比不上Java中調用Java方法了袁。

二者內存模型不一樣朗恳,參數(shù)需要轉換,比如字符串载绿,數(shù)組粥诫,復雜結構。轉換成本非常高崭庸。此開銷和調用接口參數(shù)有關怀浆。

在默認情況下谊囚,方法的反射調用為委派實現(xiàn),委派給本地實現(xiàn)來進行方法調用执赡。在調用超過 15 次之后镰踏,委派實現(xiàn)便會將委派對象切換至動態(tài)實現(xiàn)。這個動態(tài)的字節(jié)碼是在Java運行過程中通過ASM自動生成的沙合,它將直接使用 invoke 指令來調用目標方法奠伪。Java實現(xiàn)的版本在初始化時需要較多時間,但長久來說性能較好首懈;native版本正好相反绊率,啟動時相對較快,但運行時間長了之后速度就比不過Java版了究履。這是HotSpot的優(yōu)化方式帶來的性能特性滤否,同時也是許多虛擬機的共同點:跨越native邊界會對優(yōu)化有阻礙作用,它就像個黑箱一樣讓虛擬機難以分析也將其內聯(lián)最仑,于是運行時間長了之后反而是托管版本的代碼更快些藐俺。?

GeneratedMethodAccessor機制

默認method.invoke調用超過15次,會調用MethodAccessorGenerator.generate生成對應GeneratedMethodAccessorN類泥彤,代碼如下:

// MethodAccessorGenerator

private MagicAccessorImpl generate(final Class<?> var1, String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, int var6, boolean var7, boolean var8, Class<?> var9) {

? ? ByteVector var10 = ByteVectorFactory.create();

? ? this.asm = new ClassFileAssembler(var10);

? ? this.declaringClass = var1;

? ? this.parameterTypes = var3;

? ? this.returnType = var4;

? ? this.modifiers = var6;

? ? this.isConstructor = var7;

? ? this.forSerialization = var8;

? ? this.asm.emitMagicAndVersion();

? ? short var11 = 42;

? ? boolean var12 = this.usesPrimitiveTypes();

? ? if (var12) {

? ? ? ? var11 = (short)(var11 + 72);

? ? }

? ? if (var8) {

? ? ? ? var11 = (short)(var11 + 2);

? ? }

? ? var11 += (short)(2 * this.numNonPrimitiveParameterTypes());

? ? this.asm.emitShort(add(var11, (short)1));

? ? final String var13 = generateName(var7, var8);

? ? this.asm.emitConstantPoolUTF8(var13);

? ? this.asm.emitConstantPoolClass(this.asm.cpi());

? ? this.thisClass = this.asm.cpi();

? ? if (var7) {

? ? ? ? if (var8) {

? ? ? ? ? ? this.asm.emitConstantPoolUTF8("sun/reflect/SerializationConstructorAccessorImpl");

? ? ? ? } else {

? ? ? ? ? ? this.asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl");

? ? ? ? }

? ? } else {

? ? ? ? this.asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl");

? ? }

? ? this.asm.emitConstantPoolClass(this.asm.cpi());

? ? this.superClass = this.asm.cpi();

? ? this.asm.emitConstantPoolUTF8(getClassName(var1, false));

? ? this.asm.emitConstantPoolClass(this.asm.cpi());

? ? this.targetClass = this.asm.cpi();

? ? short var14 = 0;

? ? if (var8) {

? ? ? ? this.asm.emitConstantPoolUTF8(getClassName(var9, false));

? ? ? ? this.asm.emitConstantPoolClass(this.asm.cpi());

? ? ? ? var14 = this.asm.cpi();

? ? }

? ? this.asm.emitConstantPoolUTF8(var2);

? ? this.asm.emitConstantPoolUTF8(this.buildInternalSignature());

? ? this.asm.emitConstantPoolNameAndType(sub(this.asm.cpi(), (short)1), this.asm.cpi());

? ? if (this.isInterface()) {

? ? ? ? this.asm.emitConstantPoolInterfaceMethodref(this.targetClass, this.asm.cpi());

? ? } else if (var8) {

? ? ? ? this.asm.emitConstantPoolMethodref(var14, this.asm.cpi());

? ? } else {

? ? ? ? this.asm.emitConstantPoolMethodref(this.targetClass, this.asm.cpi());

? ? }

? ? this.targetMethodRef = this.asm.cpi();

? ? if (var7) {

? ? ? ? this.asm.emitConstantPoolUTF8("newInstance");

? ? } else {

? ? ? ? this.asm.emitConstantPoolUTF8("invoke");

? ? }

? ? this.invokeIdx = this.asm.cpi();

? ? if (var7) {

? ? ? ? this.asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;");

? ? } else {

? ? ? ? this.asm.emitConstantPoolUTF8("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");

? ? }

? ? this.invokeDescriptorIdx = this.asm.cpi();

? ? this.nonPrimitiveParametersBaseIdx = add(this.asm.cpi(), (short)2);

? ? for(int var15 = 0; var15 < var3.length; ++var15) {

? ? ? ? Class var16 = var3[var15];

? ? ? ? if (!isPrimitive(var16)) {

? ? ? ? ? ? this.asm.emitConstantPoolUTF8(getClassName(var16, false));

? ? ? ? ? ? this.asm.emitConstantPoolClass(this.asm.cpi());

? ? ? ? }

? ? }

? ? this.emitCommonConstantPoolEntries();

? ? if (var12) {

? ? ? ? this.emitBoxingContantPoolEntries();

? ? }

? ? if (this.asm.cpi() != var11) {

? ? ? ? throw new InternalError("Adjust this code (cpi = " + this.asm.cpi() + ", numCPEntries = " + var11 + ")");

? ? } else {

? ? ? ? this.asm.emitShort((short)1);

? ? ? ? this.asm.emitShort(this.thisClass);

? ? ? ? this.asm.emitShort(this.superClass);

? ? ? ? this.asm.emitShort((short)0);

? ? ? ? this.asm.emitShort((short)0);

? ? ? ? this.asm.emitShort((short)2);

? ? ? ? this.emitConstructor();

? ? ? ? this.emitInvoke();

? ? ? ? this.asm.emitShort((short)0);

? ? ? ? var10.trim();

? ? ? ? final byte[] var17 = var10.getData(); // class數(shù)據

? ? ? ? return (MagicAccessorImpl)AccessController.doPrivileged(new PrivilegedAction<MagicAccessorImpl>() {

? ? ? ? ? ? public MagicAccessorImpl run() {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? // 生成反射類GeneratedMethodAccessorN欲芹,對應的classLoader為DelegatingClassLoader

? ? ? ? ? ? ? ? ? ? return (MagicAccessorImpl)ClassDefiner.defineClass(var13, var17, 0, var17.length, var1.getClassLoader()).newInstance();

? ? ? ? ? ? ? ? } catch (IllegalAccessException | InstantiationException var2) {

? ? ? ? ? ? ? ? ? ? throw new InternalError(var2);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? });

? ? }

}

View Code

注意:生成對應GeneratedMethodAccessorN類,其對應的classLoader是DelegatingClassLoader全景,生成的GeneratedMethodAccessorN類是放在永久代的耀石,那么就會產生一個問題,如果數(shù)量過多爸黄,則會占用永久代太多空間(java8中已沒有永久代空間,類數(shù)據放在直接內存中)揭鳞。

生成的GeneratedMethodAccessorN類是什么樣的呢炕贵?如下所示:在此我向大家推薦一個架構學習交流群。交流學習群號:821169538 ?里面會分享一些資深架構師錄制的視頻錄像:有Spring野崇,MyBatis称开,Netty源碼分析,高并發(fā)乓梨、高性能鳖轰、分布式、微服務架構的原理扶镀,JVM性能優(yōu)化蕴侣、分布式架構等這些成為架構師必備的知識體系。還能領取免費的學習資源臭觉,目前受益良多昆雀。

package sun.reflect;

import com.luo.test.InvokeBean;

import java.lang.reflect.InvocationTargetException;

public class GeneratedMethodAccessor1 extends MethodAccessorImpl {

? ? public GeneratedMethodAccessor1() {

? ? }

? ? public Object invoke(Object var1, Object[] var2) throws InvocationTargetException {

? ? ? ? if (var1 == null) {

? ? ? ? ? ? throw new NullPointerException();

? ? ? ? } else {

? ? ? ? ? ? InvokeBean var10000;

? ? ? ? ? ? Integer var10001;

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? var10000 = (InvokeBean)var1;

? ? ? ? ? ? ? ? if (var2.length != 1) {

? ? ? ? ? ? ? ? ? ? throw new IllegalArgumentException();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? var10001 = (Integer)var2[0];

? ? ? ? ? ? } catch (NullPointerException | ClassCastException var4) {

? ? ? ? ? ? ? ? throw new IllegalArgumentException(var4.toString());

? ? ? ? ? ? }

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? return var10000.test(var10001);

? ? ? ? ? ? } catch (Throwable var3) {

? ? ? ? ? ? ? ? throw new InvocationTargetException(var3);

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末辱志,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子狞膘,更是在濱河造成了極大的恐慌揩懒,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挽封,死亡現(xiàn)場離奇詭異已球,居然都是意外死亡,警方通過查閱死者的電腦和手機辅愿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門和悦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渠缕,你說我怎么就攤上這事鸽素。” “怎么了亦鳞?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵馍忽,是天一觀的道長。 經常有香客問我燕差,道長遭笋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任徒探,我火速辦了婚禮瓦呼,結果婚禮上,老公的妹妹穿的比我還像新娘测暗。我一直安慰自己央串,他們只是感情好,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布碗啄。 她就那樣靜靜地躺著质和,像睡著了一般。 火紅的嫁衣襯著肌膚如雪稚字。 梳的紋絲不亂的頭發(fā)上饲宿,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音胆描,去河邊找鬼瘫想。 笑死,一個胖子當著我的面吹牛昌讲,可吹牛的內容都是我干的国夜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼剧蚣,長吁一口氣:“原來是場噩夢啊……” “哼支竹!你這毒婦竟也來了旋廷?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤礼搁,失蹤者是張志新(化名)和其女友劉穎饶碘,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體馒吴,經...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡扎运,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了饮戳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豪治。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖扯罐,靈堂內的尸體忽然破棺而出负拟,到底是詐尸還是另有隱情,我是刑警寧澤歹河,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布掩浙,位于F島的核電站,受9級特大地震影響秸歧,放射性物質發(fā)生泄漏厨姚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一键菱、第九天 我趴在偏房一處隱蔽的房頂上張望谬墙。 院中可真熱鬧,春花似錦经备、人聲如沸拭抬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玖喘。三九已至,卻和暖如春蘑志,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贬派。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工急但, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搞乏。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓波桩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親请敦。 傳聞我的和親對象是個殘疾皇子镐躲,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內容

  • Java設計模式——代理模式 代理模式主要分為接口储玫,委托類,代理類 接口:規(guī)定具體方法委托類:實現(xiàn)接口萤皂,完成具體的...
    vczyh閱讀 661評論 0 0
  • 反射的demopackage com.java.reflect;import java.lang.reflect....
    5473631d8226閱讀 2,848評論 0 2
  • #深度理解Android InstantRun原理以及源碼分析 @Author 莫川 ##Instant Run官...
    鄭海波mobctrl閱讀 1,547評論 2 7
  • 今天真的是深刻體會到一件事:機會來了撒穷,放手的那一個永遠都是自己。 時間要追溯到中秋節(jié)左右裆熙,當時受各位大咖的刺激腦子...
    小獅子Emily閱讀 454評論 3 1
  • 前天晚上失眠端礼,早上將近五點的時候入睡。八點出頭便醒來入录。昨晚一點半已經眼皮酥軟蛤奥,手機一拋,沉入漫漫虛無僚稿。 這一覺不短...
    minilily閱讀 465評論 0 1