成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

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

icyfire / 1717人閱讀

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

導(dǎo)讀

源碼地址

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

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

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

RTTI

po類

/**
 * 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);
    }
}

以上是用來說明的persistent object類,也就是,我們在進(jìn)行pojo常用的javabean類。其有繼承關(guān)系,如下圖:

展示信息

如下代碼所示,方法eatWhatToday有兩個(gè)參數(shù),這兩個(gè)參數(shù)一個(gè)是接口類,一個(gè)是父類,也就是說,我們并不知道打印出的是什么信息。只有根據(jù)接口的實(shí)現(xiàn)類來和父類的子類,來確認(rèn)打印出的信息。這就是我們說的運(yùn)行時(shí)類型信息,正因?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 枚舉類型 這里表示的時(shí)間
     * @param pet      寵物
     */
    public static void eatWhatToday(BaseEnum baseEnum, Pet pet) {
        System.out.println( pet.getName() + "今天" + baseEnum.getTitle() + "吃的" + pet.getFood());
    }
    
}

測試類

 @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)前類信息

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

測試:
 @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è)神奇的地方,就是名字沒有輸出來,但我們寫了名字呀,為什么沒有輸出來?因?yàn)?,dog是繼承了父類Pet,當(dāng)我們在創(chuàng)建子類對象時(shí),首先,會(huì)加載父類未加載的構(gòu)造器、靜態(tài)代碼塊、靜態(tài)屬性、靜態(tài)方法等等。但是,Dog在這里是以無參構(gòu)造器加載的,當(dāng)然,同時(shí)也通過無參構(gòu)造器的實(shí)例化了父類。我們在給dog對象的name賦值時(shí),并沒有給父類對象的name賦值,所以,dog的name是沒有值的。父類引用指向子類對象,就是這個(gè)意思。

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

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

    /**
     * Created By zby on 18:26 2019/3/16
     * 普通的方式創(chuàng)建對象
     */
    public static  T byConstruct(Class clazz, String name, BaseEnum baseEnum) {
        if (null == clazz) {
            return null;
        }
//        參數(shù)類型,
        Class paramType[] = {String.class, String.class};
        try {
//          一般情況下,構(gòu)造器不止一個(gè),我們根據(jù)構(gòu)器的參數(shù)類型,來使用構(gòu)造器創(chuàng)建對象
            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;
    }
    
    測試:
    
   @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);
    }

測試結(jié)果:

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

總結(jié)

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

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

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

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/73736.html

相關(guān)文章

  • 《Java編程思想》筆記14.類型信息

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

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

    摘要:因而,我從中也知道了,很多公司沒有實(shí)現(xiàn)數(shù)據(jù)過濾。因?yàn)?,那樣將?huì)造成數(shù)據(jù)的冗余。因而,我們這時(shí)需要過濾數(shù)據(jù)對象,如代碼所示常用的把圖片轉(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)系。尤其是對反射做了詳細(xì)說明,很多培訓(xùn)機(jī)構(gòu)也將其作為高級教程來講解。 其實(shí),我工作年限...

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

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

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

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

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

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

    fobnn 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<