Class對象的創建與使用

来源:http://www.cnblogs.com/guohaien/archive/2017/08/14/7350331.html
-Advertisement-
Play Games

類與Class對象 類是程式的一部分,每個類都有一個Class對象,即每當編寫並且編譯一個新類的時候就會產生一個Class對象。當程式創建第一個對類的靜態成員的引用的時候,會將該類動態載入到JVM中,這個說明瞭類的構造起器也是一個靜態方法,即使在構造器之前並沒有使用static關鍵字。所以java程 ...


類與Class對象


類是程式的一部分,每個類都有一個Class對象,即每當編寫並且編譯一個新類的時候就會產生一個Class對象。當程式創建第一個對類的靜態成員的引用的時候,會將該類動態載入到JVM中,這個說明瞭類的構造起器也是一個靜態方法,即使在構造器之前並沒有使用static關鍵字。所以java程式在運行之前並沒有被完全載入,各個類只在需要的時候才將該類的Class對象載入記憶體,該Class對象被用來創建這個類的所有對象。通過下麵的代碼可以證明以上內容:

class Demo1 {
    static int i;
    static {
        System.out.println("loading Demo1");
    }
}
class Demo2 {
    static {
        System.out.println("loading Demo2");
    }
}
class Demo3 {
    static {
        System.out.println("loading Demo3");
    }
}
class TestDemo {
    public static void main(String[] args) {
        int i = Demo1.i;
        try {
            Class.forName("Demo2");
        } catch (ClassNotFoundException e) {
            System.out.println("couldn't find Demo2");
        }
        new Demo3();
    }
    /* output
     * loading Demo1
     * loading Demo2
     * loading Demo3
     */
}

其中static{}是靜態塊,在類被載入的時候會執行,第一個輸出是我們是用Demo1中的靜態成員i而載入了Demo1類,而第二個輸出我們調用了Class類的一個靜態方法forName,參數是一個類的名稱,返回的是該類名的類的Class對象,該方法還有一個作用就是若該類未被載入則載入它,最後使用了new關鍵字創建對象即調用了類的構造器,也對類進行了載入輸出了第三行

Class對象的創建


若我們想要在運行的時候獲取某個類的類型信息,就必須先獲得該類的Class對象。得到Class對象的方法主要有三種

  • Class.forName:Class類的一個靜態方法,傳入類的全名
  • 對象.getClass:根類Object的方法,返回該對象的類的Class對象
  • 類名.class:這種方法又稱為類字面常量,該方法不僅簡單,而且更安全,因為可以在編譯時就會受到檢查
    class Demo1 {
        static final int i1 = 47;
        static final int i2 = (int)(Math.random() * 10000);
        static {
            System.out.println("loading Demo1");
        }
    }
    class TestDemo {
        public static void main(String[] args) {
            Class<Demo1> demo1Class = Demo1.class;
            System.out.println("after Demo1.class");
            int i = Demo1.i1;
            System.out.println("after Demo1.i1");
            i = Demo1.i2;
            System.out.println("after Demo1.i2");
        }
        /* output
         * after Demo1.class
         * after Demo1.i1
         * loading Demo1
         * after Demo1.i2
         */
    }

    從以上的代碼中你會發現,通過使用類名.class的方法並沒有對類進行載入,因為通過這種方法創建Class對象不會自動地初始化該Class對象。當我們使用某個類的時候實際上可以分為三個步驟:1)載入,這是由類載入器執行的,即通過查找到的位元組碼創建一個Class對象。2)鏈接,驗證類中的位元組碼,為靜態域分配存儲空間,解析對其它類的引用。3)初始化,若該類有父類,則對其初始化,執行靜態初始化器和靜態塊。從這三個步驟中看出只有對Class對象進行初始化才執行靜態塊。接著我們又調用了Demo1的i1也為執行靜態塊,因為被static final修飾的是一個編譯期常量,當我們讀取這個值的時候並不需要的類進行初始化,但並不是說訪問的域被static final修飾時就不會對類進行初始化,從調用i2就可以看出,因為i2的值不是一個編譯器的常量。

Class對象的使用


Class對象中提供了大量的方法來讓我們獲取類中的屬性與方法,而且我們也可以通過Class對象來創建類的實例與修改屬性值和執行方法,以下為Class對象中比較常用的方法:

  • getFields:獲取public修飾的所有屬性,返回一個Field數組(包括父類的)
  • getDeclaredFields:獲取所有屬性,返回一個Field數組
  • getField:傳入一個參數(屬性名),獲取單個屬性,返回一個Field對象,只能獲取public修飾的
  • getDeclaredField:傳入一個參數(屬性名),獲取單個屬性,返回一個Field對象
    public class Demo {
        public int field1;
        private String field2;
        public void method1(Integer arg0) {
            System.out.println("執行method1");
        }
        private String method1() { return null;}
    }
    class TestDemo {
        public static void main(String[] args) throws Exception {
            Class<Demo> demoClass = Demo.class;
            Field[] fields1 = demoClass.getFields();
            Field[] fields2 = demoClass.getDeclaredFields();
            System.out.println(Arrays.toString(fields1));
            System.out.println(Arrays.toString(fields2));
            Field field1 = demoClass.getField("field1");
            // Field field2 = demoClass.getField("field2"); // 運行時拋異常
            Field field3 = demoClass.getDeclaredField("field2");
            System.out.println(field1);
            System.out.println(field3);
        }
        /* output
         * [public int Demo.field1]
         * [public int Demo.field1, private java.lang.String Demo.field2]
         * public int Demo.field1
         * private java.lang.String Demo.field2
         */
    }
  • getMethods:獲取所有的public修飾的方法,包括父類的,返回Method數組
  • getDeclaredMethods:獲取所有的返回,不包括父類,返回Method數組
  • getMethod:傳入一個參數(方法名),返回一個Method對象,只能獲取到public修飾的
  • getDeclared:傳入一個參數(方法名),返回一個Method對象
    class TestDemo {
        public static void main(String[] args) throws Exception {
            Class<Demo> demoClass = Demo.class; //上段代碼的Demo類
            Method[] methods1 = demoClass.getMethods();
            Method[] methods2 = demoClass.getDeclaredMethods();
            System.out.println(Arrays.toString(methods1));
            System.out.println(Arrays.toString(methods2));
            Method method1 = demoClass.getMethod("method1", new Class[]{Integer.class});
    //        Method method2 = demoClass.getMethod("method2");
            Method method3 = demoClass.getDeclaredMethod("method2");
            System.out.println(method1);
            System.out.println(method3);
        }
        /**
         * [public void Demo.method1(java.lang.Integer), public final void java.lang.Object.wait() throws java.lang.InterruptedException,...
         * [public void Demo.method1(java.lang.Integer), private java.lang.String Demo.method2()]
         * public void Demo.method1(java.lang.Integer)
         * private java.lang.String Demo.method2()
         */
    }
  • newInstance:創建該類型的一個實例
    class TestDemo {
        public static void main(String[] args) throws Exception {
            Class<Demo> demoClass = Demo.class;
            Demo demo = demoClass.newInstance();
            Field field2 = demoClass.getDeclaredField("field2");
            field2.setAccessible(true);
            field2.set(demo, "setField2");
            System.out.println(field2.get(demo));
            Method method1 = demoClass.getMethod("method1", Integer.class);
            method1.invoke(demo, new Object[]{11});
        }
        /**
         * setField2
         * 執行method1
         */
    }

    以上代碼中可以看出創建類的一個實例並不只能通過new關鍵字來創建,而且上述還使用了Field對象的方法,可以獲取一個實例中的屬性值。而且你會發現通過Field對象的方法甚至可以改變一個實例的私有的屬性值。若想改變私有屬性值必須調用setAccessible方法並傳入true(預設為false)。後面又使用了Method對象的方法,可以執行實例的方法,傳入參數分別為實例與方法的參數數組,若調用Method的setAccessible方法並傳入true,可以執行實例的私有方法。到這你可能會想有沒有什麼辦法可以阻止我們通過反射(即Field和Method方法)調用那些私有的屬性,可以試著將該屬性值放在一個私有內部類中或則放在匿名類中,最後將會發現這都不法阻止反射的調用。但我們可以通過將一個屬性用final來修飾,即使可以執行修改操作但並不會真正的改變屬性值。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 題目背景 縮點+DP 題目描述 給定一個n個點m條邊有向圖,每個點有一個權值,求一條路徑,使路徑經過的點權值之和最大。你只需要求出這個權值和。 允許多次經過一條邊或者一個點,但是,重覆經過的點,權值只計算一次。 輸入輸出格式 輸入格式: 第一行,n,m 第二行,n個整數,依次代表點權 第三至m+2行 ...
  • 逆向工程1.什麼是逆向工程mybaits需要程式員自己編寫sql語句,mybatis官方提供逆向工程 可以針對單表自動生成mybatis執行所需要的代碼(mapper.java,mapper.xml、po..)企業實際開發中,常用的逆向工程方式:由於資料庫的表生成java代碼。2.下載逆向工程myb ...
  • 巨集觀上: 1.技術廣度方面至少要精通多門開源技術吧,研究過struts\spring等的源碼。2.項目經驗方面從頭到尾跟過幾個大項目,頭是指需求階段,包括需求調研。尾是指上線交付之後,包括維護階段。3.架構經驗方面有過分散式系統的架構和開發經驗。對於跨系統的結構優化,數據存儲的性能指標等有豐富經驗。 ...
  • Java的三大版本是什麼?它們有什麼功能?Java另一個與三有關的三大環境是什麼?它們是什麼關係?併列還是包含?Oracle公司官網UI更新後,Java又怎麼下載?小星星帶你一探究竟。 ...
  • 本篇文章講解的是在centos7.3下配置 Apache2.4 + MySQL5.7 + PHP7.1.8 一.Apache 1.查看httpd包是否可用yum list | grep httpd 2.安裝Apacheyum install httpd 3.配置servernamevi /etc/h ...
  • 上一章節,我們講解了通過mybatis的懶載入來提高查詢效率,那麼除了懶載入,還有什麼方法能提高查詢效率呢?這就是我們本章講的緩存。 mybatis 為我們提供了一級緩存和二級緩存,可以通過下圖來理解: ①、一級緩存是SqlSession級別的緩存。在操作資料庫時需要構造sqlSession對象,在 ...
  • 上一篇我們介紹了JVM的基本運行流程以及記憶體結構,對JVM有了初步的認識,這篇文章我們將根據JVM的記憶體模型探索java當中變數的可見性以及不同的java指令在併發時可能發生的指令重排序的情況。 記憶體模型 首先我們思考一下一個java線程要向另外一個線程進行通信,應該怎麼做,我們再把需求明確一點,一 ...
  • 目錄 · 初步認識 · Java里程碑(關鍵部分) · 理解虛擬機 · Java虛擬機種類 · Java語言規範 · Java虛擬機規範 · 基本結構 · Java堆(Heap) · Java棧(Stacks) · 方法區(Method Area) · 直接記憶體(Direct Memory) · 本 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...