Java 基礎 - 反射(1)

文章目錄

  • 引入
    • 類加載過程
    • 1. 通過 new 創建對象
    • 2. 通過反射創建對象
      • 2.1 觸發加載但不初始化
      • 2.2 按需觸發初始化
      • 2.3 選擇性初始化控制
  • 核心用法
  • 示例
    • 1. 通過無參構造函數創建實例對象
    • 2. 通過有參構造函數創建實例對象
    • 3. 反射通過私有構造函數創建對象, 破壞單例模式
    • 4. 通過反射獲得類的public屬性值, 演示getField與getDeclaredField兩者的區別

引入

當我們剛接觸java語言的時候, 我們最常寫的代碼應該就是初始化某個對象, 然后調用該 對象的方法。 如:

MyClass obj = new MyClass();
obj.doSth();

上面的這種用法的前提是, 我們在寫代碼的時候已經確定要去創建MyClass類的具體實例對象。
那如果我們想在代碼運行的時候才去指定具體對象的類(比如根據傳入的參數名稱確定創建的類名),普通的硬編碼方式將無法實現需求 ;反射登場了。
反射為什么可以實現呢, 這個就要先介紹一下類加載的過程了。

類加載過程

  1. 加載(Loading)
    JVM將類的字節碼文件(.class)加載到內存,創建Class對象。
  2. 鏈接(Linking)
  • 驗證:確保字節碼符合規范。
  • 準備:為靜態變量分配內存并賦予默認值(如int初始化為0)。
  • 解析:將符號引用轉換為直接引用。
  1. 初始化(Initialization)
    執行類的靜態代碼塊(static {})和靜態變量顯式賦值。

1. 通過 new 創建對象

new關鍵字會直接觸發類的完整加載、鏈接和初始化過程:

  1. 若類未加載:
    - 立即執行加載、鏈接,完成后強制觸發類的初始化(執行static代碼塊和初始化靜態變量)。
  2. 初始化完成后:調用構造函數創建對象。
    示例:
// 第一次使用類時觸發初始化
MyClass obj = new MyClass();

特點:

  • 類必須在編譯時已知(硬編碼依賴)。
  • 初始化在對象創建時必定發生。

2. 通過反射創建對象

通過反射(Class.newInstance()或Constructor.newInstance())創建對象時,允許分階段控制類的加載過程:

2.1 觸發加載但不初始化

使用ClassLoader.loadClass()可加載類但不初始化:

ClassLoader loader = MyClass.class.getClassLoader();
Class<?> clazz = loader.loadClass("MyClass"); // 僅加載和鏈接,不初始化

此時尚未執行靜態代碼塊或靜態變量顯式賦值。

2.2 按需觸發初始化

在首次需要初始化時才觸發(如反射調用newInstance()):

Object obj = clazz.newInstance(); // 觸發初始化 → 執行static代碼塊

2.3 選擇性初始化控制

通過Class.forName可指定是否初始化:

public class Main {public static void main(String[] args) throws Exception {// 反射示例:ClassLoader loader = MyClass.class.getClassLoader();// 加載類但不初始化(第三個參數為類加載器)System.out.println("加載類但不初始化1...");Class<?> clazz2 = Class.forName("com.test.galaxy.MyClass", false, loader);// 加載類但不初始化System.out.println("加載類但不初始化2...");Class<?> clazz = loader.loadClass("com.test.galaxy.MyClass"); // 無輸出// 觸發初始化前,類的靜態代碼塊仍未執行System.out.println("準備創建對象...");Object obj = clazz.newInstance(); // 輸出:靜態代碼塊執行!// 加載類同時觸發初始化System.out.println("加載類同時觸發初始化...");Class<?> clazz1 = Class.forName("com.test.galaxy.MyClass2");}
}
class MyClass {static {System.out.println("靜態代碼塊執行!"); // 初始化觸發}
}
class MyClass2 {static {System.out.println("靜態代碼塊2執行!"); // 初始化觸發}
}

特點:

  • 類的加載步驟可拆分(加載、鏈接、初始化分開觸發)。
  • 初始化在需要時才發生(如通過newInstance())。
  • 靈活支持運行時動態加載類(例如插件化架構)。

核心用法

反射允許程序在運行時動態獲取類的信息并操作類或對象。核心類是 Class,關鍵操作包括:

  • 動態創建對象(newInstance())
  • 調用方法(method.invoke())
  • 訪問/修改字段(field.get()/set())

示例

1. 通過無參構造函數創建實例對象

import java.lang.reflect.Constructor;
public class ReflectionExample1 {public static void main(String[] args) {try {// 1. 獲取Class對象(觸發類加載,可能初始化)Class<?> clazz = Class.forName("com.test.galaxy.User");// 2. 獲取無參構造方法(需處理異常)Constructor<?> constructor = clazz.getDeclaredConstructor();// 3. 調用newInstance()創建實例(無參數)Object instance = constructor.newInstance();System.out.println("實例創建成功:" + instance.getClass());} catch (Exception e) {e.printStackTrace();}}
}
class User {public User() {System.out.println("無參構造函數被調用!");}
}

關鍵說明

  • Class.forName():動態加載類,默認觸發初始化。
  • getDeclaredConstructor():傳入空參數類型列表表示獲取無參構造方法。
  • 私有構造方法處理:若構造函數是私有(private),需調用 constructor.setAccessible(true) 解除訪問限制。

2. 通過有參構造函數創建實例對象

import java.lang.reflect.Constructor;public class ReflectionExample2 {public static void main(String[] args) {try {// 1. 獲取Class對象(注意使用全限定類名)Class<?> clazz = Class.forName("com.test.galaxy.User2");// 2. 指定參數類型列表,獲取有參構造方法Class<?>[] paramTypes = {String.class, int.class}; // 參數類型順序嚴格匹配Constructor<?> constructor = clazz.getDeclaredConstructor(paramTypes);// 3. 傳遞參數值實例化對象Object[] initArgs = {"張三", 25}; // 參數值順序與類型列表一致Object instance = constructor.newInstance(initArgs);System.out.println("實例創建成功:" + instance.getClass());} catch (Exception e) {e.printStackTrace();}}
}
class User2 {private String name;private int age;public User2(String name, int age) {this.name = name;this.age = age;System.out.println("有參構造函數被調用!name=" + name + ", age=" + age);}
}

關鍵說明

  • 參數類型匹配:必須精確指定參數類型(如 int.class 不能寫作 Integer.class)。
  • 參數值順序:傳入的參數值順序需與聲明時一致。
  • 可變長參數處理:若構造方法參數為可變長度(如 String…),類型寫為 String[].class。

3. 反射通過私有構造函數創建對象, 破壞單例模式

import java.lang.reflect.Constructor;class Singleton {private static Singleton instance;private Singleton() {// 私有構造函數}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
public class ReflectionExample3 {public static void main(String[] args) {try {// 通過正常方式獲取單例對象Singleton instance1 = Singleton.getInstance();System.out.println("正常實例:" + instance1);// 方式 1:通過反射創建新實例(直接訪問構造函數)Class<Singleton> clazz = Singleton.class;Constructor<Singleton> constructor = clazz.getDeclaredConstructor();constructor.setAccessible(true); // 訪問私有構造函數Singleton instance2 = constructor.newInstance();// 方式 2:通過反射多次創建實例(動態控制)for (int i = 0; i < 3; i++) {Constructor<Singleton> ctor = clazz.getDeclaredConstructor();ctor.setAccessible(true);Singleton instance = ctor.newInstance();System.out.println("反射實例 " + (i+1) + ": " + instance);}// 驗證兩個實例是否相同System.out.println("instance1 == instance2 ? " + (instance1 == instance2));} catch (Exception e) {e.printStackTrace();}}
}

4. 通過反射獲得類的public屬性值, 演示getField與getDeclaredField兩者的區別

  1. getField() 的特點
    • 只能獲取 當前類及繼承鏈中聲明為 public 的屬性;
    • 無法獲取非 public 屬性;
    • 可以直接訪問繼承的父類 public 屬性。
  2. getDeclaredField() 的特點
    • 能獲取 當前類中聲明的所有屬性(包括 private/protected/public);
    • 無法獲取父類聲明的屬性;
    • 訪問非 public 屬性需通過 setAccessible(true)。
import java.lang.reflect.Field;class Parent {public String parentPublicField = "Parent-Public";private String parentPrivateField = "Parent-Private";
}
class Child extends Parent {public String childPublicField = "Child-Public";private String childPrivateField = "Child-Private";
}
public class ReflectionExample4 {public static void main(String[] args) {Child child = new Child();Class<?> clazz = Child.class;try {// ======================= 使用 getField() ========================// 1. 獲取子類的 public 屬性(成功)Field childPublicField = clazz.getField("childPublicField");System.out.println("[getField] 子類 public 屬性: " + childPublicField.get(child));// 2. 獲取父類的 public 屬性(成功)Field parentPublicField = clazz.getField("parentPublicField");System.out.println("[getField] 父類 public 屬性: " + parentPublicField.get(child));// 3. 嘗試獲取子類的 private 屬性(失敗,觸發異常)clazz.getField("childPrivateField");} catch (Exception e) {System.err.println("[getField 失敗] " + e.getClass().getSimpleName() + ": " + e.getMessage());}try {// ================== 使用 getDeclaredField() ======================// 1. 獲取子類的 public 屬性(成功)Field childPublicDeclaredField = clazz.getDeclaredField("childPublicField");System.out.println("[getDeclaredField] 子類 public 屬性: " + childPublicDeclaredField.get(child));// 2. 獲取子類的 private 屬性(需解除訪問限制)Field childPrivateDeclaredField = clazz.getDeclaredField("childPrivateField");childPrivateDeclaredField.setAccessible(true);  // 強制訪問私有屬性System.out.println("[getDeclaredField] 子類 private 屬性: " + childPrivateDeclaredField.get(child));// 3. 嘗試獲取父類的屬性(失敗,無論是否是 public)clazz.getDeclaredField("parentPublicField");} catch (Exception e) {System.err.println("[getDeclaredField 失敗] " + e.getClass().getSimpleName() + ": " + e.getMessage());}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/75289.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/75289.shtml
英文地址,請注明出處:http://en.pswp.cn/web/75289.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何在React中集成 PDF.js?構建支持打印下載的PDF閱讀器詳解

本文深入解析基于 React 和 PDF.js 構建 PDF 查看器的實現方案&#xff0c;該組件支持 PDF 渲染、圖片打印和下載功能&#xff0c;并包含完整的加載狀態與錯誤處理機制。 完整代碼在最后 一個PDF 文件&#xff1a; https://mozilla.github.io/pdf.js/web/compressed.tracemo…

數據結構與算法-動態規劃-線性動態規劃,0-1背包,多重背包,完全背包,有依賴的背包,分組背包,背包計數,背包路徑

動態規劃原理 動態規劃這玩意兒&#xff0c;就好比是在拓撲圖上玩跳格子游戲。在圖論中&#xff0c;咱們是從特定的節點跳到其他節點&#xff1b;而在動態規劃里呢&#xff0c;我們是從一個狀態 “嗖” 地轉移到另一個狀態。狀態一般用數組來表示&#xff0c;就像 f [i][j]&am…

解決文件夾解壓中文字符產生亂碼的問題

太tm智能了&#xff0c;本來還想看看解壓工具在哪里修改&#xff0c;智能的識別到亂碼了。點贊 看到那個地球了嗎&#xff0c;點擊那個球&#xff0c;這個修改不是侵略性的&#xff0c;不會修改壓縮文件本身所以需要在當前頁面解壓 參考 https://blog.csdn.net/QCSYSZQ/artic…

C++與C的區別

目錄 前言 一、從字面上看 二、從編程思想上看 三、C 和 C++ 都有各自適合的領域和特性 四、劃重點 前言 本文主要對 C 和 C++ 兩種編程語言進行對比區分,便于大家理解 一、從字面上看 1.首先:兩者第一個字符完全一致 說明:C++ 完全兼容 C ,凡是合法的 C 程序在 C…

水利水電安全員ABC適合哪些人考?

水利水電安全員證是水利工程建設領域的重要職業資格證書&#xff0c;主要涉及水利水電工程施工安全管理、風險防控和應急處理等工作。那么&#xff0c;哪些人適合考取&#xff1f; 哪些人適合考水利水電安全員&#xff1f; 1. 水利水電工程從業人員 ? 施工管理人員&#xf…

Linux中用gdb查看coredump文件

查看dump的命令&#xff1a; gdb 可執行文件 dump文件路徑查看函數調用棧 (gdb)bt查看反匯編代碼 (gdb)disassemble查看寄存器的值 (gdb)info all-registers如果通過上述簡單命令無法排查&#xff0c;還是通過-g參數編譯帶符號表的可執行文件&#xff0c;再用gdb查看

【前端】【React】useCallback的作用與使用場景總結

一、useCallback 的作用與使用場景總結 useCallback 是 React 提供的一個 Hook&#xff0c;用于緩存函數的引用&#xff0c;避免因為組件重新渲染而導致函數地址發生變化。它返回一個記憶&#xff08;memoized&#xff09;后的回調函數&#xff0c;只有當依賴項發生變化時才會…

藍橋杯備賽學習筆記:高頻考點與真題預測(C++/Java/python版)

2025藍橋杯備賽學習筆記 ——高頻考點與真題預測 一、考察趨勢分析 通過對第13-15屆藍橋杯真題的分析&#xff0c;可以發現題目主要圍繞基礎算法、數據結構、數學問題、字符串處理、編程語言基礎展開&#xff0c;且近年逐漸增加動態規劃、圖論、貪心算法等較難題目。 1. 基…

20250410在榮品的PRO-RK3566開發板使用Rockchip原廠的buildroot系統時自動掛載eth0【直接編譯進IMG】

【暫時沒有找到第一次編譯就可以修改的地方&#xff01;&#xff01;&#xff01;&#xff01;】 rootrootrootroot-X99-Turbo:~/RK3566_RK3568_Linux5.10_V1.2.0$ find . -name interfaces 【完整編譯之后&#xff0c;基本確認修改這里有效。】 ./buildroot/output/rockchip_r…

c11新特性,繼承構造函數

#include <iostream> #include <string>class Person { public:std::string name;int age;// 主構造函數Person(const std::string& name, int age) : name(name), age(age) {std::cout << "Person created with name: " << name <&l…

【TS學習】(24)什么是裝飾器

在 TypeScript 中&#xff0c;裝飾器&#xff08;Decorators&#xff09; 是一種特殊的聲明&#xff0c;用于為類、類成員&#xff08;屬性、方法、訪問器&#xff09;、方法參數或整個類添加元數據或修改其行為。裝飾器是 JavaScript 和 TypeScript 的實驗性特性&#xff0c;廣…

datagrip如何連接數據庫

datagrip連接數據庫的步驟 2025版本 想要鏈接數據庫是需要一個jar包的&#xff0c;所以將上面進行刪除之后&#xff0c;需要下載一個jar包 那么這個時候需要鏈接上傳一個mysql鏈接的jar包 選擇核心驅動類 上述操作完成之后&#xff0c;然后點擊apply再點擊ok即可 如下圖說明my…

菊風RTC 2.0 開發者文檔正式發布,解鎖音視頻新體驗!

重磅發布&#xff01; 開發者們&#xff0c;菊風實時音視頻2.0文檔已正式發布上線&#xff0c;為您提供更清晰、更高效的開發支持&#xff01;讓菊風實時音視頻2.0為您的音視頻應用加速~ 菊風實時音視頻2.0聚焦性能升級、體驗升級、錄制服務升級&#xff0c;助力視頻通話、語…

輕量級碎片化筆記memos本地NAS部署與跨平臺跨網絡同步筆記實戰

文章目錄 前言1. 使用Docker部署memos2. 注冊賬號與簡單操作演示3. 安裝cpolar內網穿透4. 創建公網地址5. 創建固定公網地址 推薦 ? 前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。 點擊跳轉到網站 前言…

【Vue #2】腳手架 指令

一、腳手架 腳手架&#xff1a;一個保證各項工作順利開展的平臺&#xff0c;方便我們 拿來就用&#xff0c;零配置 1. Vue 代碼開發方式 相比直接 script 引入 vue 源碼&#xff0c;有沒有更好的方式編寫vue代碼呢? ① 傳統開發模式&#xff1a; 基于html文件開發Vue&…

ArkTS語言入門之接口、泛型、空安全、特殊運算符等

前言 臭寶們&#xff0c;今天我們來學習ArkTS中最后的一些內容。 實現接口 包含implements子句的類必須實現列出的接口中定義的所有方法&#xff0c;但使用默認實現定義的方法除外。 interface DateInterface {now(): string; } class MyDate implements DateInterface {no…

Maven超級詳細安裝部署

1.到底什么是Maven&#xff1f;搞清楚這個 Maven 是一個項目管理工具&#xff0c;主要用于 Java 項目的構建、依賴管理和文檔生成。 它基于項目對象模型&#xff08;POM&#xff09;&#xff0c;通過 pom.xml 文件定義項目的配置。 &#xff08;簡單說破&#xff1a;就是工程…

高并發內存池(三):PageCache(頁緩存)的實現

前言&#xff1a; 在前兩期內容中&#xff0c;我們深入探討了內存管理機制中在 ThreadCache 和 CentralCache兩個層級進行內存申請的具體實現。這兩層緩存作為高效的內存分配策略&#xff0c;能夠快速響應線程的內存需求&#xff0c;減少鎖競爭&#xff0c;提升程序性能。 本期…

機器學習 | 強化學習方法分類匯總 | 概念向

文章目錄 ??Model-Free RL vs Model-Based RL??核心定義??核心區別??Policy-Based RL vs Value-Based RL??核心定義?? 核心區別??Monte-Carlo update vs Temporal-Difference update??核心定義??核心區別??On-Policy vs Off-Policy??核心定義??核心區別…

GSO-YOLO:基于全局穩定性優化的建筑工地目標檢測算法解析

論文地址:https://arxiv.org/pdf/2407.00906 1. 論文概述 《GSO-YOLO: Global Stability Optimization YOLO for Construction Site Detection》提出了一種針對建筑工地復雜場景優化的目標檢測模型。通過融合全局優化模塊(GOM)?、穩定捕捉模塊(SCM)?和創新的AIoU損失函…