玩轉(zhuǎn)Class之Class的各種騷操作(反射封裝必看)

簡介

Class類表示正在運(yùn)行的Java應(yīng)用程序中的類和接口掘鄙,枚舉和基本數(shù)據(jù)類型院究,我們可以從中獲取到類的一切相關(guān)信息洽瞬,包括字段,方法业汰,名稱伙窃,父類,接口等

常用方法介紹

名稱獲取

此類方法用于獲取類的名稱信息
getName()方法

  • 如果是一個(gè)實(shí)體類样漆,則會(huì)返回完整包名路徑名稱,
    例如位于com.hj.testclass包下的student類为障,則會(huì)返回com.hj.testclass.student
  • 如果是一個(gè)數(shù)組類型,則返回內(nèi)部嵌套深度的一個(gè)或多個(gè)"["字符,后面拼接上基本數(shù)據(jù)類型的二進(jìn)制名稱,二進(jìn)制名稱表如下:
Element Type Encoding
boolean Z
byte B
char C
class or interface Lclassname
double D
float F
int I
long J
short S

示例:

(new long[1][2][3]).getClass().getName()
輸出:
[[[J
  • 如果是基本數(shù)據(jù)類型鳍怨,則會(huì)返回?cái)?shù)據(jù)類型的關(guān)鍵字
byte.class.getName()
輸出:
byte

getSimpleName()方法
返回源代碼中給出的基礎(chǔ)類的簡單名稱呻右。 如果基礎(chǔ)類是匿名的,則返回一個(gè)空字符串鞋喇。

Student student = new Student();
Class mClass = student.getClass();
log("getSimpleName:"+mClass.getSimpleName());
輸出:
Student

getPackage()方法
返回包名信息

注解相關(guān)

可獲取此類是否是注解声滥,是否包含某注解并獲取到其對象,獲取全部注解

方法 作用
getAnnotation(Class<A> annotationClass) 獲取傳入的注解對象侦香,如果不存在落塑,則返回null
getAnnotations() 返回此類上的所有注解
getAnnotationsByType(Class<A> annotationClass) 返回傳入的注解對象數(shù)組,與getAnnotation()的區(qū)別是檢測傳入的注解是否是重復(fù)元素
isAnnotation() 判斷這個(gè)類是否是一個(gè)注解類
isAnnotationPresent(Class<? extends Annotation> annotationClass) 是否包含傳入的注解類,效果與getAnnotation()!=null相同

舉個(gè)栗子:
新建一個(gè)注解對象:

/**
 * Created by hj on 2019/1/10.
 * 說明:
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}

新建一個(gè)Student并加入@MyAnnotation注解:

/**
 * Created by hj on 2019/1/10.
 * 說明:
 */
@MyAnnotation
public class Student{

    public static void main(String[] args) {
        Student student = new Student();
        Class studentClass = student.getClass();
        boolean isAnnotation = studentClass.isAnnotation();
        log(studentClass.getSimpleName()+"是否是注解類:"+isAnnotation);
        boolean isContainAnnotation = studentClass.isAnnotationPresent(MyAnnotation.class);
        log(studentClass.getSimpleName()+"是否包含MyAnnotation注解類:"+isContainAnnotation);
        Annotation annotation = studentClass.getAnnotation(MyAnnotation.class);
        Annotation[] annotations = studentClass.getAnnotations();
        if (annotation != null) {
            log("獲取指定的MyAnnotation類:"+annotation.toString());
        }
        if (annotations.length > 0){
            log("獲取注解集合中的第一個(gè)元素:"+annotations[0].toString());
        }
    }

    private static void log(String value) {
        System.out.print(value);
    }
}

打庸藓:

Student是否是注解類:false
Student是否包含MyAnnotation注解類:true
獲取指定的MyAnnotation類:@jie.com.imageoptimize.mclass.MyAnnotation()
獲取注解集合中的第一個(gè)元素:@jie.com.imageoptimize.mclass.MyAnnotation()

構(gòu)造方法相關(guān)

在介紹構(gòu)造方法之前憾赁,先介紹一個(gè)類Constructor,它的作用是提供一個(gè)類的單個(gè)構(gòu)造方法的信息訪問,如果一個(gè)類有兩個(gè)構(gòu)造方法伴逸,那么這個(gè)類就會(huì)對應(yīng)有兩個(gè)Constructor類缠沈,它可以使用newInstance方法來進(jìn)行類的構(gòu)造方法實(shí)現(xiàn)并進(jìn)行擴(kuò)展,但如果發(fā)生縮小轉(zhuǎn)換則會(huì)拋出IllegalArgumentException異常错蝴,比如這個(gè)類有兩個(gè)構(gòu)造參數(shù)卻只傳入一個(gè)洲愤,就會(huì)拋異常。
獲取Constructor信息的方法有:

方法 作用
getConstructors() 返回這個(gè)類的公共構(gòu)造函數(shù)的 Constructor對象的數(shù)組
getConstructor(Class<?>..parameterTypes) 傳入一個(gè)指定的參數(shù)類型來獲取特定的公共構(gòu)造方法類
getDeclaredConstructors() 與getConstructors的區(qū)別是返回所有的構(gòu)造方法數(shù)組顷锰,不限于public protected private
getDeclaredConstructor(Class<?>..parameterTypes) 與getConstructor的區(qū)別是會(huì)返回所有類型的構(gòu)造方法

接下來介紹一下Constructor類的newInstance方法柬赐,這個(gè)方法說白了就是執(zhí)行構(gòu)造函數(shù)的,你傳入一個(gè)當(dāng)前構(gòu)造函數(shù)的類型的值進(jìn)去官紫,那么就會(huì)執(zhí)行這個(gè)類的構(gòu)造方法肛宋。
下面舉一個(gè)反射調(diào)用構(gòu)造方法的栗子:
新建一個(gè)Student類,分別添加兩個(gè)私有構(gòu)造方法束世,一個(gè)公有構(gòu)造方法酝陈,并添加兩個(gè)參數(shù),在構(gòu)造方法處執(zhí)行打印邏輯毁涉,代碼如下:

/**
 * Created by hj on 2019/1/10.
 * 說明:
 */

public class Student {

    private String name;
    private int age;

    private Student() {

    }

    public Student(String name) {
        this.name = name;
        log("公有構(gòu)造方法執(zhí)行了沉帮,打印傳入的名稱為:"+name);
    }

    private Student(int age){
        this.age = age;
        log("私有構(gòu)造方法執(zhí)行了,打印傳入的年齡為:"+age);
    }

    public static void main(String[] args) {
        Student student = new Student();
        Class mClass = student.getClass();
        Constructor[] constructors = mClass.getConstructors();
        log("獲取" + mClass.getSimpleName() + "的公共構(gòu)造方法數(shù)量為:" + constructors.length);
        try {
            Constructor constructor = mClass.getConstructor(String.class);

            constructor.newInstance("張三");

        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }


        Constructor[] declaredConstructors = mClass.getDeclaredConstructors();
        log("獲取" + mClass.getSimpleName() + "的所有構(gòu)造方法數(shù)量為:" + declaredConstructors.length);
        try {
            Constructor constructor = mClass.getDeclaredConstructor(int.class);

            constructor.newInstance(18);

        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private static void log(String value) {
        System.out.print(value);
    }
}

打印結(jié)果如下:

獲取Student的公共構(gòu)造方法數(shù)量為:1
公有構(gòu)造方法執(zhí)行了贫堰,打印傳入的名稱為:張三
獲取Student的所有構(gòu)造方法數(shù)量為:3
私有構(gòu)造方法執(zhí)行了穆壕,打印傳入的年齡為:18

怎么樣,反射調(diào)用類的構(gòu)造方法技能get到了嗎

字段相關(guān)

此類型方法是用的最多的一種其屏,希望小伙伴們可以熟練掌握
在介紹字段方法之前喇勋,先介紹一個(gè)類Field,它用于保存,修改字段的信息偎行,甚至可以修改字段的訪問權(quán)限,常用的方法如下:

方法 作用
getName() 獲取字段名稱
get() 傳入需要獲取的值的類的對象川背,獲取該字段的值,返回object類型贰拿,使用的時(shí)候需要做類型判斷
getBoolean(),getInt()... 獲取指定類型的字段值
set(),setBoolean()... 將指定的類的指定值設(shè)置為新值
isAccessible() 判斷此字段是否有訪問權(quán)限
setAccessible() 設(shè)置字段的權(quán)限渗常,為true代表可以訪問

接下來再來看看如何使用Class來獲取字段信息:

方法 作用
getFields() 獲取所有的公共字段
getField() 傳入字段的名稱壮不,返回公共字段對象
getDeclaredFields() 獲取所有字段,返回一個(gè)Field數(shù)組
getDeclaredField 傳入字段的名稱皱碘,返回字段對象,無訪問限制

下面舉個(gè)獲取字段名稱和內(nèi)容的例子询一,并分享一個(gè)常用的套路寫法,一般的都可以按照這個(gè)套路來寫:

/**
 * Created by hj on 2019/1/10.
 * 說明:
 */

public class Student {

    public String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {
        Student student = new Student("張三", 18);
        setAllComponentsName(student);
    }
   
    private static void setAllComponentsName(Object f) {
        Field[] fields = f.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 對于每個(gè)屬性癌椿,獲取屬性名
            String varName = field.getName();
            try {
                // 獲取原來的訪問控制權(quán)限
                boolean accessFlag = field.isAccessible();
                // 修改訪問控制權(quán)限
                field.setAccessible(true);
                // 獲取在對象f中屬性fields[i]對應(yīng)的對象中的變量
                Object o = field.get(f);
                if (!"".equals(varName)) {
                    //這里可以處理相關(guān)邏輯
                    if (o != null) {
                        if (o instanceof String) {
                            String value = (String) o;
                            log("String類型字段:" + value);
                        } else if (o instanceof Integer) {
                            int value = (int) o;
                            log("int類型字段:" + value);
                        }
                    }
                }
                // 恢復(fù)訪問控制權(quán)限
                field.setAccessible(accessFlag);
            } catch (IllegalArgumentException | IllegalAccessException ex) {
                ex.printStackTrace();
            }
        }
    }

    private static void log(String value) {
        System.out.print(value);
    }
}

打印結(jié)果:

String類型字段:張三
int類型字段:18

一般都是先將權(quán)限修改為true,再去讀取字段內(nèi)容健蕊,隨后進(jìn)行邏輯處理,最后記得將權(quán)限改回來.

方法相關(guān)

此類型方法也是用的比較多的一種踢俄,是反射調(diào)用方法的關(guān)鍵
class中關(guān)于方法的封裝都是給Method類來操作的缩功,它可以獲取,修改都办,執(zhí)行類的方法嫡锌,核心方法就是invoke(),作用是執(zhí)行方法琳钉,第一個(gè)參數(shù)是需要執(zhí)行的方法的類對象势木,隨后是需要執(zhí)行的方法參數(shù)值苞笨。
獲取Method對象有四個(gè)方法:

方法 作用
getMethods() 獲取類的所有公共方法
getMethod() 獲取指定公共方法离唬,傳入一個(gè)方法的名稱與參數(shù)的類型
getDeclaredMethods() 獲取類的所有方法
getDeclaredMethod() 獲取類的指定方法,傳入?yún)?shù)同getMethod()

舉個(gè)例子镣屹,將本來為張三的名稱修改為李四:

/**
 * Created by hj on 2019/1/10.
 * 說明:
 */

public class Student {

    public String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private void setName(String name){
        this.name = name;
        log("設(shè)置的名稱為:"+name);
    }

    public static void main(String[] args) {
        Student student = new Student("張三", 18);
        Class mClass = student.getClass();
        try {
            Method method = mClass.getDeclaredMethod("setName",String.class);
            method.invoke(student,"李四");
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }


    private static void log(String value) {
        System.out.print(value);
    }
}

打印結(jié)果:

設(shè)置的名稱為:李四

在項(xiàng)目中使用及皂,可以參考上面的Field類甫男,其實(shí)都是差不多的,一通百通验烧。

抽象,繼承,接口,泛型相關(guān)

此類方法雖然不常用到板驳,但在某些情況下能發(fā)揮意想不到的作用,在許多框架的源碼中也能看見它們的身影碍拆,如Gson$Gson$Types類,RetrofitUtil類等笋庄,來跟我學(xué)習(xí)一下把。

泛型
首先說說獲取類的泛型獲取倔监,先介紹一個(gè)接口Type,它是java中所有類型的通用接口,類型包括原始類型(class)菌仁,參數(shù)化類型(ParameterizedType)浩习,數(shù)組類型(GenericArrayType),類型變量(TypeVariable)和基本數(shù)據(jù)類型济丘。內(nèi)部實(shí)現(xiàn)了一個(gè)方法getTypeName(),用于返回類型參數(shù)信息谱秽,基于它向外擴(kuò)展的接口有:

  • ParameterizedType
    表示一個(gè)參數(shù)化類型洽蛀,說簡單點(diǎn)就是帶有參數(shù)類型的類型,如Collection ,如果帶有了類型疟赊,如Collection<String>郊供,那么就是說String將Collection參數(shù)化
  • GenericArrayType
    表示一個(gè)參數(shù)化類型或類型變量的數(shù)組類型,簡單說就是泛型A<T>[]或泛型數(shù)組T[]
  • TypeVariable
    所有類型變量的父接口,也就是我們定義抽象類中的那種K,E等泛型變量近哟,可以泛指任何類
  • WildcardType
    表示一個(gè)通配符表達(dá)驮审,例如?,? extends Integer
    Type可以通過classgetGenericInterfacesgetGenericSuperclass()方法來獲取
    這么說或許還有點(diǎn)抽象,ParameterizedTypeTypeVariable的概念或許還拎不太清吉执。下面通過示例來加深理解
    先來說說ParameterizedType
    舉個(gè)例子疯淫,先聲明一個(gè)帶泛型的超類Person
/**
 * Created by hj on 2019/1/10.
 * 說明:
 */

public class Person<T> {

    public T feature;

}

隨后定義一個(gè)實(shí)體類Feature

/**
 * Created by hj on 2019/1/10.
 * 說明:
 */
public class Feature {

}

最后申明一個(gè)Student并繼承Person,將Feature作為T傳入:

/**
 * Created by hj on 2019/1/10.
 * 說明:
 */

public class Student extends Person<Feature> {

    public static void main(String[] args) {
        Student student = new Student();
        Class mClass = student.getClass();
        Type type = mClass.getGenericSuperclass();
        //這里獲取的type為Person<Feature>,所以屬于ParameterizedType
        if (type instanceof ParameterizedType) {
            //父類的泛型可能有多個(gè)戳玫,這里只寫了一個(gè)熙掺,所以取第一個(gè)就行了,這里是取Person<Feature>中的Feature
            Class tClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
            log(tClass.getSimpleName());
        }
    }


    private static void log(String value) {
        System.out.print(value);
    }
}

因?yàn)?code>Class類也實(shí)現(xiàn)了Type接口咕宿,所以是可以強(qiáng)制轉(zhuǎn)化的
打印結(jié)果如下:

Feature

可以看到在此示例中币绩,只要是Person<>中傳入的參數(shù)是一個(gè)參數(shù)類型,不管是實(shí)體類府阀,String都行缆镣,只要是參數(shù)而沒有泛指意義,那么它就屬于ParameterizedType類型
接下來將代碼修改一下肌似,將Feature改為T,修改獲取邏輯:

/**
 * Created by hj on 2019/1/10.
 * 說明:
 */

public class Student<T> extends Person<T> {

    public static void main(String[] args) {
        Student student = new Student();
        Class mClass = student.getClass();
        Type type = mClass.getGenericSuperclass();
       //此時(shí)這里的type為Person<T>费就,而不是T,所以依舊屬于ParameterizedType
        if (type instanceof ParameterizedType) {
           //這里是從Person<T>中獲取T
            Type childType = ((ParameterizedType)type).getActualTypeArguments()[0];
            if (childType instanceof TypeVariable){
                log(((TypeVariable) childType).getName());
            }
        }
    }


    private static void log(String value) {
        System.out.print(value);
    }
}

輸出名稱為

T

不知道小伙伴們有沒有理解了川队,當(dāng)獲取的類型是泛指的力细,如T等,那么就是TypeVariable類型固额,當(dāng)類型是參數(shù)類型的眠蚂,例如由子類傳入的,具體化的斗躏,那么就是ParameterizedType類型逝慧。
接下來的GenericArrayTypeWildcardType就比較好理解了,將以上示例中的Person<T>改為Person<T[]>啄糙,將結(jié)果輸出笛臣,代碼如下:

/**
 * Created by hj on 2019/1/10.
 * 說明:
 */

public class Student<T> extends Person<T[]> {

    public static void main(String[] args) {
        Student student = new Student();
        Class mClass = student.getClass();
        Type type = mClass.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            //這里獲取到的類型為:T[],是一個(gè)數(shù)組類型,所以是GenericArrayType
            Type childType = ((ParameterizedType)type).getActualTypeArguments()[0];
            if (childType instanceof GenericArrayType){
                log(childType.toString());
            }
        }
    }


    private static void log(String value) {
        System.out.print(value);
    }
}

輸出名稱為:

T[]

WildcardType就不演示了隧饼,將T改為帶沈堡?的類型就可以了,如果封裝類型實(shí)在太復(fù)雜燕雁,教大家一個(gè)技巧诞丽,打個(gè)斷點(diǎn)鲸拥,啥類型都幫你顯示出來了。

斷點(diǎn)識(shí)別參數(shù)類型.png

一看就知道是ParameterizedType類型了僧免,哈哈哈刑赶,溜不溜。

這里給小伙伴一個(gè)作業(yè):
List<? extends T>[]中的List<? extends T>,? extends T懂衩,T ,List<? extends T>[]分別代表什么類型呢撞叨?

繼承
獲取父級(jí)類使用Class.getSuperclass()方法即可獲取父類的class

接口
獲取接口也比較簡單,一個(gè)接口實(shí)際上也是一個(gè)Class類勃痴,通過getInterfaces()獲取到一個(gè)Class數(shù)組谒所,而接口里聲明的方法可以通過獲取Method的方式來獲取,
舉個(gè)栗子,先創(chuàng)建一個(gè)MyInterface接口

/**
 * Created by hj on 2019/1/11.
 * 說明:
 */
public interface MyInterface {
    void getName();
}

Student類里實(shí)現(xiàn)并獲扰嫔辍:

/**
 * Created by hj on 2019/1/10.
 * 說明:
 */

public class Student implements MyInterface{

    public static void main(String[] args) {
        Student student = new Student();
        Class mClass = student.getClass();
        Class interfaces = mClass.getInterfaces()[0];
        try {
            Method methods = interfaces.getMethod("getName",null);
            log(methods.getName());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    private static void log(String value) {
        System.out.print(value);
    }

    @Override
    public void getName() {

    }
}

打印出方法名為:

getName

總結(jié)

只要拿到Class類劣领,我們就可以獲取到它的父類,接口铁材,方法尖淘,字段,以及泛型著觉,從而做一些解耦封裝村生,提高類的解耦性”穑或者在不修改源碼的情況下執(zhí)行或修改源碼中的方法趁桃,字段,從而達(dá)到理想中的效果肄鸽。小伙伴們快去練習(xí)一下把卫病。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市典徘,隨后出現(xiàn)的幾起案子蟀苛,更是在濱河造成了極大的恐慌,老刑警劉巖逮诲,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帜平,死亡現(xiàn)場離奇詭異,居然都是意外死亡梅鹦,警方通過查閱死者的電腦和手機(jī)裆甩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來齐唆,“玉大人淑掌,你說我怎么就攤上這事〉睿” “怎么了抛腕?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長媒殉。 經(jīng)常有香客問我担敌,道長,這世上最難降的妖魔是什么廷蓉? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任全封,我火速辦了婚禮,結(jié)果婚禮上桃犬,老公的妹妹穿的比我還像新娘刹悴。我一直安慰自己,他們只是感情好攒暇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布土匀。 她就那樣靜靜地躺著,像睡著了一般形用。 火紅的嫁衣襯著肌膚如雪就轧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天田度,我揣著相機(jī)與錄音妒御,去河邊找鬼。 笑死镇饺,一個(gè)胖子當(dāng)著我的面吹牛乎莉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播奸笤,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惋啃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了揭保?” 一聲冷哼從身側(cè)響起肥橙,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秸侣,沒想到半個(gè)月后存筏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡味榛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年椭坚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搏色。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡善茎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出频轿,到底是詐尸還是另有隱情垂涯,我是刑警寧澤烁焙,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站耕赘,受9級(jí)特大地震影響骄蝇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜操骡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一九火、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧册招,春花似錦岔激、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至冀惭,卻和暖如春震叙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背散休。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工媒楼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人戚丸。 一個(gè)月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓划址,卻偏偏與公主長得像,于是被迫代替她去往敵國和親限府。 傳聞我的和親對象是個(gè)殘疾皇子夺颤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

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