<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## [`Class`對象](https://lingcoder.gitee.io/onjava8/#/book/19-Type-Information?id=class-%e5%af%b9%e8%b1%a1) 要理解 RTTI 在 Java 中的工作原理,首先必須知道類型信息在運行時是如何表示的。這項工作是由稱為**`Class`對象**的特殊對象完成的,它包含了與類有關的信息。實際上,`Class`對象就是用來創建該類所有"常規"對象的。Java 使用`Class`對象來實現 RTTI,即便是類型轉換這樣的操作都是用`Class`對象實現的。不僅如此,`Class`類還提供了很多使用 RTTI 的其它方式。 類是程序的一部分,每個類都有一個`Class`對象。換言之,每當我們編寫并且編譯了一個新類,就會產生一個`Class`對象(更恰當的說,是被保存在一個同名的`.class`文件中)。為了生成這個類的對象,Java 虛擬機 (JVM) 先會調用"類加載器"子系統把這個類加載到內存中。 類加載器子系統可能包含一條類加載器鏈,但有且只有一個**原生類加載器**,它是 JVM 實現的一部分。原生類加載器加載的是”可信類”(包括 Java API 類)。它們通常是從本地盤加載的。在這條鏈中,通常不需要添加額外的類加載器,但是如果你有特殊需求(例如以某種特殊的方式加載類,以支持 Web 服務器應用,或者通過網絡下載類),也可以掛載額外的類加載器。 所有的類都是第一次使用時動態加載到 JVM 中的,當程序創建第一個對類的靜態成員的引用時,就會加載這個類。 > 其實構造器也是類的靜態方法,雖然構造器前面并沒有`static`關鍵字。所以,使用`new`操作符創建類的新對象,這個操作也算作對類的靜態成員引用。 因此,Java 程序在它開始運行之前并沒有被完全加載,很多部分是在需要時才會加載。這一點與許多傳統編程語言不同,動態加載使得 Java 具有一些靜態加載語言(如 C++)很難或者根本不可能實現的特性。 類加載器首先會檢查這個類的`Class`對象是否已經加載,如果尚未加載,默認的類加載器就會根據類名查找`.class`文件(如果有附加的類加載器,這時候可能就會在數據庫中或者通過其它方式獲得字節碼)。這個類的字節碼被加載后,JVM 會對其進行驗證,確保它沒有損壞,并且不包含不良的 Java 代碼(這是 Java 安全防范的一種措施)。 一旦某個類的`Class`對象被載入內存,它就可以用來創建這個類的所有對象。下面的示范程序可以證明這點: ~~~ // typeinfo/SweetShop.java // 檢查類加載器工作方式 class Cookie { static { System.out.println("Loading Cookie"); } } class Gum { static { System.out.println("Loading Gum"); } } class Candy { static { System.out.println("Loading Candy"); } } public class SweetShop { public static void main(String[] args) { System.out.println("inside main"); new Candy(); System.out.println("After creating Candy"); try { Class.forName("Gum"); } catch(ClassNotFoundException e) { System.out.println("Couldn't find Gum"); } System.out.println("After Class.forName(\"Gum\")"); new Cookie(); System.out.println("After creating Cookie"); } } ~~~ 輸出結果: ~~~ inside main Loading Candy After creating Candy Loading Gum After Class.forName("Gum") Loading Cookie After creating Cookie ~~~ 上面的代碼中,`Candy`、`Gum`和`Cookie`這幾個類都有一個`static{...}`靜態初始化塊,這些靜態初始化塊在類第一次被加載的時候就會執行。也就是說,靜態初始化塊會打印出相應的信息,告訴我們這些類分別是什么時候被加載了。而在主方法里邊,創建對象的代碼都放在了`print()`語句之間,以幫助我們判斷類加載的時間點。 從輸出中可以看到,`Class`對象僅在需要的時候才會被加載,`static`初始化是在類加載時進行的。 代碼里面還有特別有趣的一行: ~~~ Class.forName("Gum"); ~~~ 所有`Class`對象都屬于`Class`類,而且它跟其他普通對象一樣,我們可以獲取和操控它的引用(這也是類加載器的工作)。`forName()`是`Class`類的一個靜態方法,我們可以使用`forName()`根據目標類的類名(`String`)得到該類的`Class`對象。上面的代碼忽略了`forName()`的返回值,因為那個調用是為了得到它產生的“副作用”。從結果可以看出,`forName()`執行的副作用是如果`Gum`類沒有被加載就加載它,而在加載的過程中,`Gum`的`static`初始化塊被執行了。 還需要注意的是,如果`Class.forName()`找不到要加載的類,它就會拋出異常`ClassNotFoundException`。上面的例子中我們只是簡單地報告了問題,但在更嚴密的程序里,就要考慮在異常處理程序中把問題解決掉(具體例子詳見[設計模式](https://lingcoder.gitee.io/onjava8/#/./25-Patterns)章節)。 無論何時,只要你想在運行時使用類型信息,就必須先得到那個`Class`對象的引用。`Class.forName()`就是實現這個功能的一個便捷途徑,因為使用該方法你不需要先持有這個類型 的對象。但是,如果你已經擁有了目標類的對象,那就可以通過調用`getClass()`方法來獲取`Class`引用了,這個方法來自根類`Object`,它將返回表示該對象實際類型的`Class`對象的引用。`Class`包含很多有用的方法,下面代碼展示了其中的一部分: ~~~ // typeinfo/toys/ToyTest.java // 測試 Class 類 // {java typeinfo.toys.ToyTest} package typeinfo.toys; interface HasBatteries {} interface Waterproof {} interface Shoots {} class Toy { // 注釋下面的無參數構造器會引起 NoSuchMethodError 錯誤 Toy() {} Toy(int i) {} } class FancyToy extends Toy implements HasBatteries, Waterproof, Shoots { FancyToy() { super(1); } } public class ToyTest { static void printInfo(Class cc) { System.out.println("Class name: " + cc.getName() + " is interface? [" + cc.isInterface() + "]"); System.out.println( "Simple name: " + cc.getSimpleName()); System.out.println( "Canonical name : " + cc.getCanonicalName()); } public static void main(String[] args) { Class c = null; try { c = Class.forName("typeinfo.toys.FancyToy"); } catch(ClassNotFoundException e) { System.out.println("Can't find FancyToy"); System.exit(1); } printInfo(c); for(Class face : c.getInterfaces()) printInfo(face); Class up = c.getSuperclass(); Object obj = null; try { // Requires no-arg constructor: obj = up.newInstance(); } catch(InstantiationException e) { System.out.println("Cannot instantiate"); System.exit(1); } catch(IllegalAccessException e) { System.out.println("Cannot access"); System.exit(1); } printInfo(obj.getClass()); } } ~~~ 輸出結果: ~~~ Class name: typeinfo.toys.FancyToy is interface? [false] Simple name: FancyToy Canonical name : typeinfo.toys.FancyToy Class name: typeinfo.toys.HasBatteries is interface? [true] Simple name: HasBatteries Canonical name : typeinfo.toys.HasBatteries Class name: typeinfo.toys.Waterproof is interface? [true] Simple name: Waterproof Canonical name : typeinfo.toys.Waterproof Class name: typeinfo.toys.Shoots is interface? [true] Simple name: Shoots Canonical name : typeinfo.toys.Shoots Class name: typeinfo.toys.Toy is interface? [false] Simple name: Toy Canonical name : typeinfo.toys.Toy ~~~ `FancyToy`繼承自`Toy`并實現了`HasBatteries`、`Waterproof`和`Shoots`接口。在`main`方法中,我們創建了一個`Class`引用,然后在`try`語句里邊用`forName()`方法創建了一個`FancyToy`的類對象并賦值給該引用。需要注意的是,傳遞給`forName()`的字符串必須使用類的全限定名(包含包名)。 `printInfo()`函數使用`getName()`來產生完整類名,使用`getSimpleName()`產生不帶包名的類名,`getCanonicalName()`也是產生完整類名(除內部類和數組外,對大部分類產生的結果與`getName()`相同)。`isInterface()`用于判斷某個`Class`對象代表的是否為一個接口。因此,通過`Class`對象,你可以得到關于該類型的所有信息。 在主方法中調用的`Class.getInterfaces()`方法返回的是存放`Class`對象的數組,里面的`Class`對象表示的是那個類實現的接口。 另外,你還可以調用`getSuperclass()`方法來得到父類的`Class`對象,再用父類的`Class`對象調用該方法,重復多次,你就可以得到一個對象完整的類繼承結構。 `Class`對象的`newInstance()`方法是實現“虛擬構造器”的一種途徑,虛擬構造器可以讓你在不知道一個類的確切類型的時候,創建這個類的對象。在前面的例子中,`up`只是一個`Class`對象的引用,在編譯期并不知道這個引用會指向哪個類的`Class`對象。當你創建新實例時,會得到一個`Object`引用,但是這個引用指向的是`Toy`對象。當然,由于得到的是`Object`引用,目前你只能給它發送`Object`對象能夠接受的調用。而如果你想請求具體對象才有的調用,你就得先獲取該對象更多的類型信息,并執行某種轉型。另外,使用`newInstance()`來創建的類,必須帶有無參數的構造器。在本章稍后部分,你將會看到如何通過 Java 的反射 API,用任意的構造器來動態地創建類的對象。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看