【Java面試指北】反射(1) 初識反射

来源:https://www.cnblogs.com/shuofxz/archive/2022/12/18/16991309.html
-Advertisement-
Play Games

如果你被問到:什麼是反射?為什麼需要反射、以及反射的應用?你會如何回答呢? 本篇會帶大家初識反射,瞭解反射概念和基本應用。反射的原理以及深入源碼的探究將會在後面幾篇介紹。 ...


如果你被問到:什麼是反射?為什麼需要反射、以及反射的應用?你會如何回答呢?
本篇會帶大家初識反射,瞭解反射概念和基本應用。反射的原理以及深入源碼的探究將會在後面幾篇介紹。

一、什麼是反射?

要理解什麼是反射,我們先看看什麼是「正射」,一個常見的獲取Student的正射如下:

Student student = new Student();

通常 我們都是直接聲明,或者通過 new Student() 直接獲取一個 Student 類,然後再使用。而一個反射的例子如下:

// 這裡的“com.demo.Student”是需要反射的類的全限定名(包名+類名)
Class clz = Class.forName("com.demo.Student")	
Object stu = clz.newInstance();

先獲取實例的Class類,然後再通過其Class類生成一個Student的Instance。以上兩種方式(new Student和clz.newInstance)是效果是等價的,都是獲取到了一個Student 的實例。

那麼什麼是反射呢?反射是Java中的一個重要的特性,使用反射可以在運行時動態生成對象、獲取對象屬性以及調用對象方法。
Oracle 官方對反射的解釋是:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

反射的核心是 JVM 在運行時才動態載入類或調用方法/訪問屬性,它不需要事先(寫代碼的時候或編譯期)知道運行對象是誰。

反射的問題:
這裡先簡單提一下:反射相當於一系列解釋操作,通知 JVM 要做的事情,性能比直接的 Java 代碼要慢很多。

二、為什麼需要反射?

舉一個直觀的例子(僅為了說明其中一種用法):
如果我讓你寫一個根據運行時輸入的名字進行列印輸出,你會寫出類似下麵的代碼:

public void sayHello(String name) {
    // 在運行前根本不知道 name 是什麼,只有在運行時 name 才會被確認並列印出來
    System.out.println("hello, " + name);
}

那麼同樣的,在寫代碼時可能也不知道要用什麼類,運行時才知道。比如載入資料庫驅動的時候,你可以直接 new 出來具體的驅動類,但要是換了資料庫呢,還要修改源碼重新打包更新麽?

new com.mysql.jdbc.Driver();

那你可能會說,我多寫幾個 if else 不就行了,類似下麵這樣:

if ( xxx == "mysql") {
    new com.mysql.jdbc.Driver();
else if ( xxx == "redis" ) {
    new com.redis.jdbc.Driver();
else if ( ... ){
}

這樣的問題是,在編譯期就要湊齊所有的 jdbc 連接庫,甭管用不用這些都會被載入到記憶體中,資料庫類型多了會有極大的浪費。
那麼這種情況,就可以用反射來解決,在運行時才去動態的載入對應類。你也可以在配置文件中指明要使用哪種資料庫類,連接不同的資料庫都可以使用這一份程式。

// 反射的方式動態載入類
Class.forName("com.mysql.jdbc.Driver");

三、反射的基本使用

下麵介紹通過反射都能做什麼:

一)獲得 Class 對象

// 1 使用 Class 類的 forName 靜態方法
 Class.forName(driver);

// 2 直接獲取某一個對象的 class
Class<?> cl = int.class;

// 3 調用某個對象的 getClass() 方法
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();

二)判斷是否為某個類的實例

public static void displayObjectClass(Object o) {
    if (o instanceof Vector)
   		System.out.println("對象是 java.util.Vector 類的實例");
  	else if (o instanceof ArrayList)
   		System.out.println("對象是 java.util.ArrayList 類的實例");
   	else
   		System.out.println("對象是 " + o.getClass() + " 類的實例");
}

三)創建實例

Class<?> c = String.class;
Object str = c.newInstance();

四)獲取方法

getDeclaredMethods() 方法返回類或介面聲明的所有方法,包括公共、保護、預設(包)訪問和私有方法,但不包括繼承的方法。
getMethods() 方法返回某個類的所有公用(public)方法,包括其繼承類的公用方法。
getMethod() 方法返回一個特定的方法,其中第一個參數為方法名稱,後面的參數為方法的參數對應Class的對象。

public class ReflectDemo {
	public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
	    Class<?> c = MyClass.class;

	    Method[] methods = c.getMethods();
	    Method[] declaredMethods = c.getDeclaredMethods();
	    Method method = c.getMethod("add", int.class, int.class);

        System.out.println("getMethods獲取的方法:");
        for(Method m:methods)
            System.out.println(m);

        System.out.println("getDeclaredMethods獲取的方法:");
        for(Method m:declaredMethods)
            System.out.println(m);
    }
}

class MyClass {
    public int add(int a, int b) {
        return a + b;
    }
    public int sub(int a, int b) {
        return a - b;
    }
}

// 輸出
/*
getMethods獲取的方法:
public int com.shuofxz.basic.ReflectDemo$MyClass.add(int,int)
public int com.shuofxz.basic.ReflectDemo$MyClass.sub(int,int)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

getDeclaredMethods獲取的方法:
public int com.shuofxz.basic.ReflectDemo$MyClass.add(int,int)
public int com.shuofxz.basic.ReflectDemo$MyClass.sub(int,int)
*/

五)調用方法

當我們從類中獲取了一個方法後,我們就可以用 invoke() 來調用這個方法。

public class ReflectDemo {
	public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> mc = MyClass.class;
        Object obj = mc.newInstance();
        //獲取methodClass類的add方法
        Method method = mc.getMethod("add", int.class, int.class);
        //調用method對應的方法 => add(1,4)
        Object result = method.invoke(obj, 1, 4);
        System.out.println(result);
    }
}

六)獲取構造器、類的成員變數(欄位)信息

  • 通過 Class 類的 getConstructor 方法得到 Constructor 類的一個實例
  • getFiled:訪問公有的成員變數
  • getDeclaredField:所有已聲明的成員變數,但不能得到其父類的成員變數

四、小結

本篇文章初步介紹了反射機制。讓大家瞭解了反射是什麼,為什麼會有反射這個功能,以及一些基本使用方式。後續文章將會反射的機制和原理做進一步的講解。


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

-Advertisement-
Play Games
更多相關文章
  • 一、前言 最近學習pyqt5中文教程時,最後一個例子製作了一個俄羅斯方塊小游戲,由於解釋的不是很清楚,所以源碼有點看不懂,查找網上資料後,大概弄懂了源碼的原理。 二、繪製主視窗 將主視窗居中,且設置了一個狀態欄來顯示三種信息:消除的行數,游戲暫停狀態或者游戲結束狀態。 class Tetris(QM ...
  • JZ46 把數字翻譯成字元串 描述 有一種將字母編碼成數字的方式:'a'->1, 'b->2', ... , 'z->26'。 現在給一串數字,返回有多少種可能的解碼結果 示例1 輸入: "12" 返回值:2 說明: 2種可能的解碼結果(”ab” 或”l”) 思路 思路: 對於普通數組1-9,解碼方 ...
  • 1、設計想法 原理與之前的串口發送模塊一樣,1位的數據位和8位的數據位再加上1位的停止位。唯一不同的是在接收的時候要考慮到有干擾的情況下,為了避免干擾,我們對每位數據進行多次採樣,按出現概率大的值為該數據位的值。 如果按照通常想法在每bits位中間取值的話,bit3位出現圖中的干擾很有可能會讀出錯誤 ...
  • win11特有的快捷鍵 win鍵就是圖案是windows圖標的那個按鍵 | 作用 | 快捷鍵 | | | | | 打開快速設置,win11是展開音量,wifi,藍牙的設置項,win10也可以用 | win + a | | 打開通知中心和日曆,win10無 | win + n | | 打開投屏,win ...
  • RDP,Remote Desktop Protocol,遠程桌面協議,是一個多通道(mutil-channel)的協議,讓用戶(客戶端或稱“本地電腦”)連上提供微軟終端機服務的電腦(伺服器端或稱“遠程電腦”)。大部分的Windows、Linux、FreeBSD、Mac OS X都有相應的客戶端。服務... ...
  • 1. 判斷本地是否已經安裝MySQL ① 在運行界面輸入services.msc進入服務界面,查看是否有MySQL服務 ② 進入任務管理器,點擊服務看是否有MySQL服務 2. 安裝MySQL(壓縮包版) 1. 下載MySQL社區伺服器(ZIP): MySQL zip下載 點擊No thanks,j ...
  • 本文是 CSS Houdini 之 CSS Painting API 系列第四篇。 現代 CSS 之高階圖片漸隱消失術 現代 CSS 高階技巧,像 Canvas 一樣自由繪圖構建樣式! 現代 CSS 高階技巧,完美的波浪進度條效果! 在上三篇中,我們詳細介紹了 CSS Painting API 是如 ...
  • 因為團隊內部開啟了一個持續的前端代碼質量改進計劃,其中一個專項就是TS類型覆蓋率,期間用到了type-coverage這個倉庫,所以借這篇文章分享一下這個工具,並順便從源碼閱讀的角度來分析一下該工具的源碼,我自己fork了一個倉庫,完成了中文版本的ReadMe文件並對核心代碼添加了關鍵註釋,需要的同 ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...