中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中RTTI與反射機制的區別有什么

發布時間:2020-12-05 17:29:04 來源:億速云 閱讀:142 作者:Leah 欄目:編程語言

今天就跟大家聊聊有關Java中RTTI與反射機制的區別有什么,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

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

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

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

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方法是靜態方法,參數是類名,用來查找是否存在該類,如果找到則返回一個Class引用,否則會拋出ClassNotFoundException異常。

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

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

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

對于基本數據類型的包裝類來說,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。為了放松限制可以使用通配符&#63;,即Class<&#63;>,效果跟Class是一樣的,但是代碼更為優雅,使用Class<&#63;>表示你并非是碰巧或疏忽才使用一個非具體的類引用。同時,可以限制繼承的類,示例如下

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

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

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

1、傳統的類型轉換,由RTTI保證類型轉換的正確性,如果執行一個錯誤的類型轉換,就會拋出ClassCastException異常;

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

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

而Java中RTTI還有第3種形式,就是關鍵字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等),現在某方法要為所有Circle上色,而輸入參數時一堆Shape對象,此時就可以用instandof判斷該Shape對象是不是Circle對象。

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

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

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

這樣就可以用Constructor創建未知對象,用get()和set()方法讀取和修改與Field對象關聯的字段,用invoke方法調用與Method對象關聯的字段,等等。

// 使用反射展示類的所有方法, 即使方法是在基類中定義的
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<&#63;> c = Class.forName(args[0]);
      // 反射獲得對象c所屬類的方法
      Method[] methods = c.getMethods();
      // 反射獲得對象c所屬類的構造
      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()
  */
}

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

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"
        + "參數: " + 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();
    // 使用動態代理
    Interface proxy = (Interface)Proxy.newProxyInstance(
        Interface.class.getClassLoader(),
        new Class[] { Interface.class }, 
        new DynamicProxyHandler(realObject));
    consumer(proxy);
  } /* 輸出:
  代理類: class $Proxy0
  代理方法: public abstract void Interface.doSomething()
  參數: null
  doSomething
  */
}

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

關鍵方法是:

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

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

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

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

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

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

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

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

看完上述內容,你們對Java中RTTI與反射機制的區別有什么有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

深水埗区| 乐昌市| 天镇县| 根河市| 仙游县| 商城县| 彭山县| 剑阁县| 离岛区| 策勒县| 中江县| 漳平市| 淮北市| 洛宁县| 彭泽县| 朝阳县| 夹江县| 漾濞| 江阴市| 攀枝花市| 通榆县| 台南县| 五原县| 昌图县| 邓州市| 南丹县| 富平县| 冕宁县| 上虞市| 麻城市| 嘉祥县| 邹城市| 庄河市| 环江| 沙雅县| 东兴市| 许昌市| 明水县| 马公市| 和平县| 伊金霍洛旗|