一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Java教程 - Java的RTTI和反射機(jī)制代碼分析

Java的RTTI和反射機(jī)制代碼分析

2021-01-06 10:59Harley_Quinn Java教程

這篇文章主要涉及了Java的RTTI和反射機(jī)制代碼分析的相關(guān)內(nèi)容,在介紹運(yùn)行時(shí)類型識(shí)別的同時(shí),又向大家展示了其實(shí)例以及什么時(shí)候會(huì)用到反射機(jī)制,內(nèi)容豐富,需要的朋友可以參考下。

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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對(duì)象的基類
    Object obj = null;
    try {
      obj = up.newInstance();
    } catch (InstantiationException e) {
      System.out.println("不能實(shí)例化");
      System.exit(1);
    } catch (IllegalAccessException e) {
      System.out.println("不能訪問(wèn)");
      System.exit(1);
    }
    printerInfo(obj.getClass());
  } /* 輸出:
  加載Base類
  加載Derived類
  類名: typeinfo.Derived是否接口? [false]
  類名: typeinfo.Base是否接口? [false]
  */
}

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

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

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

Java還提供了一種方法來(lái)生成對(duì)Class對(duì)象的引用,即類字面常量。對(duì)上述程序來(lái)說(shuō),up等價(jià)于Base.class。

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

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

?
1
2
3
4
5
Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
genericIntClass = Integer.class; // 等價(jià)
intClass = double.class; // ok
// genericIntClass = double.class; // Illegal!

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

?
1
2
3
4
5
6
7
8
9
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引用添加泛型語(yǔ)法的原因僅僅是為了提供編譯期類型檢查,以便在編譯時(shí)就能發(fā)現(xiàn)類型錯(cuò)誤。

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

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

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

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

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

?
1
2
3
4
5
6
7
8
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ù)時(shí)一堆Shape對(duì)象,此時(shí)就可以用instandof判斷該Shape對(duì)象是不是Circle對(duì)象。

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 使用反射展示類的所有方法, 即使方法是在基類中定義的
package typeinfo;
// Print類的print方法等價(jià)于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]);
      // 反射獲得對(duì)象c所屬類的方法
      Method[] methods = c.getMethods();
      // 反射獲得對(duì)象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()
  */
}

簡(jiǎn)單來(lái)說(shuō),反射機(jī)制就是識(shí)別未知類型的對(duì)象。反射常用于動(dòng)態(tài)代理中。舉例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
class DynamicProxyHandler implements InvocationHandler {
  private Object proxied; // 代理對(duì)象
  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();
    // 使用動(dòng)態(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è)計(jì)模式之一,即用代理類為被代理類提供額外的或不同的操作。而動(dòng)態(tài)代理則需要一個(gè)類加載器,就像Java實(shí)現(xiàn)RTTI時(shí)需要類加載器加載類的信息,這樣就可以知道類的相關(guān)信息。

關(guān)鍵方法是:

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

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

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

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

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

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

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

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

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

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

總結(jié)

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

原文鏈接:http://www.cnblogs.com/Harley-Quinn/p/5263802.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 免费高清视频日本 | 欧美一区二区三区精品 | 国产午夜视频在线观看网站 | 99精品影院 | 亚洲成综合人影院在院播放 | 韩国三级在线高速影院 | 免费观看无遮挡www的小视频 | bl双性肉文| 高清国语自产拍免费视频国产 | 调教禽兽| 欧美操屁股 | 久久电影精品久久99久久 | 好逼365 | 三级欧美在线 | 天堂成人在线 | 亚洲精品综合一二三区在线 | 欧美高清3dfreexxxx性 | 天天色资料 | 摄像头东北对白清晰 | 成免费视频| 无耻三级在线观看 | 羞羞漫画免费漫画页面在线看漫画秋蝉 | 2020精品极品国产色在线观看 | 色综合网天天综合色中文男男 | 亚洲精品黄色 | 久久99国产综合精品AV蜜桃 | 好深快点再快点好爽视频 | 九九精品视频在线播放 | 久久99r66热这里有精品 | 国产主播福利在线观看 | 三星w999| 亚洲天堂2016 | 无套内射在线观看THEPORN | 久草在线福利资站免费视频 | 99热综合在线 | 国产精品一区二区三区免费 | 精品国产免费一区二区三区 | 9191精品国产观看 | 欧美成人精品第一区二区三区 | 精品视频免费在线观看 | 韩国三级日本三级香港三级黄 |