java反射筆記

来源:https://www.cnblogs.com/hhmm99/archive/2018/08/28/9551514.html
-Advertisement-
Play Games

反射(reflect) 1. Class對象 1.1 什麼是Class對象 當JVM載入某個class文件的時候,會自動創建一個唯一的Class對象(註意:由同一個類載入器載入的class文件),這個Class對象包含了整個class類的信息(例如:類的名稱、訪問修飾符、欄位、欄位描述、方法等等一切 ...


反射(reflect)

1. Class對象

1.1 什麼是Class對象

當JVM載入某個class文件的時候,會自動創建一個唯一的Class對象(註意:由同一個類載入器載入的class文件),這個Class對象包含了整個class類的信息(例如:類的名稱、訪問修飾符、欄位、欄位描述、方法等等一切信息)。當使用new關鍵字創建這個類的實例時,jvm都會使用這個Class對象來創建類的實例。

1.1 獲取Class對象方式

方式說明
Test.class 通過類名.class直接獲取
t1.getClass() 通過對象的getClass()方法獲取
Class.forName("...") 使用Class類的forName靜態方法獲取,參數給的是完整類名(包名+類名)

2. 反射

2.1 什麼是反射

所謂的反射,是在程式運行時動態獲得任何一個Class對象的成員信息(類信息、欄位、構造方法、成員方法等),並且能在運行時依據某個Class對象創建當前類的實例。

2.2 Class常用API

方法說明
newInstance() 創建類實例
getName() 獲取類的完整類名
getSimpleName() 獲取類的簡單類名
getPackage() 獲取類對應的包信息
getSuperclass() 獲取父類的Class對象
getInterfaces() 獲取實現的所有介面
getClassLoader() 獲取當前類的類載入器
getConstructor(..) 獲取公共且明確參數類型的構造方法
getDeclaredConstructor(..) 獲取受保護且明確參數類型的構造方法
getConstructors() 獲取所有公共的構造方法
getDeclaredConstructors() 獲取所有(包括受保護)的構造方法
getField(..) 根據欄位名稱獲取某一個公共的欄位
getDeclaredField(..) 根據欄位名稱獲取當前類某一個的欄位(包括受保護的)
getFields() 獲取所有公共的欄位,包括繼承自父類的公共欄位
getDeclaredFields 獲取當前類所有欄位,包括受保護的(不包括繼承父類的欄位)
getMethod(..) 根據方法名以及參數類型獲取一個公共的方法
getDeclaredMethod(..) 根據方法名以及參數類型獲取當前類的一個受保護的方法
getMethods() 獲取所有公共的方法(包括繼承自父類的公共方法)
getDeclaredMethods() 獲取當前類的所有方法,包括受保護的(不包括繼承父類的方法)
isAnnotation() 此Class是否是一個註解
isAnnotationPresent(..) 當前Class時是否定義了某個註解
getAnnotation(..) 獲取當前類上定義的某個註解
getAnnotations() 獲取當前類上定義的所有註解
isEnum() 此Class是否是一個枚舉
isArray() 此Class是否是一個數組類型
isInterface() 此Class是否是一個介面
... 其他API請參照官方文檔
public class TestClass {
​
    public static void main(String[] args) throws Exception{
        Class<?> clazz = Class.forName("edu.nf.reflect.Users");
        //通過Class對象來創建實例,當前類必須提供一個公開並且無參的構造方法
        Users user = (Users) clazz.newInstance();
        //獲取類的完整類名
        System.out.println(clazz.getName());
        //獲取類的簡單類名
        System.out.println(clazz.getSimpleName());
        //獲取類所在的包名
        System.out.println(clazz.getPackage().getName());
        //獲取當前類的父類的Class
        System.out.println(clazz.getSuperclass());
        //獲取當前類實現的所有介面
        System.out.println(clazz.getInterfaces());
        //獲取載入這個類的類載入器
        System.out.println(clazz.getClassLoader());
        //獲取公共並且參數類型為String的構造方法
        clazz.getConstructor(String.class);
        //獲取私有並且參數為int類型的構造方法
        clazz.getDeclaredConstructor(Integer.TYPE);
        //獲取所有公共的構造方法
        Constructor[] consArray1 = clazz.getConstructors();
        //獲取所有構造方法,包括公共和是有的
        Constructor[] consArray2 = clazz.getDeclaredConstructors();
        //獲取某一個公共的欄位,參數指定欄位的名字
        Field f1 = clazz.getField("address");
        System.out.println(f1.getName());
        //獲取某一個受保護的欄位
        Field f2 = clazz.getDeclaredField("tel");
        System.out.println(f2.getName());
        System.out.println("-----------------------");
        //獲取所有公共的欄位,包括繼承自父類的公共欄位
        Field[] fieldArray1 = clazz.getFields();
        for (Field field : fieldArray1) {
            System.out.println(field.getName());
        }
        System.out.println("-----------------------");
        //獲取所有欄位,包括受保護的(不包括繼承父類的欄位)
        Field[] fieldArray2 = clazz.getDeclaredFields();
        for (Field field : fieldArray2) {
            System.out.println(field.getName());
        }
        System.out.println("-----------------------");
        //獲取某一個公共的方法(第一個參數指定方法名,第二個參數指定方法參數的類型,這是一個可變參數,有多少個參數就要指定多少個類型)
        Method m1 = clazz.getMethod("say", String.class);
        System.out.println(m1.getName());
        //獲取某一個受保護的方法
        Method m2 = clazz.getDeclaredMethod("call");
        System.out.println(m2.getName());
        System.out.println("-----------------------");
        //獲取所有公共的方法(包括繼承自父類的公共方法)
        Method[] methodArray1 = clazz.getMethods();
        for (Method method : methodArray1) {
            System.out.println(method.getName());
        }
        System.out.println("-----------------------");
        //獲取本類的所有方法,包括受保護的(不包括父類的方法)
        Method[] methodArray2 = clazz.getDeclaredMethods();
        for (Method method : methodArray2) {
            System.out.println(method.getName());
        }
    }
}
​

 

 

2.3 Class對象中的成員

成員說明
Constructor 用於描述類的構造方法
Field 用於描述類的欄位
Method 用於描述類的方法
Parameter 用於描述方法或構造方法的參數信息

2.3.1 Constructor常用API

API說明
getName() 獲取當前構造方法的名稱
getParameterCount() 獲取構造方法的參數個數
getDeclaringClass() 獲取聲明此構造方法的Class類
setAccessible(..) 設置訪問開關
newInstance(..) 使用當前的Constructor構建實例
... 其他API請參照官方文檔
public class TestCons {
    public static void main(String[] args) throws Exception{
        //載入Users的class信息,並構建Class對象
        Class<?> clazz = Class.forName("edu.nf.reflect.Users");
        //通過class對象訪問構造方法信息,
        // 根據構造方法的參數類型獲取某一個公共的構造方法
        Constructor cons1 = clazz.getConstructor(int.class);
        // 訪問受保護的構造方法
        Constructor cons2 = clazz.getDeclaredConstructor(int.class);
        //構造方法的名稱
        System.out.println(cons1.getName());
        //獲取構造方法的參數個數
        System.out.println(cons1.getParameterCount());
        //獲取定義這個構造方法的Class類
        System.out.println(cons1.getDeclaringClass());
        //通過構造方法來創建類的實例
        //由於構這個造方法是私有的,因此必須強制打開訪問開關
        cons1.setAccessible(true);
        //然後通過newInstance方法來創建類的實例,並傳入構造方法所需的參數
        Users user = (Users)cons1.newInstance(21);
        //獲取所有的構造方法(包括私有和公有的)
        Constructor[] cons = clazz.getDeclaredConstructors();
        for (Constructor con : cons) {
            System.out.println("參數個數: "+con.getParameterCount());
        }
    }
}

 

 

2.3.2 Field常用API

API說明
getType() 獲取當前欄位類型
getName() 獲取當前欄位名稱
get(..) 獲取當前欄位的值
set(..) 給當前欄位賦值
setAccessible(..) 設置訪問開關
... 其他API請參照官方文檔
public class TestField {
​
    public static void main(String[] args) throws Exception{
        Class<?> clazz = Class.forName("edu.nf.reflect.Users");
        //創建類的實例
        Object instance = clazz.newInstance();
        //獲取一個受保護的欄位
        Field f = clazz.getDeclaredField("tel");
        //打開訪問開關
        f.setAccessible(true);
        //給欄位賦值(第一個參數指定當前類的實例,第二個參數是具體的值)
        f.set(instance, "123456");
        //取值(get方法的參數也是指定當前類的實例)
        System.out.println(f.get(instance));
        //獲取欄位的類型
        System.out.println(f.getType());
        //獲取欄位的名稱
        System.out.println(f.getName());
    }
}

 


 

2.3.3 Method常用API

API說明
getName() 獲取當前方法名稱
getReturnType() 獲取當前方法的返回值
getParameterCount() 獲取當前方法參數的個數
getParameters() 獲取當前方法的參數對象
getParameterTypes() 獲取當前方法的所有參數類型
setAccessible(..) 設置訪問開關
invoke(..) 回調當前的method
... 其他API請參照官方文檔
public class TestMethod {
​
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("edu.nf.reflect.Users");
        //構建當前類的實例
        Object instance = clazz.newInstance();
        //獲取受保護的call方法
        Method method = clazz.getDeclaredMethod("call", String.class);
        //獲取方法的名稱
        System.out.println(method.getName());
        //獲取方法的返回值類型
        System.out.println(method.getReturnType());
        //獲取方法參數的個數
        System.out.println(method.getParameterCount());
        //獲取方法的所有參數對象
        System.out.println(method.getParameters());
        //獲取方法中所有參數的類型
        Class<?>[] paramsType = method.getParameterTypes();
        for (Class<?> aClass : paramsType) {
            System.out.println(aClass);
        }
        //調用當前方法(第一個參數是當前類的實例,第二個參數開始是方法所需的參數)
        //invoke方法返回的是當前調用的方法的返回值
        //這個方法是受保護的,因此需要先打開訪問開關
        method.setAccessible(true);
        Object returnValue = method.invoke(instance, "12345678");
        System.out.println(returnValue);
    }
}
​

 

 

2.3.4 Parameter常用API

API說明
getType() 獲取參數類型
getName() 獲取參數的名稱(說明:JDK8預設編譯的時候是不會將參數名信息保存在位元組碼中,如果想要獲取參數名,那麼在編譯時就需要加上編譯參數 ,如:javac -parameters Users.java)
... 其他API請參照官方文檔

public class TestParameter {
​
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("edu.nf.reflect.Users");
        //先獲取指定的受保護的方法
        Method callMethod = clazz.getDeclaredMethod("call", String.class);
        //獲取該方法的參數信息
        Parameter[] params = callMethod.getParameters();
        for (Parameter param : params) {
            //獲取參數的類型
            System.out.println(param.getType());
            //獲取參數的名稱(在JDK8之前是沒有辦法獲取參數名的,必須通過第三方位元組碼工具來獲取)
            //JDK8預設編譯的時候是不會講參數名信息保存在位元組碼中
            //如果想要獲取參數名,那麼在編譯時就需要加上編譯參數
            //如:javac -parameters Users.java
            System.out.println(param.getName());
        }
    }
}

 

 

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

-Advertisement-
Play Games
更多相關文章
  • 變數定義 1.基礎定義 變數類型在變數名後 2.定義並賦值 3.類型推導 不用定義變數類型 4.簡寫(只能在函數內) 用":="代替"var" 5.定義多個變數並賦值 測試代碼 ...
  • 詳細教程 參考struts教程https://www.w3cschool.cn/struts_2/struts_configuration.html Struts2 基於MVC設計模式的web應用程式框架,它不僅僅是Struts1 的升級版本,更是一個全新的Struts架構。最初,是以WebWork ...
  • 一.文件的打開,open函數 打開模式有很多種 1. 'r': 以只讀方式打開文件。文件的指針將會放在文件的開頭。這是預設模式。 2. 'r+': 打開一個文件用於讀寫。文件指針將會放在文件的開頭,但寫入內容會寫到文件內容末尾。 3. 'w': 打開一個文件只用於寫入。如果該文件已存在則打開文件,並 ...
  • 1.什麼是servlet? Servlet(Servlet Applet),全稱Java Servlet,是用Java編寫的伺服器端程式。而這些Servlet都要實現Servlet這個介面。其主要功能在於互動式的瀏覽和修改數據,生成動態Web內容。Servlet運行於支持Java的應用伺服器中。 2 ...
  • <!--done--> 知識預覽 Django 如何使用admin組件來對後臺數據進行管理的? Django admin如何實現後臺數據管理的?(admin源碼解析) 如何仿照admin實現一個自定義的增刪改查的組件? 回到頂部 Django 如何使用admin組件來對後臺數據進行管理的? 在每個a ...
  • 給定一組字元,使用原地演算法將其壓縮。 壓縮後的長度必須始終小於或等於原數組長度。 數組的每個元素應該是長度為1 的字元(不是 int 整數類型)。 在完成原地修改輸入數組後,返回數組的新長度。 進階: 你能否僅使用O(1) 空間解決問題? 示例 1: 輸入: ["a","a","b","b","c" ...
  • JPA是java Persistence API簡稱,中文名:java持久層API,JPA是JCP組織發佈的J2EE標準之一 1.創建DataSource連接池對象 1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <art ...
  • 俗話說得好,工欲善其事,必先利其器,有了好的工具肯定得知道如何用好這些工具,本篇將介紹如何利用好緩存。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...