面試:說說Java反射中獲取Class對象三種方式的區別?


咱們都知道Java的反射機制中有三種獲取類對象的方式,那麼這幾種方式的區別究竟是什麼呢?
一、new Object().getClass 二、Object.class 三、Class.forName("java.util.String")

測試場景一

由於類對象與類的加載息息相關,因此爲了展現區別,咱們先加入動態的和靜態的代碼塊。
public class CalBean {
            static {
                System.out.println("靜態的代碼塊。");
            }
            {
                System.out.println("動態的代碼塊。");
            }
            public CalBean() {
                System.out.println("構造方法");
            }
            private Integer num1;
            private Integer num2;
            public Integer getNum1() {
                return num1;
            }
            public void setNum1(Integer num1) {
                this.num1 = num1;
            }
            public Integer getNum2() {
                return num2;
            }
            public void setNum2(Integer num2) {
                this.num2 = num2;
            }
        }

建立一個測試單元:java

@SuppressWarnings("unused")
    @org.junit.Test
    public void test() {
         Class<CalBean> cla1 = CalBean.class;
         System.out.println("===================================");
         try {
              Class<?> cla2 =Class.forName("com.zking.entity.CalBean");
         } catch (ClassNotFoundException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
         }
         System.out.println("===================================");
         Class<? extends CalBean> cla3 = new CalBean().getClass();
    }

控制檯結果:數據結構


23.png
結果能夠看到,類名.class的方式並未執行該類的代碼塊或者是代碼。
而forName的方式執行了該類的靜態代碼塊。
實例對象.getClass()的方式打印全部內容的緣由顯而易見,由於要先實例化。測試

測試場景二

咱們將.class方式與實例對象.getClass()組合測試:this

@SuppressWarnings("unused")
        @org.junit.Test
        public void test() {
             Class<CalBean> cla1 = CalBean.class;
             System.out.println("===================================");
             Class<? extends CalBean> cla3 = new CalBean().getClass();
        }

控制檯結果:spa


123.png
能夠看到.class方式仍然沒有輸出,實例對象.getClass()方式將全部的內容打印。code

測試場景三

這裏,咱們測試一下三種方式獲取到的類對象是否相等:對象

@SuppressWarnings("unused")
    @org.junit.Test
    public void test() {
        try {
             Class<CalBean> cla1 = CalBean.class;
             System.out.println("===================================");
             Class<?> cla2 =Class.forName("com.zking.entity.CalBean");
             System.out.println("===================================");
             Class<? extends CalBean> cla3 = new CalBean().getClass();
             
             System.out.println(cla1 == cla2);
             System.out.println(cla2 == cla3);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

等於號判斷引用地址:blog


qe.png

三種方式獲取到的是同一個對象,爲何呢?
這就涉及到類的加載過程,咱們知道類加載過程分:加載階段、鏈接階段和初始化階段。
類的加載階段是將class文件中的二進制數據讀取到內存中,而後將該字節流所表明的靜態存儲結構轉化爲方法區中運行時的數據結構,而且在堆內存中生成一個該類的java.lang.class對象,做爲方法區數據結構的入口。
類加載階段的最終產物是堆內存中的class對象,對於同一個Classloader對象,無論某個類被加載多少次,對應堆內存中的class對象始終只有一個。
也就是說不管經過哪一種形式來獲取Class對象,得到的都是堆內存中對應的Class對象。

總結三種方式

  1. 類名.class:JVM將使用類裝載器,將類裝入內存(前提是:類尚未裝入內存),不作類的初始化工做,返回Class的對象。
  2. Class.forName("類名字符串"):裝入類,並作類的靜態初始化,返回Class的對象。
  3. 實例對象.getClass():對類進行靜態初始化、非靜態初始化;返回引用運行時真正所指的對象所屬的類的Class的對象。
相關文章
相關標籤/搜索