亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java的RTTI和反射機制代碼分析

 更新時間:2017年09月19日 17:05:55   作者:Harley_Quinn  
這篇文章主要涉及了Java的RTTI和反射機制代碼分析的相關(guān)內(nèi)容,在介紹運行時類型識別的同時,又向大家展示了其實例以及什么時候會用到反射機制,內(nèi)容豐富,需要的朋友可以參考下。

RTTI,即Run-Time Type Identification,運行時類型識別。運行時類型識別是Java中非常有用的機制,在Java運行時,RTTI維護類的相關(guān)信息。RTTI能在運行時就能夠自動識別每個編譯時已知的類型。

很多時候需要進行向上轉(zhuǎn)型,比如Base類派生出Derived類,但是現(xiàn)有的方法只需要將Base對象作為參數(shù),實際傳入的則是其派生類的引用。那么RTTI就在此時起到了作用,比如通過RTTI能識別出Derive類是Base的派生類,這樣就能夠向上轉(zhuǎn)型為Derived。類似的,在用接口作為參數(shù)時,向上轉(zhuǎn)型更為常用,RTTI此時能夠判斷是否可以進行向上轉(zhuǎn)型。

而這些類型信息是通過Class對象(java.lang.Class)的特殊對象完成的,它包含跟類相關(guān)的信息。每當(dāng)編寫并編譯一個類時就會產(chǎn)生一個.class文件,保存著Class對象,運行這個程序的Java虛擬機(JVM)將使用被稱為類加載器(Class Loader)的子系統(tǒng)。而類加載器并非在程序運行之前就加載所有的Class對象,如果尚未加載,默認的類加載器就會根據(jù)類名查找.class文件(例如,某個附加類加載器可能會在數(shù)據(jù)庫中查找字節(jié)碼),在這個類的字節(jié)碼被加載時接受驗證,以確保沒有被破壞并且不包含不良Java代碼。這也是Java中的類型安全機制之一。一旦某個類的Class對象被載入內(nèi)存,就可以創(chuàng)建該類的所有對象。

package typeinfo;
class Base {
  static { System.out.println("加載Base類"); }
}
class Derived extends Base { 
  static { System.out.println("加載Derived類");}
}
public class Test {
  static void printerInfo(Class c) {
    System.out.println("類名: " + c.getName() +
      "是否接口? [" + c.isInterface() + "]");
  }
  public static void main(String[] args) {
    Class c = null;
    try {
      c = Class.forName("typeinfo.Derived");
    } catch (ClassNotFoundException e) {
      System.out.println("找不到Base類");
      System.exit(1);
    }
    printerInfo(c);
    Class up = c.getSuperclass(); // 取得c對象的基類
    Object obj = null;
    try {
      obj = up.newInstance();
    } catch (InstantiationException e) {
      System.out.println("不能實例化");
      System.exit(1);
    } catch (IllegalAccessException e) {
      System.out.println("不能訪問");
      System.exit(1);
    }
    printerInfo(obj.getClass());
  } /* 輸出:
  加載Base類
  加載Derived類
  類名: typeinfo.Derived是否接口? [false]
  類名: typeinfo.Base是否接口? [false]
  */
}

上述代碼中,forName方法是靜態(tài)方法,參數(shù)是類名,用來查找是否存在該類,如果找到則返回一個Class引用,否則會拋出ClassNotFoundException異常。

如果類不是在默認文件夾下,而是在某個包下,前面的包名需要帶上,比如這里的typeinfo.Derived。

可以通過getSuperclass方法來返回基類對應(yīng)的Class對象。使用newInstance方法可以按默認構(gòu)造創(chuàng)建一個實例對象,在不能實例化和不能訪問時分別拋出。會拋出InstantiationException和IllegalAccessException異常。

Java還提供了一種方法來生成對Class對象的引用,即類字面常量。對上述程序來說,up等價于Base.class。

對于基本數(shù)據(jù)類型的包裝類來說,char.class等價于Character.TYPE,int.class等價于Integer.TYPE。其余的ab.class等價于Ab.TYPE。(比如void.class等價于Void.TYP)。另外,Java SE5開始int.class和Integer.class也是一回事。

泛化的Class引用,見下面代碼

    Class intClass = int.class;
    Class<Integer> genericIntClass = int.class;
    genericIntClass = Integer.class; // 等價
    intClass = double.class; // ok
    // genericIntClass = double.class; // Illegal!

Class<Integer>對象的引用指定了Integer對象,所以不能將引用指向double.class。為了放松限制可以使用通配符?,即Class<?>,效果跟Class是一樣的,但是代碼更為優(yōu)雅,使用Class<?>表示你并非是碰巧或疏忽才使用一個非具體的類引用。同時,可以限制繼承的類,示例如下

class Base {}
class Derived extends Base {}
class Base2 {}
public class Test {
  public static void main(String[] args) {
    Class<? extends Base> cc = Derived.class; // ok
    // cc = Base2.class; // Illegal
  } 
}

向Class引用添加泛型語法的原因僅僅是為了提供編譯期類型檢查,以便在編譯時就能發(fā)現(xiàn)類型錯誤。

總結(jié)下來,我們已知的RTTI形式包括:

1、傳統(tǒng)的類型轉(zhuǎn)換,由RTTI保證類型轉(zhuǎn)換的正確性,如果執(zhí)行一個錯誤的類型轉(zhuǎn)換,就會拋出ClassCastException異常;

2、代表對象的類型的Class對象,通過查詢Class對象(即調(diào)用Class類的方法)可以獲取運行時所需的信息。

在C++中經(jīng)典的類型轉(zhuǎn)換并不使用RTTI,這點具體見C++的RTTI部分。(說句題外話,以前學(xué)C++時看到RTTI這章只是隨便掃了眼,現(xiàn)在才記起來dynamic_cast什么的都是為了類型安全而特地添加的,C++在安全方面可以提供選擇性,就像Java的StringBuilder和StringBuffer,安全和效率不可兼得?而Java在類型安全上則更為強制,就像表達式x = 1不能被隱式轉(zhuǎn)型為boolean類型)。

而Java中RTTI還有第3種形式,就是關(guān)鍵字instanceof,返回一個布爾值,告訴對象是不是某個特定類型的示例,見下列代碼。

class Base {}
class Derived extends Base {}
public class Test {
  public static void main(String[] args) {
    Derived derived = new Derived();
    System.out.println(derived instanceof Base); // 輸出true
  } 
}

利用 instanceof 可以判斷某些類型,比如基類Shape派生出各種類(Circle、Rectangle等),現(xiàn)在某方法要為所有Circle上色,而輸入?yún)?shù)時一堆Shape對象,此時就可以用instandof判斷該Shape對象是不是Circle對象。

RTTI可以識別程序空間的所有類,但是有時候需要從磁盤文件或網(wǎng)絡(luò)文件中讀取一串字節(jié)碼,并且被告知這些字節(jié)代表一個類,就需要用到反射機制。

比如在IDE中創(chuàng)建圖形化程序時會使用到一些控件,只需要從本地的控件對應(yīng)class文件中讀取即可,然后再主動修改這些控件的屬性。(題外話:大概.net組件就是這樣的?學(xué)C#時總聽到反射,但總沒感覺用過,前幾天做.net項目的同學(xué)也跟我說他從來都沒用過委托和事件……)

Class類與java.lang.reflect類庫一起對反射的概念進行了支持,該類庫包含F(xiàn)ield、Method和Constructor類(每個類都實現(xiàn)了Member接口),這些類型的對象都是JVM在運行時創(chuàng)建的,用以表示未知類里對應(yīng)成員。

這樣就可以用Constructor創(chuàng)建未知對象,用get()和set()方法讀取和修改與Field對象關(guān)聯(lián)的字段,用invoke方法調(diào)用與Method對象關(guān)聯(lián)的字段,等等。

// 使用反射展示類的所有方法, 即使方法是在基類中定義的
package typeinfo;
// Print類的print方法等價于System.Out.Println,方便減少代碼量
import static xyz.util.Print.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
// {Args: typeinfo.ShowMethods}
public class ShowMethods {
  private static String usage = 
    "usage:\n" +
    "ShowMethods qualified.class.name\n" +
    "To show all methods in class or:\n" +
    "ShowMethods qualified.class.name word\n" +
    "To search for methods involving 'word'";
  // 去掉類名前面的包名
  private static Pattern p = Pattern.compile("\\w+\\.");
  public static void main(String[] args) {
    if (args.length < 1) {
      print(usage);
      System.exit(0);
    }
    int lines = 0;
    try {
      Class<?> c = Class.forName(args[0]);
      // 反射獲得對象c所屬類的方法
      Method[] methods = c.getMethods();
      // 反射獲得對象c所屬類的構(gòu)造
      Constructor[] ctors = c.getConstructors();
      if (args.length == 1) {
        for (Method method : methods)
          print(p.matcher(method.toString()).replaceAll(""));
        for (Constructor ctor : ctors)
          print(p.matcher(ctor.toString()).replaceAll(""));
      }
    } catch (ClassNotFoundException e) {
      print("No such class: " + e);
    }
  } /*
  public static void main(String[])
  public final void wait() throws InterruptedException
  public final void wait(long,int) throws InterruptedException
  public final native void wait(long) throws InterruptedException
  public boolean equals(Object)
  public String toString()
  public native int hashCode()
  public final native Class getClass()
  public final native void notify()
  public final native void notifyAll()
  public ShowMethods()
  */
}

簡單來說,反射機制就是識別未知類型的對象。反射常用于動態(tài)代理中。舉例如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
class DynamicProxyHandler implements InvocationHandler {
  private Object proxied; // 代理對象
  public DynamicProxyHandler(Object proxied) {
    // TODO Auto-generated constructor stub
    this.proxied = proxied;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // TODO Auto-generated method stub
    System.out.println("代理類: " + proxy.getClass() + "\n"
        + "代理方法: " + method + "\n"
        + "參數(shù): " + args);
    if (args != null)
      for (Object arg : args)
        System.out.println(" " + arg);
    return method.invoke(proxied, args);
  }
}
interface Interface { void doSomething(); }

class RealObject implements Interface {
  
  @Override
  public void doSomething() {
    // TODO Auto-generated method stub
    System.out.println("doSomething");
  }
}
public class DynamicProxyDemo {
  public static void consumer(Interface iface) {
    iface.doSomething();
  }
  public static void main(String[] args) {
    RealObject realObject = new RealObject();
    // 使用動態(tài)代理
    Interface proxy = (Interface)Proxy.newProxyInstance(
        Interface.class.getClassLoader(),
        new Class[] { Interface.class }, 
        new DynamicProxyHandler(realObject));
    consumer(proxy);
  } /* 輸出:
  代理類: class $Proxy0
  代理方法: public abstract void Interface.doSomething()
  參數(shù): null
  doSomething
  */
}

代理是基本的設(shè)計模式之一,即用代理類為被代理類提供額外的或不同的操作。而動態(tài)代理則需要一個類加載器,就像Java實現(xiàn)RTTI時需要類加載器加載類的信息,這樣就可以知道類的相關(guān)信息。

關(guān)鍵方法是:

Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

傳入三個參數(shù):代理接口的加載器(通過Class對象的getClassLoader方法獲取),代理的方法接口,代理對象

前兩個參數(shù)很好理解,就是要代理的方法所屬的接口對應(yīng)的Class對象(主語)的加載器和Class對象本身,主要是參數(shù)3,要設(shè)計一個實現(xiàn)InvocationHandler接口的類,作為代理對象,一般命名以Handler結(jié)尾,Handler翻譯為處理者,很形象,就是代替原對象進行處理的處理者(即代理),在程序設(shè)計中經(jīng)常被翻譯成“句柄”。

這個類通過傳入代理對象來構(gòu)造,比如這里傳入的是Object對象。然后必須覆蓋invoke方法。

通過最后輸出和invoke方法的具體實現(xiàn)可以發(fā)現(xiàn),return method.invoke(proxied, args);是相當(dāng)于原對象調(diào)用該方法(類似C++的回調(diào)函數(shù)?)

由于有類加載器,所以代理對象可以知道原對象的具體類名、方法、參數(shù),本示例在調(diào)用方法前就輸出了這些。

實際應(yīng)用中可能會針對類名而有所選擇。比如接口中有好多個類,你可以選擇性的對特定的類、方法、參數(shù)進行處理

比如 if(proxied instanceof RealObject) {} 或者 if(method.getName.equals("doSomething")) {}

PS:我這個示例沒有參數(shù)所以沒有距離

參考:《Java編程思想》第四版,更多細節(jié)見書上第14章

總結(jié)

以上就是本文關(guān)于Java的RTTI和反射機制代碼分析的全部內(nèi)容,希望對大家有所幫助。

相關(guān)文章

  • 移除元素Java實現(xiàn)方式

    移除元素Java實現(xiàn)方式

    這篇文章主要介紹了移除元素Java實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Java面向?qū)ο筮x擇題總結(jié)歸納

    Java面向?qū)ο筮x擇題總結(jié)歸納

    今天小編就為大家分享一篇關(guān)于Java面向?qū)ο筮x擇題總結(jié)歸納,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Spring Boot2.x集成JPA快速開發(fā)的示例代碼

    Spring Boot2.x集成JPA快速開發(fā)的示例代碼

    這篇文章主要介紹了Spring Boot2.x集成JPA快速開發(fā),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • 詳解IDEA中Debug的使用和進制轉(zhuǎn)換問題

    詳解IDEA中Debug的使用和進制轉(zhuǎn)換問題

    這篇文章主要介紹了IDEA中Debug的使用和進制轉(zhuǎn)換,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • 如何使用mybatis-plus實現(xiàn)分頁查詢功能

    如何使用mybatis-plus實現(xiàn)分頁查詢功能

    最近在研究mybatis,然后就去找簡化mybatis開發(fā)的工具,發(fā)現(xiàn)就有通用Mapper和mybatis-plus兩個比較好的可是使用,可是經(jīng)過對比發(fā)現(xiàn)還是mybatis-plus比較好,下面這篇文章主要給大家介紹了關(guān)于如何使用mybatis-plus實現(xiàn)分頁查詢功能的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之貪心算法

    Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之貪心算法

    我們可能在好多地方都會聽到貪心算法這一概念,并且它的算法思想也比較簡單就是說算法只保證局部最優(yōu),進而達到全局最優(yōu)。但我們實際編程的過程中用的并不是很多,究其原因可能是貪心算法使用的條件比較苛刻,所要解決的問題必須滿足貪心選擇性質(zhì)
    2022-02-02
  • Spring Boot自定義Banner實現(xiàn)代碼

    Spring Boot自定義Banner實現(xiàn)代碼

    這篇文章主要介紹了Spring Boot自定義Banner實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-01-01
  • mybatis深入講解resultMap的定義及用法

    mybatis深入講解resultMap的定義及用法

    MyBatis的每一個查詢映射的返回類型都是ResultMap,當(dāng)我們提供返回類型屬性是resultType時,MyBatis會自動給我們把對應(yīng)值賦給resultType所指定對象的屬性,當(dāng)我們提供返回類型是resultMap時,將數(shù)據(jù)庫中列數(shù)據(jù)復(fù)制到對象的相應(yīng)屬性上,可以用于復(fù)制查詢,兩者不能同時用
    2022-04-04
  • 通過實例了解java TransferQueue

    通過實例了解java TransferQueue

    這篇文章主要介紹了TransferQueue實例,下面小編和大家一起來學(xué)習(xí)一下
    2019-05-05
  • Spring Boot高級教程之Spring Boot連接MySql數(shù)據(jù)庫

    Spring Boot高級教程之Spring Boot連接MySql數(shù)據(jù)庫

    這篇文章主要為大家詳細介紹了Spring Boot高級教程之Spring Boot連接MySql數(shù)據(jù)庫,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10

最新評論