Java基礎(chǔ)-反射機(jī)制學(xué)習(xí)

1. 前言

??Java的反射功能平時(shí)已使用了多次,但從來沒有仔細(xì)的梳理過,趁著最近在梳理Java基礎(chǔ),再來系統(tǒng)的了解下Java反射機(jī)制,本文所使用的JDK版本是JDK 8。

2. Java中為什么會(huì)有反射?

??本次,我們通過How-What-Why的方式來進(jìn)行學(xué)習(xí),首先,我們來了解下為什么會(huì)有反射。

  1. 首先,Java反射功能是在JDK1.1時(shí)引入的,所圍繞的點(diǎn)就是網(wǎng)絡(luò)。大概在互聯(lián)網(wǎng)剛出現(xiàn)的時(shí)候,Java所倡導(dǎo)的跨平臺(tái)讓它迅速占領(lǐng)了一席之地,而跨平臺(tái)的實(shí)現(xiàn),很大程度上是通過Java的Applet來實(shí)現(xiàn)的。
  2. 那么applet是如何運(yùn)行的呢?Applet的運(yùn)行是通過把遠(yuǎn)程的類文件下載到本地來運(yùn)行,而網(wǎng)絡(luò)在當(dāng)時(shí)給我們的感覺就是一個(gè)字:慢,如果Java采用傳統(tǒng)可執(zhí)行文件的方式,即執(zhí)行一個(gè)完整的可執(zhí)行文件,把整個(gè)Applet下載下來然后再運(yùn)行,只怕等到花兒也謝了。所以Java采用的方式是把文件拆開,以類為單位進(jìn)行組織,這就是我們今天見到的class文件。這樣,執(zhí)行的過程就變成第一個(gè)類下載之后就可以運(yùn)行,大大節(jié)省了最初的等待時(shí)間。而好的設(shè)計(jì)會(huì)把程序分成若干的模塊,所以,絕大多數(shù)程序不可能寫在一個(gè)類中。因此,類文件中必須包含它所用到的類,這樣就會(huì)加載對(duì)應(yīng)的用到的類,進(jìn)一步字段等信息也會(huì)加載進(jìn)來,這樣幾乎一個(gè)完整類的信息就出來了,而這樣的實(shí)現(xiàn)恰好是反射的雛形。
  3. 但JDK1.1的反射其實(shí)還不能完全的叫反射,在維基百科中,對(duì)JDK1.1的reflection解釋為:reflection which supported Introspection only, no modification at runtime was possible,JDK1.1的反射其實(shí)就是內(nèi)省。維基百科地址:https://en.wikipedia.org/wiki/Java_version_history
2.1 什么是內(nèi)省機(jī)制

??在計(jì)算機(jī)科學(xué)中,內(nèi)?。↖ntrospection)是指計(jì)算機(jī)程序在運(yùn)行的時(shí)候檢查對(duì)象類型的一種能力,通常也可以稱為運(yùn)行時(shí)類型檢查。而反射是在內(nèi)省基礎(chǔ)之上增加了可以修改的能力,內(nèi)省和反射還是有些區(qū)別的,因?yàn)橛行┱Z言支持內(nèi)省,不支持反射,比如 C++語言。反射與內(nèi)省只是概念上的區(qū)分,其實(shí)內(nèi)省還是通過反射來實(shí)現(xiàn)的。

??內(nèi)省是Java 語言對(duì) Bean 類屬性、事件的一種缺省處理方法。例如類 A 中有屬性 name, 那我們可以通過 getName,setName 來得到其值或者設(shè)置新的值,而通過 getName/setName 來訪問 name 屬性,這就是默認(rèn)的規(guī)則。Java種提供了一套API用來內(nèi)省相關(guān)實(shí)現(xiàn),位于java.beans包中,比如 BeanInfo,Introspector等相關(guān)接口與類。由于本文的重點(diǎn)是反射,所以有關(guān)內(nèi)省就不多說了,如果有興趣,可以參考底部鏈接。

3. 什么是反射?

??現(xiàn)在,我們可以大概來說說什么是反射了。所謂反射,其實(shí)就是動(dòng)態(tài)的內(nèi)省,是指在程序運(yùn)行期間,能夠動(dòng)態(tài)獲取對(duì)象信息并且能夠動(dòng)態(tài)調(diào)用對(duì)象方法的一種功能。由于Java是一種編譯型語言,一般情況下,對(duì)象的類型在編譯期間就已經(jīng)確定下來了,而使用反射我們可以動(dòng)態(tài)的創(chuàng)建對(duì)象,而這樣的對(duì)象的類型從在編譯期間可以是未知的。
??一般情況下,一個(gè)類中有成員變量,方法,構(gòu)造方法,包等,通過反射可以將類中的各個(gè)屬性映射為一個(gè)個(gè)對(duì)象,然后通過對(duì)這些對(duì)象來進(jìn)行操作。

Java中的反射機(jī)制大概有如下幾種使用情況:

  1. 在運(yùn)行時(shí)構(gòu)造一個(gè)類的對(duì)象;
  2. 在運(yùn)行時(shí)獲取一個(gè)類擁有的屬性和方法;
  3. 在運(yùn)行時(shí)調(diào)用一個(gè)對(duì)象的方法;
  4. 在運(yùn)行時(shí)判斷一個(gè)對(duì)象所屬的類;
4. 反射的使用

接下來我們就來了解下反射中相關(guān)API的使用,反射相關(guān)的類一般都在java.lang.relfect包目錄下。

4.1 Class對(duì)象

??首先,Class是一個(gè)類,一個(gè)描述類本身的類,封裝了描述方法的Method,描述字段的Filed,描述構(gòu)造器的Constructor等屬性。而CLASS類的實(shí)例表示正在運(yùn)行的JAVA應(yīng)用程序中的類和程序接口,也就是在JVM中每個(gè)實(shí)例都會(huì)有且僅有一個(gè)對(duì)應(yīng)的CLASS對(duì)象。

而獲取Class對(duì)象的方式一般有三種:

  • 第一種方式,直接通過類名的方式獲取;
Class<Person> clazz = Person.class;
Class<Integer> integerClass = Integer.TYPE;
  • 第二種方式,通過Class類的forName靜態(tài)方法進(jìn)行加載,傳遞類名的時(shí)候需要是類的全路徑,由于是根據(jù)類名加載,該類會(huì)拋出一個(gè)ClassNotFoundException異常,注意處理下;
// 我們以前通過JDBC加載數(shù)據(jù)庫驅(qū)動(dòng)的時(shí)候經(jīng)常使用這種方式
Class<?> cls = Class.forName("jdk8.stream.Person");
  • 第三種方式,通過某個(gè)對(duì)象的getClass方法來獲??;
String str = "reflection";
Class<?> cls = str.getClass();

??針對(duì)基礎(chǔ)類型和包裝類型,這里再多說一點(diǎn),通過 int/Integer來舉例,在Integer類中有一個(gè)TYPE變量,用于返回基礎(chǔ)類型的Class對(duì)象,也就是說:Integer.class返回的是Integer類所對(duì)應(yīng)的Class對(duì)象,Integer.TYPE返回的是基礎(chǔ)類型int的Class對(duì)象。

4.2 判斷對(duì)象是否是某個(gè)類的實(shí)例

??在Java中,我們一般是通過 instanceof 關(guān)鍵字來判斷是否為某個(gè)類的實(shí)例,同時(shí)我們也可以借助反射中Class對(duì)象的 isInstance() 方法來判斷是否是某個(gè)類的實(shí)例,該方法是一個(gè)native方法:

public native boolean isInstance(Object obj);

使用方式比較簡(jiǎn)單:

Class<Person> cls = Person.class;
Person person = new Person();
System.out.println(cls.isInstance(person));
4.3 通過反射來創(chuàng)建對(duì)象,生成實(shí)例

通過反射來創(chuàng)建對(duì)象一般有兩種方式:

  • 第一種是通過Class對(duì)象的newInstance()方法,不過記得需要類提供無參構(gòu)造方法,另外注意下反射的相關(guān)異常:
Class<Person> cls = Person.class;
Person person = cls.newInstance();
  • 第二種方式是通過Constructor對(duì)象的newInstance方法來創(chuàng)建,這種方法可以用指定的構(gòu)造器來構(gòu)造類的實(shí)例,不過我們需要先通過Class對(duì)象獲取指定的Constructor對(duì)象:
Class<Person> cls = Person.class;
// 只有一個(gè)參數(shù)的構(gòu)造方法
Constructor constructor = cls.getConstructor(String.class);
//根據(jù)構(gòu)造器創(chuàng)建實(shí)例
Person person =(Person)constructor.newInstance("beijing");
System.out.println(person);
4.4 通過反射創(chuàng)建數(shù)組

??數(shù)組在Java中可以算比較特殊的一種類型了,通過反射創(chuàng)建數(shù)組和上述創(chuàng)建對(duì)象的方式有些不同,我們可以借助java.lang.reflect.Array類的newInstance方法來創(chuàng)建:

Class<String> cls = String.class;
Object array = Array.newInstance(cls, 25);
//往數(shù)組里添加內(nèi)容
Array.set(array, 0, "hello");
Array.set(array, 1, "wold");
Array.set(array, 2, "java");
//獲取某一項(xiàng)的內(nèi)容
System.out.println(Array.get(array, 2));

有興趣的可以看下Array類的一些get開頭的方法,比如getIntgetDouble等方法。

4.5 獲取方法

??我們通過Class類提供的一系列方法,可以獲取到該Class對(duì)象對(duì)應(yīng)類或接口的方法,屬性,構(gòu)造方法等,我們先來看看如何獲取方法:

    1. getDeclaredMethods方法,獲取類或接口聲明的所有方法,包括public,protected,默認(rèn)訪問和private方法,但不包括繼承的方法,返回的是Method[]數(shù)組;
    1. getMethods方法,返回類的公用方法,包括繼承的公用方法,默認(rèn)情況下類繼承自O(shè)bject,所以會(huì)返回Object的一些方法,比如notify,equals,hashCode方法等;
    1. getMethod(String name, Class<?>... parameterTypes),返回類的特定公用方法,第一個(gè)參數(shù)是方法名,第二個(gè)參數(shù)是一個(gè)可變參數(shù),表示該方法的參數(shù);該方法不能是繼承類的方法,必須是目標(biāo)類的方法;
    1. getDeclaredMethod(String name, Class<?>... parameterTypes),功能和getMethod類似,但返回的方法類型不但但是public方法,還有protected,默認(rèn)訪問方法和private方法,同樣不包括繼承的方法;
    1. getEnclosingMethod,該方法表示當(dāng)前類的一個(gè)內(nèi)部類是在哪一個(gè)方法中被定義的,比如:
public void newInnerClass() {
    class InnerClass {}
}

而針對(duì)其他方法,可以簡(jiǎn)單測(cè)試下:

// 先在Person類中創(chuàng)建一個(gè)公用方法,用于測(cè)試
public void getSumByCity(String city) {
    System.out.println("test");
}

Class<Person> cls = Person.class;
Method[] methods = cls.getMethods();
// 第一個(gè)是方法名, 后面是方法對(duì)應(yīng)的參數(shù)
Method method = cls.getMethod("getSumByCity", String.class);
4.6 獲取構(gòu)造方法

??獲取構(gòu)造方法和上述獲取方法一樣,通過如下方法:

public Constructor<T> getConstructor(Class<?>... parameterTypes)
public Constructor<?>[] getConstructors()
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
public Constructor<?>[] getDeclaredConstructors()
public Constructor<?> getEnclosingConstructor()

至于方法的訪問權(quán)限,都是和上述接口一致的,比如說getDeclaredConstructors 能獲取所有構(gòu)造方法,不論是私有的還是公用的。

4.7 獲取屬性

同樣獲取屬性也是類似的,通過如下方法:

public Field getField(String name)
public Field[] getFields()
public Field getDeclaredField(String name)
public Field[] getDeclaredFields() 
4.8 獲取注解

獲取注解的方式和上述是一致的,有如下方法:

public Annotation[] getAnnotations()
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)
public Annotation[] getDeclaredAnnotations()

除了這幾個(gè)常用的方法之外,JDK 8還引入了兩個(gè)方法:

public <A extends Annotation> A[] getDeclaredAnnotationsByType(Class<A> annotationClass)
public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass) 

由于JDK 8引入了重復(fù)注解的支持,也就是使用@Repeatable,這兩個(gè)方法就是用于返回重復(fù)注解的類型。還有一個(gè)方法,用于判斷某一個(gè)類是否有某注解:

// 如果存在指定注解返回true,否則返回false
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 
4.9 獲取枚舉類型

Class對(duì)象提供了一個(gè)方法用于獲取枚舉對(duì)象并轉(zhuǎn)換為數(shù)組:

public T[] getEnumConstants() 

我們通過一個(gè)例子來看一下。首先,定義枚舉類:

public enum RESULT_STATE {
    PASS("已通過", 1),
    REJECT("未通過", -1),
    PROCESSING("進(jìn)行中", 2);

    private final String name;
    private final Integer value;

    //get,set,構(gòu)造方法省略
}

然后使用反射獲取枚舉對(duì)象,并執(zhí)行對(duì)應(yīng)的getName方法:

public static void main(String[] args) throws Exception {
    // 枚舉類定義在了類MainTest內(nèi)部
    Class<?> class1 = Class.forName("reflection.MainTest$RESULT_STATE");
    Method method = class1.getDeclaredMethod("getName");

    if (class1.isEnum()) {
        List<?> list = Arrays.asList(class1.getEnumConstants());
        for (Object enu : list) {
            System.out.println(method.invoke(enu));
        }
    }
}

這里再多說一點(diǎn),Class提供了多個(gè)以is開頭的方法,用來判斷該Class對(duì)象的實(shí)際類型,比如:

public boolean isEnum()              // 是否是枚舉類型
public native boolean isInterface()  // 是否是接口
public native boolean isArray()      // 是否是數(shù)組
public boolean isAnnotation()        // 是否是注解

剩余的有興趣的童鞋可以自行查看源代碼。

對(duì)于枚舉的獲取,我們可以借助反射寫一個(gè)通用的方法:根據(jù)枚舉類型獲取枚舉的所有對(duì)象,或者根據(jù)枚舉的名稱來獲取所有對(duì)象:

public static Map<String, Integer> convertEnumToMap(Enum enu) {
    Map<String, Integer> map = new LinkedHashMap<>();
    try {
        Class<?> cls = enu.getDeclaringClass();
        Method nameMethod = cls.getMethod("getName");
        Method valueMethod = cls.getMethod("getValue");
        // 針對(duì)方法,我們也可以通過cls.cls.getDeclaredMethods()來獲取
        Object[] objects = cls.getEnumConstants();
        for (Object object : objects) {
            map.put(String.valueOf(nameMethod.invoke(object)), (Integer)valueMethod.invoke(object));
        }
        return map;
    } catch (ReflectiveOperationException e) {
        e.printStackTrace();
    }
    return map;
}

調(diào)用的時(shí)候:

Map<String, Integer> map = convertEnumToMap(RESULT_STATE.PASS);
// output : {已通過=1, 未通過=-1, 進(jìn)行中=2}
4.10 invoke方法

??當(dāng)我們通過反射從類中獲取了一個(gè)方法后,我們就可以用invoke()方法來調(diào)用這個(gè)方法。invoke方法的功能就是用來在運(yùn)行時(shí)動(dòng)態(tài)地調(diào)用某個(gè)實(shí)例的方法,來先看個(gè)簡(jiǎn)單的例子:

Class<Person> cls = Person.class;
//先實(shí)例化對(duì)象
Object obj = cls.newInstance();
// 然后獲取對(duì)應(yīng)類的方法
Method method = cls.getMethod("getCity");
// 執(zhí)行方法
Object result = method.invoke(obj);
System.out.println(result);

該方法就相當(dāng)于我們執(zhí)行了Person類的 getCity 方法,然后打印出返回的值。其中invoke方法的第一個(gè)參數(shù)是實(shí)例化之后的對(duì)象(也就是對(duì)象參數(shù)),第二個(gè)參數(shù)是個(gè)可變參數(shù),是要調(diào)用方法的參數(shù);而如果要訪問靜態(tài)方法的話,第一個(gè)參數(shù)可以被忽略,也就是將invoke方法的對(duì)象參數(shù)設(shè)置為null即可:

// 然后獲取對(duì)應(yīng)類的靜態(tài)方法
Method method = cls.getDeclaredMethod("test");
// 執(zhí)行方法
Object result = method.invoke(null);

??在使用中,我們經(jīng)常能看到無論是Field,Method還是Constructor都有一個(gè)setAccessible方法,該方法是用來控制Java的訪問控制檢查,比如執(zhí)行私有的方法或?qū)傩缘?。我們來?jiǎn)單了解下:

??invoke方法在執(zhí)行的時(shí)候,會(huì)先進(jìn)行權(quán)限檢查,也就是檢查AccessibleObject類中的override值,而AccessibleObject 類是 Field、Method 和 Constructor 對(duì)象的基類,它提供了將反射的對(duì)象標(biāo)記為在使用時(shí)取消默認(rèn) Java 語言訪問控制檢查的能力。override的值默認(rèn)是false,表示調(diào)用方法的時(shí)候需要檢查訪問控制的權(quán)限;我們可以用setAccessible方法設(shè)置為true,若override的值為true,表示忽略權(quán)限規(guī)則,調(diào)用方法時(shí)無需檢查權(quán)限(也就是說可以調(diào)用任意的private方法,違反了封裝)。

public Object invoke(Object obj, Object... args)
    throws IllegalAccessException, IllegalArgumentException,
       InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    MethodAccessor ma = methodAccessor;             // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

而有關(guān)invoke源碼解讀,強(qiáng)烈推薦深入解析Java反射(2) - invoke方法

5. 如何讓一個(gè)方法不被反射調(diào)用?

??正常情況下,無論是私有的,共有的方法,還是靜態(tài)的,非靜態(tài)的方法,都可以通過反射來進(jìn)行調(diào)用,不過知乎上的老哥提供了一種相關(guān)的解決方案:

對(duì)于私有方法,在私有方法的內(nèi)部,來判斷方法的堆棧來將其限定到當(dāng)前類所屬方法。雖然不知道行不行得通,但可以試試。

知乎地址:https://www.zhihu.com/question/47896687?sort=created

6. 反射很慢么?

??正常情況下,如果我們測(cè)試過反射的性能的話,會(huì)發(fā)現(xiàn)反射會(huì)比直接調(diào)用慢一些。而反射的慢是因?yàn)橛捎贘ava本身是一種靜態(tài)語言,一般情況下類型在編譯期間已經(jīng)確定了,而反射涉及到類型的動(dòng)態(tài)處理,Java的編譯器如JIT 沒辦法對(duì)反射相關(guān)的代碼進(jìn)行優(yōu)化,并且反射執(zhí)行的時(shí)候還有安全檢查,訪問控制等操作,所以說反射可能會(huì)比常規(guī)的調(diào)用慢一些,不過基本每次JDK 版本更新,反射的性能都會(huì)被優(yōu)化一些,所以目前的反射其實(shí)不會(huì)慢太多。

這里參考:Java 反射到底慢在哪里?,感謝R神的回復(fù)。

7. JDK 7處理反射方法的異常

在JDK 7之前,當(dāng)調(diào)用一個(gè)反射方法時(shí),不得不捕獲多個(gè)不相關(guān)的檢查期異常,比如:

Class.forName("jdk8.stream.GoodsInfo").getMethod("getName").invoke(null,new String[]{});

上面這行代碼通過Java反射動(dòng)態(tài)加載一個(gè)類,并調(diào)用它的某個(gè)方法,為了保證編譯器能通過,我們能需要捕獲如下異常:

try {
    Class.forName("jdk8.stream.GoodsInfo").getMethod("getName").invoke(null,new String[]{});
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
} catch (NoSuchMethodException e) {
    e.printStackTrace();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

當(dāng)然,借助于JDK7新的異常處理的特性,我們可以通過一個(gè)catch分支來捕獲:

try {
    Class.forName("jdk8.stream.GoodsInfo").getMethod("getName").invoke(null,new String[]{});
} catch (IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) {
    e.printStackTrace();
}

不過,JDK 7引入了一個(gè)新的反射操作相關(guān)異常的父類 ReflectiveOperationException,這樣的話就可以通過這一異常來捕獲所有其他反射操作相關(guān)的子類異常,從而使我們的代碼更加簡(jiǎn)潔:

try {
    Class.forName("jdk8.stream.GoodsInfo").getMethod("getName").invoke(null,new String[]{});
} catch (ReflectiveOperationException e) {
    e.printStackTrace();
}
8. 使用反射越過泛型檢查

??拿一個(gè)例子來說,比如定義一個(gè)字符串類型的List,List<String> list,如果我們想往里面添加一個(gè)Integer類型的元素,能添加進(jìn)去么?正常情況下,是不行的,因?yàn)榫幾g的時(shí)候就直接提示錯(cuò)誤了,不過通過反射我們可以實(shí)現(xiàn):

public static void main(String[] args) throws Exception {
    List<String> list = new ArrayList<>();
    list.add("hello");
    list.add("world");

    Class<?> cls = list.getClass();
    Method method = cls.getMethod("add", Object.class);
    method.invoke(list, 100);
    System.out.println(list);
    // output: [hello, world, 100]
}

因?yàn)榉盒褪怯糜诰幾g期間的,編譯過后泛型擦除,所以我們才可以借助反射來實(shí)現(xiàn)。

9. 總結(jié)

??到這里,有關(guān)反射的內(nèi)容基本就學(xué)習(xí)完了,現(xiàn)在來簡(jiǎn)單總結(jié)一下。反射增加了程序的靈活性,所以在一般的框架中使用比較多,如Spring等。反射的功能很強(qiáng)大,在一定程度上可以說是破壞了Java語言封裝的特性,另外反射調(diào)用的時(shí)候可以忽略權(quán)限的檢查,從而可能會(huì)導(dǎo)致對(duì)象的安全性問題。不過,我們不妨換一個(gè)角度來思考,思考下什么是封裝?什么是安全?

  1. 所謂封裝,就是將具體的實(shí)現(xiàn)細(xì)節(jié)隱藏,將實(shí)現(xiàn)后的結(jié)果通過共有方法返回給外部調(diào)用,而針對(duì)私有方法,即使別人能通過反射的方式調(diào)用,但即使調(diào)用但卻得不到一個(gè)完整的結(jié)果,因?yàn)橐话闱闆r下只有共有方法的返回才是完整的。從這一點(diǎn)來說,封裝性其實(shí)沒有被破壞。
  2. 而所謂安全,如果是為了保護(hù)源碼的話,那其實(shí)沒必要,即使不通過反射,也有其他方式獲取源碼。
  3. 因?yàn)镴ava語言畢竟是一種靜態(tài)語言,為了讓語言擁有動(dòng)態(tài)的特性,必須要有反射機(jī)制,而反射機(jī)制本身就是底層的處理,不可能按常規(guī)的封裝特性來處理。也就是說不給調(diào)用私有方法的能力,很多程序受到局限,那么實(shí)現(xiàn)起來就麻煩了。
  4. 所以說我們可以認(rèn)為,反射機(jī)制只是提供了一種強(qiáng)大的功能,使得開發(fā)者能在封裝之外,按照特定的需要實(shí)現(xiàn)一些功能。沒有太多的必要糾結(jié)于反射是否安全等問題。

備注:TODO Java獲取泛型,有時(shí)間了解下。

本文參考自:
Java內(nèi)省機(jī)制
Java為什么支持反射機(jī)制?
深入解析Java反射(1) - 基礎(chǔ)
java反射機(jī)制是不是破壞了JAVA的卦裝性呢

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容