成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

框架與RTTI的關(guān)系,RTTI與反射之間的關(guān)系

icyfire / 1676人閱讀

摘要:不管是什么樣的框架,其都涉及到反射。見(jiàn)名知其意,即類(lèi)對(duì)象,其包含了類(lèi)的所有信息,包括屬性方法構(gòu)造器。為了生成這個(gè)類(lèi)的對(duì)象,運(yùn)行當(dāng)前程序的將使用到類(lèi)加載器。這種是等主流框架使用的。

導(dǎo)讀

源碼地址

在之后的幾篇文章,我會(huì)講解我自己的hibernate、spring、beanutils框架,但講解這些框架之前,我需要講解RTTI和反射。

工作將近一年了,我們公司項(xiàng)目所使用的框架是SSH,或者,其他公司使用的是SSM框架。不管是什么樣的框架,其都涉及到反射。那么,什么是反射?我們?cè)谏蓪?duì)象時(shí),事先并不知道生成哪種類(lèi)型的對(duì)象,只有等到項(xiàng)目運(yùn)行起來(lái),框架根據(jù)我們的傳參,才生成我們想要的對(duì)象。

比如,我們從前端調(diào)用后端的接口,查詢(xún)出這個(gè)人的所有項(xiàng)目,我們只要傳遞這個(gè)人的id即可。當(dāng)然,數(shù)據(jù)來(lái)源于數(shù)據(jù)庫(kù),那么,問(wèn)題來(lái)了,數(shù)據(jù)是怎么從持久態(tài)轉(zhuǎn)化成我們想要的順時(shí)態(tài)的?這里面,就涉及到了反射。但是,一提到反射,我們勢(shì)必就提到RTTI,即運(yùn)行時(shí)類(lèi)型信息(runtime Type Infomation)。

RTTI

po類(lèi)

/**
 * Created By zby on 16:53 2019/3/16
 */
@AllArgsConstructor
@NoArgsConstructor
public class Pet {

    private String name;

    private String food;

    public void setName(String name) {
        this.name = name;
    }

    public void setFood(String food) {
        this.food = food;
    }

    public String getName() {
        return name;
    }

    public String getFood() {
        return food;
    }
}

/**
 * Created By zby on 17:03 2019/3/16
 */
public class Cat extends Pet{

    @Override
    public void setFood(String food) {
        super.setFood(food);
    }
}

/**
 * Created By zby on 17:04 2019/3/16
 */
public class Garfield extends Cat{

    @Override
    public void setFood(String food) {
        super.setFood(food);
    }
}


/**
 * Created By zby on 17:01 2019/3/16
 */
public class Dog extends Pet{

    @Override
    public void setFood(String food) {
        super.setFood(food);
    }
}

以上是用來(lái)說(shuō)明的persistent object類(lèi),也就是,我們?cè)谶M(jìn)行pojo常用的javabean類(lèi)。其有繼承關(guān)系,如下圖:

展示信息

如下代碼所示,方法eatWhatToday有兩個(gè)參數(shù),這兩個(gè)參數(shù)一個(gè)是接口類(lèi),一個(gè)是父類(lèi),也就是說(shuō),我們并不知道打印出的是什么信息。只有根據(jù)接口的實(shí)現(xiàn)類(lèi)來(lái)和父類(lèi)的子類(lèi),來(lái)確認(rèn)打印出的信息。這就是我們說(shuō)的運(yùn)行時(shí)類(lèi)型信息,正因?yàn)橛辛薘TTI,java才有了動(dòng)態(tài)綁定的概念。

/**
 * Created By zby on 17:05 2019/3/16
 */
public class FeedingPet {

    /**
     * Created By zby on 17:05 2019/3/16
     * 某種動(dòng)物今天吃的是什么
     *
     * @param baseEnum 枚舉類(lèi)型 這里表示的時(shí)間
     * @param pet      寵物
     */
    public static void eatWhatToday(BaseEnum baseEnum, Pet pet) {
        System.out.println( pet.getName() + "今天" + baseEnum.getTitle() + "吃的" + pet.getFood());
    }
    
}

測(cè)試類(lèi)

 @Test
public void testPet(){
    Dog dog=new Dog();
    dog.setName("寵物狗京巴");
    dog.setFood(FoodTypeEnum.FOOD_TYPE_BONE.getTitle());

    FeedingPet.eatWhatToday(DateTypeEnum.DATE_TYPE_MORNING,dog);

    Garfield garfield=new Garfield();
    garfield.setName("寵物貓加菲貓");
    garfield.setFood(FoodTypeEnum.FOOD_TYPE_CURRY.getTitle());
    FeedingPet.eatWhatToday(DateTypeEnum.DATE_TYPE_MIDNIGHT_SNACK,garfield);
}

打印出的信息為:

那么,這和反射有什么關(guān)系呢?

反射獲取當(dāng)前類(lèi)信息

正如上文提到的運(yùn)行時(shí)類(lèi)型信息,那么,類(lèi)型信息在運(yùn)行時(shí)是如何表示的?此時(shí),我們就想到了Class這個(gè)特殊對(duì)象。見(jiàn)名知其意,即類(lèi)對(duì)象,其包含了類(lèi)的所有信息,包括屬性、方法、構(gòu)造器。

我們都知道,類(lèi)是程序的一部分,每個(gè)類(lèi)都有一個(gè)Class對(duì)象。每當(dāng)編寫(xiě)并且執(zhí)行了一個(gè)新類(lèi),就會(huì)產(chǎn)生一個(gè)Class對(duì)象(更恰當(dāng)?shù)卣f(shuō),是被保存在一個(gè)同名的.class文件中)。為了生成這個(gè)類(lèi)的對(duì)象,運(yùn)行當(dāng)前程序的jvm將使用到類(lèi)加載器。jvm首先調(diào)用bootstrap類(lèi)加載器,加載核心文件,jdk的核心文件,比如Object,System等類(lèi)文件。然后調(diào)用plateform加載器,加載一些與文件相關(guān)的類(lèi),比如壓縮文件的類(lèi),圖片的類(lèi)等等。最后,才用applicationClassLoader,加載用戶(hù)自定義的類(lèi)。

加載當(dāng)前類(lèi)信息

反射正是利用了Class來(lái)創(chuàng)建、修改對(duì)象,獲取和修改屬性的值等等。那么,反射是怎么創(chuàng)建當(dāng)前類(lèi)的呢?

第一種,可以使用當(dāng)前上下文的類(lèi)路徑來(lái)創(chuàng)建對(duì)象,如我們記載jdbc類(lèi)驅(qū)動(dòng)的時(shí)候,如以下代碼:

/**
 * Created By zby on 18:07 2019/3/16
 * 通過(guò)上下文的類(lèi)路徑來(lái)加載信息
 */
public static Class byClassPath(String classPath) {
    if (StringUtils.isBlank(classPath)) {
        throw new RuntimeException("類(lèi)路徑不能為空");
    }
    classPath = classPath.replace(" ", "");
    try {
        return Class.forName(classPath);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return null;
}

第二種,通過(guò)類(lèi)字面常量,這種做法非常簡(jiǎn)單,而且更安全。因?yàn)?,他在編譯時(shí)就會(huì)受到檢查,我們不需要將其置于try catch的代碼快中,而且,它根除了對(duì)forName的方法調(diào)用,所以,更高效。這種是spring、hibernate等主流框架使用的。

框架hibernate的內(nèi)部使用類(lèi)字面常量去創(chuàng)建對(duì)象后,底層通過(guò)jdbc獲取數(shù)據(jù)表的字段值,根據(jù)數(shù)據(jù)表的字段與當(dāng)前類(lèi)的屬性進(jìn)行一一匹配,將字段值填充到當(dāng)前對(duì)象中。匹配不成功,就會(huì)報(bào)出相應(yīng)的錯(cuò)誤。

類(lèi)字面常量獲取對(duì)象信息,如代碼所示。下文,也是通過(guò)類(lèi)字面常量創(chuàng)建對(duì)象。

 /**
 * Created By zby on 18:16 2019/3/16
 * 通過(guò)類(lèi)字面常量加載當(dāng)前類(lèi)的信息
 */
public static void byClassConstant() {
    System.out.println(Dog.class);
}

第三種,是通過(guò)對(duì)象來(lái)創(chuàng)建當(dāng)前類(lèi),這種會(huì)在框架內(nèi)部使用。

/**
* Created By zby on 18:17 2019/3/16
* 通過(guò)類(lèi)對(duì)象加載當(dāng)前類(lèi)的信息
*/
public static Class byCurrentObject(Object object) {
    return object.getClass();
}
反射創(chuàng)建當(dāng)前類(lèi)對(duì)象

我們創(chuàng)建當(dāng)前對(duì)象,一般有兩種方式,一種是通過(guò)clazz.newInstance();這種一般是無(wú)參構(gòu)造器,并且創(chuàng)建對(duì)對(duì)象后,可以獲取其屬性,通過(guò)屬性賦值和方法賦值,如如代碼所示:

第一種,通過(guò)clazz.newInstance()創(chuàng)建對(duì)象

/**
 * Created By zby on 18:26 2019/3/16
 * 普通的方式創(chuàng)建對(duì)象
 */
public static  T byCommonGeneric(Class clazz, String name, BaseEnum baseEnum) {
    if (null == clazz) {
        return null;
    }
    try {
        T t = (T) clazz.newInstance();
        
        //通過(guò)屬性賦值,getField獲取公有屬性,獲取私有屬性
        Field field = clazz.getDeclaredField("name");
        //跳過(guò)檢查,否則,我們沒(méi)辦法操作私有屬性
        field.setAccessible(true);
        field.set(t, name);
        
        //通過(guò)方法賦值
        Method method1 = clazz.getDeclaredMethod("setFood", String.class);
        method1.setAccessible(true);
        method1.invoke(t, baseEnum.getTitle());

        return t;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

測(cè)試:
 @Test
public void testCommonGeneric() {
    Dog dog= GenericCurrentObject.byCommonGeneric(Dog.class,
            "寵物狗哈士奇", 
            FoodTypeEnum.FOOD_TYPE_BONE);
    FeedingPet.eatWhatToday(DateTypeEnum.DATE_TYPE_NOON,dog);
}

叔叔出結(jié)果為:

你會(huì)發(fā)現(xiàn)一個(gè)神奇的地方,就是名字沒(méi)有輸出來(lái),但我們寫(xiě)了名字呀,為什么沒(méi)有輸出來(lái)?因?yàn)?,dog是繼承了父類(lèi)Pet,當(dāng)我們?cè)趧?chuàng)建子類(lèi)對(duì)象時(shí),首先,會(huì)加載父類(lèi)未加載的構(gòu)造器、靜態(tài)代碼塊、靜態(tài)屬性、靜態(tài)方法等等。但是,Dog在這里是以無(wú)參構(gòu)造器加載的,當(dāng)然,同時(shí)也通過(guò)無(wú)參構(gòu)造器的實(shí)例化了父類(lèi)。我們?cè)诮odog對(duì)象的name賦值時(shí),并沒(méi)有給父類(lèi)對(duì)象的name賦值,所以,dog的name是沒(méi)有值的。父類(lèi)引用指向子類(lèi)對(duì)象,就是這個(gè)意思。

如果我們把Dog類(lèi)中的 @Override public void setFood(String food) {super.setFood(food); }super.setFood(food); 方法去掉,屬性food也是沒(méi)有值的。如圖所示:

通過(guò)構(gòu)造器創(chuàng)建對(duì)象

    /**
     * Created By zby on 18:26 2019/3/16
     * 普通的方式創(chuàng)建對(duì)象
     */
    public static  T byConstruct(Class clazz, String name, BaseEnum baseEnum) {
        if (null == clazz) {
            return null;
        }
//        參數(shù)類(lèi)型,
        Class paramType[] = {String.class, String.class};
        try {
//          一般情況下,構(gòu)造器不止一個(gè),我們根據(jù)構(gòu)器的參數(shù)類(lèi)型,來(lái)使用構(gòu)造器創(chuàng)建對(duì)象
            Constructor constructor = clazz.getConstructor(paramType);
//            給構(gòu)造器賦值,賦值個(gè)數(shù)和構(gòu)造器的形參個(gè)數(shù)一樣,否則,會(huì)報(bào)錯(cuò)
            return (T) constructor.newInstance(name, baseEnum.getTitle());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    測(cè)試:
    
   @Test
    public void testConstruct() {
        Dog dog= GenericCurrentObject.byConstruct(Dog.class,
                "寵物狗哈士奇",
                FoodTypeEnum.FOOD_TYPE_BONE);
        System.out.println("輸出寵物的名字:"+dog.getName()+"
");
        System.out.println("寵物吃的什么:"+dog.getFood()+"
");
        FeedingPet.eatWhatToday(DateTypeEnum.DATE_TYPE_MIDNIGHT_SNACK,dog);
    }

測(cè)試結(jié)果:

這是通過(guò)構(gòu)造器創(chuàng)建的對(duì)象。但是注意的是,形參類(lèi)型和和參數(shù)值的位數(shù)一定要相等,否則,就會(huì)報(bào)出錯(cuò)誤的。

總結(jié)

為什么寫(xiě)這篇文章,前面也說(shuō)了,很多框架都用到了反射和RTTI。但是,我們的平常的工作,一般以業(yè)務(wù)為主。往往都是使用別人封裝好的框架,比如spring、hibernate、mybatis、beanutils等框架。所以,我們不大會(huì)關(guān)注反射,但是,你如果想要往更高的方向去攀登,還是要把基礎(chǔ)給打撈。否則,基礎(chǔ)不穩(wěn),爬得越高,摔得越重。

我會(huì)以后的篇章中,通過(guò)介紹我寫(xiě)的spring、hibernate框架,來(lái)講解更好地講解反射。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73736.html

相關(guān)文章

  • 《Java編程思想》筆記14.類(lèi)型信息

    摘要:接口與類(lèi)型信息關(guān)鍵字的一種重要目標(biāo)就是允許程序員隔離構(gòu)件,進(jìn)而降低耦合性。如果你編寫(xiě)接口,那么就可以實(shí)現(xiàn)這一目標(biāo),但是通過(guò)類(lèi)型信息,這種耦合性還是會(huì)傳播出去接口并非是對(duì)解耦的一種無(wú)懈可擊的保障。 點(diǎn)擊進(jìn)入我的博客 運(yùn)行時(shí)類(lèi)型信息使得你可以在運(yùn)行時(shí)發(fā)現(xiàn)和使用類(lèi)型信息,主要有兩種方式: 傳統(tǒng)的RTTI,它假定我們?cè)诰幾g時(shí)已經(jīng)知道了所有的類(lèi)型; 反射機(jī)制,它允許我們?cè)谶\(yùn)行時(shí)發(fā)現(xiàn)和使用類(lèi)的...

    Hwg 評(píng)論0 收藏0
  • 只因數(shù)據(jù)過(guò)濾,方可模擬beanutils框架

    摘要:因而,我從中也知道了,很多公司沒(méi)有實(shí)現(xiàn)數(shù)據(jù)過(guò)濾。因?yàn)?,那樣將?huì)造成數(shù)據(jù)的冗余。因而,我們這時(shí)需要過(guò)濾數(shù)據(jù)對(duì)象,如代碼所示常用的把圖片轉(zhuǎn)成結(jié)構(gòu)如上訴代碼的轉(zhuǎn)換,公司使用的是這個(gè)框架。而棧是存放數(shù)據(jù)的一種結(jié)構(gòu),其采用,即先進(jìn)后出。 導(dǎo)讀 上一篇文章已經(jīng)詳細(xì)介紹了框架與RTTI的關(guān)系,RTTI與反射之間的關(guān)系。尤其是對(duì)反射做了詳細(xì)說(shuō)明,很多培訓(xùn)機(jī)構(gòu)也將其作為高級(jí)教程來(lái)講解。 其實(shí),我工作年限...

    yzzz 評(píng)論0 收藏0
  • 【Java學(xué)習(xí)】JDBC學(xué)習(xí)(了解CLass等)

    摘要:同時(shí)也有一些兒高級(jí)的處理,比如批處理更新事務(wù)隔離和可滾動(dòng)結(jié)果集等。連接對(duì)象表示通信上下文,即,與數(shù)據(jù)庫(kù)中的所有的通信是通過(guò)此唯一的連接對(duì)象。因?yàn)槭轻槍?duì)類(lèi)的關(guān)系而言,所以一個(gè)對(duì)象對(duì)應(yīng)多個(gè)類(lèi)的實(shí)例化。返回表示查詢(xún)返回表示其它操作。 JDBC是什么? JDBC是一個(gè)Java API,用中文可以通俗的解釋為,使用Java語(yǔ)言訪(fǎng)問(wèn)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的一套接口集合。這是調(diào)用者(程序員)和實(shí)行者(數(shù)據(jù)庫(kù)廠(chǎng)商...

    cjie 評(píng)論0 收藏0
  • Thinking in Java學(xué)習(xí)筆記——Type Information

    摘要:找到字節(jié)碼并創(chuàng)建一個(gè)對(duì)象。鏈接,檢驗(yàn)字節(jié)碼,為字段分配存儲(chǔ)空間,解決其對(duì)他類(lèi)的引用。初始化,如果有父類(lèi)則初始化父類(lèi),執(zhí)行靜態(tài)初始化器和靜態(tài)初始化區(qū)塊直到第一次訪(fǎng)問(wèn)靜態(tài)成員時(shí)初始化才執(zhí)行。如果成員不是編譯時(shí)常量由初始化器賦值,也會(huì)引起初始化。 有兩種形式在運(yùn)行時(shí)獲取類(lèi)型信息: 傳統(tǒng)的RTTI 反射 Class對(duì)象 運(yùn)行時(shí)的類(lèi)型信息是通過(guò)Class對(duì)象表現(xiàn)的,它包含了類(lèi)的信息。所有...

    liangzai_cool 評(píng)論0 收藏0
  • 反射機(jī)制原理筆記

    反射機(jī)制與原理筆記 聲明 文章均為本人技術(shù)筆記,轉(zhuǎn)載請(qǐng)注明出處https://segmentfault.com/u/yzwall 反射機(jī)制 反射:當(dāng)程序無(wú)法獲知對(duì)象類(lèi)型時(shí),在運(yùn)行期間動(dòng)態(tài)獲取類(lèi)的所有屬性和方法,這種動(dòng)態(tài)獲取類(lèi)信息和動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱(chēng)為反射機(jī)制;反射機(jī)制實(shí)現(xiàn):Class類(lèi)與java.lang.reflect類(lèi)庫(kù)一起實(shí)現(xiàn)機(jī)制,java.lang.reflect類(lèi)庫(kù)包含F(xiàn)ield...

    fobnn 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<