Java 之路 (十四) -- 類型信息(RTTI城看、Class 對(duì)象的加載测柠、Class 引用的獲取與泛化、instanceof 關(guān)鍵字谒主、反射基礎(chǔ)與簡(jiǎn)單使用示例)

學(xué)習(xí)內(nèi)容:

  • 傳統(tǒng)的 RTTI
    • Class 對(duì)象的加載
    • 獲取 Class 對(duì)象引用的方式
      • ClassforName
      • 類字面常量
    • Class 引用的泛化
    • instanceof 關(guān)鍵字
  • 反射
    • 相關(guān)類
    • 簡(jiǎn)單使用示例

前言

運(yùn)行時(shí)類型信息使得我們可以在程序運(yùn)行時(shí)發(fā)現(xiàn)和使用類型信息霎肯,Java 中運(yùn)行時(shí)識(shí)別對(duì)象和類的信息有如下兩種方式:

  1. 傳統(tǒng)的 RTTI :假定我們?cè)诰幾g時(shí)已經(jīng)知道所有的類型
    1. 傳統(tǒng)的類型轉(zhuǎn)換观游。
    2. 通過查詢 Class 對(duì)象獲取運(yùn)行時(shí)所需的信息
    3. 通過 instanceof 得知對(duì)象是否是某個(gè)特定類型的實(shí)例
  2. 反射技術(shù):允許我們運(yùn)行時(shí)發(fā)現(xiàn)和使用類型的信息

1. RTTI

RTTI,Run-Time Type information

1.1 為什么需要 RTTI允跑?

面向?qū)ο缶幊讨谢镜哪康氖牵鹤尨a只操縱對(duì)基類的引用聋丝,這樣如果要添加一個(gè)新類來擴(kuò)展程序弱睦,就不會(huì)影響到原來的代碼渊额。

于是我們往往會(huì)創(chuàng)建一個(gè)具體對(duì)象端圈,之后將其向上轉(zhuǎn)型為基類型對(duì)象子库,并在后面使用該基類型對(duì)象引用仑嗅,但此時(shí)該對(duì)象已經(jīng)丟失了其具體類型仓技,我怎么調(diào)用具體類型的方法呢?此時(shí) RTTI 就起作用了阔逼。RTTI 會(huì)在運(yùn)行時(shí)嗜浮,識(shí)別一個(gè)對(duì)象的類型摩疑,得到引用指向的對(duì)象的確切類型雷袋。再之后就是多態(tài)的事情了,針對(duì)不同類型類型執(zhí)行不同代碼瓦灶。

1.2 Class 對(duì)象

Java 使用 Class 對(duì)象來執(zhí)行其 RTTI迫卢,那么 Class 對(duì)象是什么呢乾蛤?

類是程序的一部分家卖,每一個(gè)類都有一個(gè) Class 對(duì)象。換言之趴樱,每當(dāng)編寫并編譯了一個(gè)新類叁征,就會(huì)在同名的 .class 文件中產(chǎn)生一個(gè) Class 對(duì)象逛薇。Class 對(duì)象包含了與類有關(guān)的信息永罚。那創(chuàng)建 Class 對(duì)象具體做什么呢呢袱?答案是通過 Class 對(duì)象創(chuàng)建我們需要的關(guān)于這個(gè)類的所有對(duì)象(比如對(duì)象實(shí)例,或者靜態(tài)變量的引用值等)惕蹄。實(shí)際上焊唬,所有的類都是在對(duì)其第一次使用時(shí)赶促,動(dòng)態(tài)加載到 JVM 中的挟炬,其中,“類加載器” 會(huì)負(fù)責(zé)將 Class 對(duì)象加載到內(nèi)存中老速,而一旦加載成功之后橘券,他就可以用來創(chuàng)建這個(gè)類的所有對(duì)象繁成。

1.2.1 Class 對(duì)象的加載

前面我們提到過,所有的類都是在對(duì)其第一次使用時(shí)济似,動(dòng)態(tài)加載到 JVM 中的磺樱。實(shí)際上婆咸,當(dāng)創(chuàng)建第一個(gè)對(duì)類的靜態(tài)成員的引用時(shí)尚骄,就會(huì)加載這個(gè)類乖仇。這點(diǎn)也證明構(gòu)造器是類的靜態(tài)方法询兴,雖然其沒有顯式指明 static诗舰,即使用 new 操作符創(chuàng)建類的新對(duì)象也會(huì)被當(dāng)作對(duì)類的靜態(tài)成員的引用眶根。

因此,Java 程序在開始運(yùn)行之前并非被完全加載记劝,其各個(gè)部分在必需時(shí)才進(jìn)行加載厌丑。

當(dāng)使用該類時(shí)怒竿,類加載器首先會(huì)檢查這個(gè)類的 Class 對(duì)象是否已經(jīng)加載耕驰,如果尚未加載,默認(rèn)的類加載器就會(huì)根據(jù)類名查找 .class 文件饭弓,在這個(gè)類的字節(jié)碼被加載時(shí)示启,它們會(huì)接受驗(yàn)證夫嗓,以確保其沒有被破壞舍咖,并且不含不良 Java 代碼(這是 Java 安全防范的措施之一)锉桑。一旦某個(gè)類的 Class 對(duì)象被載入內(nèi)存民轴,那么它久被用來創(chuàng)建這個(gè)類的所有對(duì)象后裸。

補(bǔ)充一點(diǎn)關(guān)于類加載器:

  1. 類加載器子系統(tǒng)實(shí)際上可以包含一條類加載器鏈微驶,但是只有一個(gè)原生類加載器因苹,它是Java 實(shí)現(xiàn)的一部分,用來加載 可信類凶杖,包括 Java API 類官卡,通常是從本地盤中加載的寻咒。
  2. 如果有特殊需求(如以某種特殊的方式加載類毛秘,以支持 Web 服務(wù)器應(yīng)用)叫挟,那么可以掛接額外的類加載器抹恳,不過通常不需要添加額外的類加載器。

下面給出一個(gè)例子健霹,證明加載的時(shí)間點(diǎn):

class Candy {
    static {    System.out.println("Loading Candy"); }
}

class Gum {
    static {    System.out.println("Loading Gum"); }
}

class Cookie {
    static {    System.out.println("Loading Cookie"); }
}

public class SweetShop {
    public static void print(Object obj) {
        System.out.println(obj);
    }
    public static void main(String[] args) {  
        print("inside main");
        new Candy();
        print("After creating Candy");
        try {
            Class.forName("Gum");
        } catch(ClassNotFoundException e) {
            print("Couldn't find Gum");
        }
        print("After Class.forName(\"Gum\")");
        new Cookie();
        print("After creating Cookie");
    }
}

/*輸出
inside main
Loading Candy
After creating Candy
Loading Gum
After Class.forName("Gum")
Loading Cookie
After creating Cookie

Process finished with exit code 0

*/

從結(jié)果上看糖埋,通過 new 創(chuàng)建 Candy 和 Cookie 的對(duì)象時(shí)瞳别,二者的 Class 對(duì)象被加載祟敛,也證明了 Class 對(duì)象僅在需要的時(shí)候才被加載垒棋。

比較特殊的一句代碼:

Class.forName("Gum")

其中 forName 是 Class 類(所有 Class 對(duì)象都屬于這個(gè)類)的一個(gè) static 成員,是取得 Class 對(duì)象的引用的一種方法衣撬。此處并未使用獲取返回的 Class 對(duì)象引用具练,它的作用在于要求 JVM 查找并加載類,加載的過程中甜无,Gum 的 static 子句被執(zhí)行扛点,Gum 類即被加載哥遮。

Class 對(duì)象和其他對(duì)象一樣,我們可以獲取并操作它的引用

1.2.2 獲取 Class 對(duì)象的引用

無論何時(shí)陵究,只要我們想在運(yùn)行時(shí)使用類型信息眠饮,就必須首先獲得對(duì)恰當(dāng)?shù)?Class 對(duì)象的引用。本小節(jié)就來介紹一下三種獲取 Class 對(duì)象引用的方式铜邮,及其要點(diǎn)。

1.2.2.1 Class.forName()

forName() 是取得 Class 對(duì)象的引用的一種方法松蒜。它接收一個(gè)包含目標(biāo)類的文本名的 String 作為輸入?yún)?shù)扔茅,返回一個(gè)和 Class 對(duì)象的引用。

該方法會(huì)要求 JVM 查找并加載指定的類秸苗,然后 JVM 會(huì)執(zhí)行靜態(tài)代碼段召娜,之后返回 Class 對(duì)象的引用

如果 Class.forName 找不到要加載的類,會(huì)配出 ClassNotFoundException 異常惊楼。

Class.forName 的優(yōu)勢(shì):

  • 不需要為了獲得 Class 引用而持有該類型的對(duì)象玖瘸。
  • 當(dāng)然,如果已經(jīng)持有對(duì)象胁后,那么就可以通過 getClass() 方法來獲取 Class 引用店读,這個(gè)方法屬于跟類 Object 的一部分,返回表示該對(duì)象的實(shí)際類型的 Class 引用攀芯。
public static void main(String[] args) {
    Class c = null;
    try{
      //通過Class.forName獲取Gum類的Class對(duì)象
      c = Class.forName("Gum");
      System.out.println(c.getName());
    }catch (ClassNotFoundException e){
      e.printStackTrace();
    }

    //通過實(shí)例對(duì)象獲取Gum的Class對(duì)象
    Gum gum = new Gum();
    Class c2=gum.getClass();

  }

關(guān)于 Class 的其他方法屯断,詳見 https://docs.oracle.com/javase/9/docs/api/java/lang/Class.html

1.2.2.2 類字面常量

Java 還提供了另一種方法來生產(chǎn)對(duì) Class 對(duì)象的引用,即使用類字面常量侣诺,如下:

Class c = Gum.class;

該方式更簡(jiǎn)單殖演,更安全,因?yàn)榫幾g時(shí)會(huì)受到檢查年鸳,因此不需要放在 try 語句塊中趴久,同時(shí)根除了 forName() 方法的調(diào)用,所以更高效搔确。

更加實(shí)用的是類字面常量不僅僅你可以用于普通的類彼棍,也可以應(yīng)用于接口、數(shù)組以及基本數(shù)據(jù)類型膳算。對(duì)于基本數(shù)據(jù)類型的包裝其類座硕,還有一個(gè)標(biāo)準(zhǔn)字段 TYPE。TYPE 字段是一個(gè)引用涕蜂,指向?qū)?yīng)的基本數(shù)據(jù)類型的 Class 對(duì)象华匾,等價(jià)關(guān)系如下所示:

boolean.class  ==  Boolean.TYPE;
char.class  == Character.TYPE;
byte.class ==  Byte.TYPE;
short.class ==  Short.TYPE;
int.class ==  Integer.TYPE;
long.class ==  Long.TYPE;
float.class ==  Float.TYPE;
double.class ==  Double.TYPE;
void.class ==  Void.TYPE;

需要注意的是:此方式 .class 創(chuàng)建對(duì) Class 對(duì)象得到引用是,不會(huì)自動(dòng)初始化該 Class 對(duì)象机隙,加載的過程如下:

  1. 加載:由類加載器執(zhí)行蜘拉。該步驟將查找字節(jié)碼萨西,并從這些字節(jié)碼中創(chuàng)建一個(gè) Class 對(duì)象
  2. 鏈接:在鏈接階段將驗(yàn)證類中的字節(jié)碼,為靜態(tài)域分配存儲(chǔ)空間旭旭,并且如果必須的話谎脯,將解析這個(gè)類創(chuàng)建的對(duì)其它類的所有引用。
  3. 初始化:如果該類具有超類您机,則對(duì)其初始化穿肄,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊。

也就是說际看,初始化被延遲到了對(duì)靜態(tài)方法或者非常數(shù)靜態(tài)域進(jìn)行首次引用時(shí)才執(zhí)行咸产,如下:

import java.util.*;

class Initable {
    //編譯期靜態(tài)常量
    static final int staticFinal = 47;
    //非編期靜態(tài)常量
    static final int staticFinal2 =
            ClassInitialization.rand.nextInt(1000);

    static {
        System.out.println("Initializing Initable");
    }
}

class Initable2 {
    //靜態(tài)成員變量
    static int staticNonFinal = 147;

    static {
        System.out.println("Initializing Initable2");
    }
}

class Initable3 {
    //靜態(tài)成員變量
    static int staticNonFinal = 74;

    static {
        System.out.println("Initializing Initable3");
    }
}

public class ClassInitialization {
    public static Random rand = new Random(47);

    public static void main(String[] args) throws Exception {
        //字面常量獲取方式獲取Class對(duì)象
        Class initable = Initable.class;
        System.out.println("After creating Initable ref");
        //不觸發(fā)類初始化
        System.out.println(Initable.staticFinal);
        //會(huì)觸發(fā)類初始化
        System.out.println(Initable.staticFinal2);
        //會(huì)觸發(fā)類初始化
        System.out.println(Initable2.staticNonFinal);
        //forName方法獲取Class對(duì)象
        Class initable3 = Class.forName("Initable3");
        System.out.println("After creating Initable3 ref");
        System.out.println(Initable3.staticNonFinal);
    }
}

/*結(jié)果
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
/*

*/

如果一個(gè) static final 值是編譯期常量,就像 Initable.staticFinal仲闽,那么這個(gè)值不需要對(duì) Initable 類進(jìn)行初始化就可以被讀取脑溢。但這并不是說 static final 就能保證這一點(diǎn),譬如Initable.staticFinal2 就需要進(jìn)行類的初始化赖欣,因?yàn)樗皇蔷幾g期常量屑彻。

如果一個(gè) static 域不是 final 的,那么訪問錢顶吮,需要先進(jìn)性鏈接(分配空間)和初始化(初始化存儲(chǔ)空間)社牲。

1.2.3 Class 引用的泛化

Class 引用總是指向某個(gè) Class 對(duì)象,它可以制造類的實(shí)例悴了,并包含可作用于這些實(shí)例的所有方法代碼搏恤。因此,Class 引用表示的是其指向的對(duì)象的確切類型湃交。

Java SE5 引入泛型后熟空,可以通過泛型來對(duì) Class 對(duì)象的類型進(jìn)行限定使得 Class 對(duì)象類型更為具體。

public class GenericClassReferences {

    public static void main(String[] args){
        Class intClass = int.class;
        Class<Integer> genericIntClass = int.class;
        genericIntClass = Integer.class;
        //沒有泛型的約束,可以隨意賦值
        intClass= double.class;

        //編譯期錯(cuò)誤,無法編譯通過
        //genericIntClass = double.class
    }
}

普通的類引用不會(huì)產(chǎn)生警告信息搞莺;而通過泛型語法息罗,可以讓編譯器強(qiáng)制執(zhí)行額外的類型檢查,使得泛型類引用只能賦值為指向其聲明的類型才沧,在編譯期就能發(fā)現(xiàn)這種錯(cuò)誤迈喉。

如下的語句看起來是正確的,但是實(shí)際上無法工作温圆。

Class<Number> genericNumberClass=Integer.class;

無法正常工作的原因在于 Integer Class 對(duì)象不是 Number Class 對(duì)象的子類(盡管 Integer 繼承自 Number)挨摸。

為了適用泛化的 Class 引用是放松限制,適用通配符 "?"捌木,其表示任何"事物":

Class<?> intClass = int.class;
intClass = double.class;

上述代碼是可行的,實(shí)際上Class<?> 優(yōu)于普通的 Class嫉戚,因?yàn)槠胀ǖ?Class 不會(huì)產(chǎn)生編譯器警告信息刨裆,CLass<?> 的好處在于它表示你并非是碰巧或者由于疏忽使用了一個(gè)非具體的類引用澈圈,而是你正是選擇了非具體的版本。

如果我們需要限定 Class 為某種類型或者該類型的任何子類型帆啃,那么需要將 ? 和 extends 配合適用瞬女,創(chuàng)建一個(gè)范圍:

Class<? extends Number> bounded = Integer.class;
//賦予其他類型
bounded = double.class;
bounded = number.class;

上述代碼同樣可行,向 Class 添加泛型語法原因僅僅是為了提供編譯期類型檢查努潘。

1.2.4 Class 的類型轉(zhuǎn)換

Java SE5 中添加了用于 Class 引用的轉(zhuǎn)型語法诽偷,即 cast() 方法:

class Building {}
class House extends Building {}

public class ClassCasts {
    public static void main(String[] args){
        //通過 cast()
        Building b= new House();
        Class<House> houseType = House.class;
        House h = houseType.cast(b);
        //直接強(qiáng)制轉(zhuǎn)換
        h = (House)b;
    }
}

1.2.5 instanceof 關(guān)鍵字

關(guān)于 instanceof 關(guān)鍵字,用于判斷對(duì)象是不是某個(gè)特定類型的實(shí)例疯坤,返回布爾值

if(x instanceof Dog){
    Dog dog = (Dog)x;
}

在將下轉(zhuǎn)型為 Dog 之前报慕,利用 instanceof 判斷 x 是不是 Dog 類型的實(shí)例,如果返回 true 在進(jìn)行類型轉(zhuǎn)換压怠。

1.2.5.1 instanceof 關(guān)鍵字與 isInstance 方法

isInstance 方法是 Class 類中的方法眠冈,也是用來判斷對(duì)象類型的,二者效果相同菌瘫∥贤纾可以說二者是動(dòng)態(tài)等價(jià)的。

來對(duì)比一下二者的用法:

obj.instanceof(class)雨让,判斷對(duì)象是不是這種類型

  1. 一個(gè)對(duì)象是本身類的一個(gè)對(duì)象
  2. 一個(gè)對(duì)象是本身類父類(父類的父類)和接口(接口的接口)的一個(gè)對(duì)象
  3. 所有對(duì)象都是Object
  4. 凡是null有關(guān)的都是false null.instanceof(class)

class.inInstance(obj)雇盖,判斷對(duì)象能不能被轉(zhuǎn)化為這個(gè)類

  1. 這個(gè)對(duì)象是本身類的一個(gè)對(duì)象
  2. 一個(gè)對(duì)象能被轉(zhuǎn)化為本身類所繼承類(父類的父類等)和實(shí)現(xiàn)的接口(接口的父接口)強(qiáng)轉(zhuǎn)
  3. 所有對(duì)象都能被Object的強(qiáng)轉(zhuǎn)
  4. 凡是null有關(guān)的都是false(class.inInstance(null))

同時(shí)對(duì)于檢查 Class 對(duì)象是否相等時(shí),”==“ 和 equals() 方法也是一樣的栖忠。

例子如下:

class A {}

class B extends A {}

public class C {
    static void test(Object x) {
        print("Testing x of type " + x.getClass());
        print("x instanceof A " + (x instanceof A));
        print("x instanceof B " + (x instanceof B));
        print("A.isInstance(x) " + A.class.isInstance(x));
        print("B.isInstance(x) " +
              B.class.isInstance(x));
        print("x.getClass() == A.class " +
              (x.getClass() == A.class));
        print("x.getClass() == B.class " +
              (x.getClass() == B.class));
        print("x.getClass().equals(A.class)) " +
              (x.getClass().equals(A.class)));
        print("x.getClass().equals(B.class)) " +
              (x.getClass().equals(B.class)));
    }

    public static void main(String[] args) {
        test(new A());
        test(new B());
    }
}

/*輸出
Testing x of type class com.zejian.A
x instanceof A true
x instanceof B false //父類不一定是子類的某個(gè)類型
A.isInstance(x) true
B.isInstance(x) false
x.getClass() == A.class true
x.getClass() == B.class false
x.getClass().equals(A.class)) true
x.getClass().equals(B.class)) false
---------------------------------------------
Testing x of type class com.zejian.B
x instanceof A true
x instanceof B true
A.isInstance(x) true
B.isInstance(x) true
x.getClass() == A.class false
x.getClass() == B.class true
x.getClass().equals(A.class)) false
x.getClass().equals(B.class)) true
*/

2. 反射

2.1 基礎(chǔ)知識(shí)

在Java中的反射機(jī)制是指在運(yùn)行狀態(tài)中崔挖,對(duì)于任意一個(gè)類都能夠知道這個(gè)類所有的屬性和方法;并且對(duì)于任意一個(gè)對(duì)象娃闲,都能夠調(diào)用它的任意一個(gè)方法虚汛;這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能成為Java語言的反射機(jī)制。

前面我們提到過皇帮,RTTI 有限制:所有類型必須在編譯時(shí)已知卷哩,這樣才能通過 RTTI 識(shí)別。換句話說属拾,編譯時(shí)将谊,編譯器必須知道所有要通過 RTTI 處理的類。

而反射也是有限制的:目標(biāo)對(duì)象的類對(duì)應(yīng)的 .class 文件必須可獲取渐白。

當(dāng)通過反射與一個(gè)位置類型的對(duì)象打交道時(shí)尊浓,JVM 只是簡(jiǎn)單的檢車這個(gè)對(duì)象,看它屬于哪個(gè)特定的類(就像 RTTI 那樣)纯衍。在用它做其他事情之前栋齿,必須先加載那個(gè)類的 Class 對(duì)象。

因此,RTTI 和 反射之間真正的區(qū)別只在于:

  • 對(duì)于 RTTI瓦堵,編譯器在編譯期間打開和檢查 .class 文件
  • 對(duì)于反射機(jī)制基协,.class 文件在編譯期間是不可獲取的,所以在運(yùn)行時(shí)打開和檢查 .class 文件菇用。

2.2 類庫(kù)的支持

Class 類與 java.lang.reflect 類庫(kù)一起對(duì)反射的概念進(jìn)行了支持澜驮,該類庫(kù)包含了 Field、Method 以及 Constructor 類(每個(gè)類都是先了 member 接口)惋鸥。這些類型的對(duì)象是由 JVM 在運(yùn)行時(shí)創(chuàng)建的杂穷,用以表示未知類里對(duì)應(yīng)的成員。

  • Class 類:反射的核心類卦绣,可以獲取類的屬性耐量、方法等信息

  • Constructor 類:表示類的構(gòu)造方法,利用它可以在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建對(duì)象

  • Field 類:表示類的成員變量迎卤,通過它可以在運(yùn)行時(shí)動(dòng)態(tài)修改成員變量的屬性值(包含private)

  • Method 類:表示類的成員方法拴鸵,通過它可以動(dòng)態(tài)調(diào)用對(duì)象的方法(包含private)

相關(guān) API,見 https://docs.oracle.com/javase/9/docs/api/overview-summary.html

2.3 反射的使用示例

使用反射蜗搔,由三個(gè)步驟

  1. 獲取 Class 對(duì)象
  2. 獲取 Class 類中的信息
  3. 使用反射的相關(guān) API 做具體操作

假定目標(biāo)類如下:

package com.whdalive.reflection;

public class Person {
    private String name;
    private int age;

    public Person() {

    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //getter和setter方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
  
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public String toString(){
        return "姓名:"+name+"  年齡:"+age;
    }
}

步驟1. 獲取 Class 對(duì)象

三種方式:(具體解釋 ↑)

  1. Class c = e.getClass();
  2. Class c = Example.class;
  3. Class c = Class.forName("類的全路徑");

示例:

Class c = Class.forName("reflection.Person");

步驟2. 獲取 Class 類中的信息

package com.whdalive.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) {
        showInfo();
    }
    private static void showInfo() {
        try {
            //獲取Person類的Class對(duì)象
            Class clazz=Class.forName("com.whdalive.reflection.Person");

            //獲取Person類的所有方法信息
            Method[] method=clazz.getDeclaredMethods();
            for(Method m:method){
                System.out.println(m.toString());
            }

            //獲取Person類的所有成員屬性信息
            Field[] field=clazz.getDeclaredFields();
            for(Field f:field){
                System.out.println(f.toString());
            }

            //獲取Person類的所有構(gòu)造方法信息
            Constructor[] constructor=clazz.getDeclaredConstructors();
            for(Constructor c:constructor){
                System.out.println(c.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/*輸出

public java.lang.String com.whdalive.reflection.Person.toString()
public java.lang.String com.whdalive.reflection.Person.getName()
public void com.whdalive.reflection.Person.setName(java.lang.String)
public void com.whdalive.reflection.Person.setAge(int)
public int com.whdalive.reflection.Person.getAge()
private java.lang.String com.whdalive.reflection.Person.name
private int com.whdalive.reflection.Person.age
public com.whdalive.reflection.Person()
public com.whdalive.reflection.Person(java.lang.String,int)

*/

步驟3. 使用反射的相關(guān) API 做具體操作

這里只展示創(chuàng)建一個(gè) Person 對(duì)象劲藐,并將其初始化。

package com.whdalive.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
    
    public static void main(String[] args) {
        create();
    }

    private static void create() {
         try {
                //獲取Person類的Class對(duì)象
                Class clazz=Class.forName("com.whdalive.reflection.Person"); 
                /**
                 * 第一種方法創(chuàng)建對(duì)象
                 */
                //創(chuàng)建對(duì)象
                Person p=(Person) clazz.newInstance();
                //設(shè)置屬性
                p.setName("張三");
                p.setAge(16);
                System.out.println(p.toString());
                /**
                 * 第二種方法創(chuàng)建
                 */
                //獲取構(gòu)造方法
                Constructor c=clazz.getDeclaredConstructor(String.class,int.class);
                //創(chuàng)建對(duì)象并設(shè)置屬性
                Person p1=(Person) c.newInstance("李四",20);
                System.out.println(p1.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

/*輸出
姓名:張三  年齡:16
姓名:李四  年齡:20
*/

以上即為 反射的簡(jiǎn)單介紹樟凄。也許日后我會(huì)再深入分析反射的應(yīng)用場(chǎng)景聘芜。hhh


全文完,共勉缝龄。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末汰现,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子叔壤,更是在濱河造成了極大的恐慌瞎饲,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炼绘,死亡現(xiàn)場(chǎng)離奇詭異嗅战,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)俺亮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門驮捍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人脚曾,你說我怎么就攤上這事东且。” “怎么了本讥?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵珊泳,是天一觀的道長(zhǎng)鲁冯。 經(jīng)常有香客問我,道長(zhǎng)色查,這世上最難降的妖魔是什么晓褪? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮综慎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勤庐。我一直安慰自己示惊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布愉镰。 她就那樣靜靜地躺著米罚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丈探。 梳的紋絲不亂的頭發(fā)上录择,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音碗降,去河邊找鬼隘竭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛讼渊,可吹牛的內(nèi)容都是我干的动看。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼爪幻,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼菱皆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挨稿,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤仇轻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后奶甘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體篷店,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年甩十,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了船庇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侣监,死狀恐怖鸭轮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情橄霉,我是刑警寧澤窃爷,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布邑蒋,位于F島的核電站,受9級(jí)特大地震影響按厘,放射性物質(zhì)發(fā)生泄漏医吊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一逮京、第九天 我趴在偏房一處隱蔽的房頂上張望卿堂。 院中可真熱鬧,春花似錦懒棉、人聲如沸草描。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽穗慕。三九已至,卻和暖如春妻导,著一層夾襖步出監(jiān)牢的瞬間逛绵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工倔韭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留术浪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓寿酌,卻偏偏與公主長(zhǎng)得像添吗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子份名,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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