天天看點

Java之Class.forName方法詳解

  • 一、前言
  • 二、案例
  • 三、詳解

一、前言:

在說明Class類的靜态方法forName()之前,先清楚有關Class類的幾個概念:

1、 Class類封裝了類或接口的運作時狀态

Java程式在運作時,Java運作時系統一直對所有的對象進行所謂的運作時類型辨別,這些标示紀錄了每個對象所屬的類。

虛拟機通常使用運作時類型資訊選擇正确方法去執行,用來儲存這些類型資訊的類是Class類。

2、Class類型的對象,是加載類時自動建立的

Class 沒有公共構造方法。Class 對象是在加載類時,由Java 虛拟機以及通過調用類加載器中的 defineClass 方法自動構造的,是以不能顯式地聲明一個Class對象。

3、虛拟機為每種類型管理一個獨一無二的Class對象

每個類(型)都有一個Class對象。

運作程式時,Java虛拟機(JVM)首先檢查所要加載的類對應的Class對象是否已經加載。如果沒有加載,JVM就會根據類名查找.class檔案,并将其Class對象載入。

1.基本的 Java 類型(boolean、byte、char、short、int、long、float 和 double)和關鍵字 void 也都對應一個 Class 對象。

2.每個數組屬于被映射為 Class 對象的一個類,所有具有相同元素類型和維數的數組都共享該 Class 對象。

3.一般某個類的Class對象被載入記憶體,它就用來建立這個類的所有對象。

以上說法檢視Class源碼會發現,

Book.class.getName()

最終調用的:

private transient String name;
public String getName() {
    String name = this.name;
    if (name == null)
        this.name = name = getName0();
    return name;
}
           

此時Book也是一個獨一無二的Class對象,即對象中的對象。

二、案例:

Book.java類

package com.junit.demo;

public class Book {
    private static final String defName = "《程式猿植發》";

    static {
        System.out.println("我是靜态代碼塊,輸出: " + defName);
    }

    //列印生産日期:
    public static String printProduceDate(String name) {
        return "我是靜态方法printProduceDate,輸出: " + name + ", produce is:" + System.currentTimeMillis();
    }

    private String name;

    public Book() {
        System.out.println("我是Book聲明的構造方法!");
        name = defName;
    }

    public String toString(String msg) {
        return name + msg;
    }
}
           

執行方法:

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
    //1-将指定類加載到JVM中(ClassNotFoundException)
    Class aClass = Class.forName("com.junit.demo.Book");
    System.out.println(aClass);
    //2.1-通路靜态方法:NoSuchMethodException,InvocationTargetException
    Method method = aClass.getMethod("printProduceDate", String.class);
    String result = (String) method.invoke(aClass, "《程式猿的頸椎自傳》");
    System.out.println(result);
    System.out.println("---------------------------\n");
    //2.2-初始化對象:
    Book obj = (Book) aClass.newInstance();
    System.out.println("得到對象後通路get方法:" + obj.toString(""));
    System.out.println("---------------------------\n");
    //2.3-初始化對象後通路方法:
    Method method3 = aClass.getMethod("toString", String.class);
    String result3 = (String) method3.invoke(aClass.newInstance()/*obj*/, "這本書是我的夥伴!");
    System.out.println(result3);
    System.out.println("---------------------------\n");

    System.out.println(Book.class.getName());
}
           

輸出:

我是靜态代碼塊,輸出: 《程式猿植發》
class com.junit.demo.Book
我是靜态方法printProduceDate,輸出: 《程式猿的頸椎自傳》, produce is:1626682894095
---------------------------

我是Book聲明的構造方法!
得到對象後通路get方法:《程式猿植發》
---------------------------

我是Book聲明的構造方法!
《程式猿植發》這本書是我的夥伴!
---------------------------

com.junit.demo.Book
           

三、詳解:

1、通路靜态方法:

// 由Class擷取方法:第一個參數為方法名,第二個參數為方法的參數類型。
// 如add(int a,int b)則getMethod("add",int.class,int.class)。當然,也可以是Java對象。
Method method = aClass.getMethod("printProduceDate", String.class);
// 引用方法:(引用執行個體/調用靜态方法可為null,參數值/有多個用逗号隔開),參數值要和參數類型的數量比對!
String result = (String) method.invoke(aClass, "《程式猿的頸椎自傳》");
           

簡寫:

2、通路執行個體方法:

  • 重要:

    aClass.newInstance();

    ,執行個體化指定對象。

    和 new Book() 效果一樣。

//方法一:直接轉化執行個體化後的對象,直接調用方法
Book book= (Book) aClass.newInstance();
// book.setName('xxx'); or book.getName(); or more...

//方法二:使用invoke調用指定執行個體a的指定方法b
Method method3 = aClass.getMethod("toString", String.class);
//這裡的book可以是已執行個體化的對象,或者使用 aClass.newInstance() 傳入,詳見簡寫:
String result3 = (String) method3.invoke(book, "這本書是我的夥伴!");
           

簡寫:

值得注意的是,如果是類似于工具類可用于全部類通路的,可以使用一個執行個體化對象,而不需要每次都newInstance。

另外,方法一适用于需要映射的類是已知或少數時,反之需要統一按指定字元串反射調用方法的話,需使用方法二。

end.