Java筆記(17) 反射

来源:https://www.cnblogs.com/hiibird/archive/2023/04/17/17325409.html
-Advertisement-
Play Games

Java的反射機制允許程式員在執行期藉助於Reflection API取得任何類的內部信息,並能操作對象的屬性和方法,在各類框架中應用非常廣泛。這一期是關於反射內容的筆記,包含Class類、Field類、Method類、Constructor類及相關方法。 ...


需求:
根據配置文件 re.properties 中指定的信息,創建對象,並調用方法
classfullpath=com.hiibird.Cat
method=hi

使用現有技術能做到嗎?

//首先讀取配置文件
Properties properties = new Properties();
File file = new File("./Reflection/src/re.properties");
properties.load(new FileReader(file));
//可以獲得類名和包路徑,以及方法名,但現有方法無法利用這些信息重構該類或者調用方法
String classFullPath = properties.getProperty("classfullpath");
String methodName = properties.getProperty("method");

//new classFullPath(); //使用傳統方法無法利用String類型的報名創建類

//使用反射機制
//1. 載入類,返回Class類型的對象
Class<?> clazz = Class.forName(classFullPath);
System.out.println(clazz);
//2. 通過aClass 的到載入的類 com.hiibird.Cat 的對象實例
Object o = clazz.getDeclaredConstructor().newInstance();
System.out.println("o的運行類型:" + o.getClass());//o的運行時類型還是com.hiibird.Cat
//3. 通過getMethod()方法得到載入的類 com.hiibird.Cat 的methodName "hi" 的方法對象
//即:在反射中,可以把方法視為對象(萬物皆對象)
Method method = clazz.getMethod(methodName);
//4. 調用:通過method 調用方法:即通過方法對象來實現調用方法
method.invoke(o);   //傳統方法 對象.方法();反射機制:方法.invoke(對象)

這樣的需求在學習框架時特別多,即通過外部配置文件,在不修改源碼的情況下來控製程序,也符合設計模式的ocp原則(開閉原則:不修改源碼,擴容功能)

1. 反射機制

  1. 反射機制允許程式員在執行期藉助於Reflection API取得任何類的內部信息(比如成員變數,構造器和成員方法等),並能操作對象的屬性及方法。反射在設計模式和框架底層都會用到。
  2. 載入完類之後,在堆中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象包含了類的完整結構信息。通過這個對象得到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構,所以形象的稱之為反射。

1.1 Java反射機制的作用及優缺點

  1. 在運行時判斷任意一個對象所屬的類
  2. 在運行時構造任意一個類的對象
  3. 在運行時得到任意一個類所具有的成員變數和方法
  4. 在運行時調用任意一個對象的成員變數和方法
  5. 生成動態代理

反射機制的優缺點:優點:可以動態的創建和使用對象(也是框架底層核心),使用靈活,沒有反射機制,框架技術就失去底層支撐;缺點:使用反射基本是解釋執行,對執行速度有影響

1.2 反射相關的主要類

  1. java.lang.Class:代表一個類,Class對象表示某個類載入後在堆中的對象
Class<?> clazz = Class.forname(classfullpath);
Object instance = clazz.getDeclaredConstructor().newInstance();
  1. java.lang.reflect.Method:代表類的方法,Method對象表示某個類的方法
//傳統寫法:instance.method(),反射:method.invoke(instance);
Method method = clazz.getMethod(methodName);
method.invoke(instance);
  1. java.lang.reflect.Field:代表類的成員方法,Field對象標識某個類的成員變數。getField得不到私有的成員變數
//傳統寫法:instance.field,反射:field.get(instance)
Field field = clazz.getField(fieldName);
System.out.println(nameField.get(instance));
  1. java.lang.reflect.Constructor:代表類的構造方法,Constructor對象表示構造器
Constructor<?> constructor = clazz.getConstructor();//返回無參構造器
//通過指定構造器參數類型,獲取有參構造器
Constructor constructor1 = clazz.getConstructor(String.class, Integer.class);

1.3 反射調用優化-關閉訪問檢查

  1. Method和Field、Constructor對象都有setAceessible()方法
  2. setAccessible作用是開啟和禁用訪問安全檢查的開關
  3. 參數值為true表示反射的對象在使用時取消訪問檢查,提高反射的效率。參數值為false則表示反射的對象執行訪問檢查
method.setAccessible(true);//在反射調用時取消訪問檢查,可以稍微提高性能
method.invoke(instance);

2. Class類

類定義:

public final class Class<T> implements java.io.Serializable,
                                GenericDeclaration,
                                Type,
                                AnnotatedElement

基本介紹

  1. Class也是類,因此也繼承Object類
  2. Class類對象不是new出來的,其構造方法是私有的,只有JVM可以創建Class類對象
  3. 對於某個類的Class類對象,在記憶體中只有一份,因為類只載入一次,這個對象在類載入時由JVM創建
  4. 每個類的實例都會記得自己是由那個Class實例所生成
  5. 通過Class可以完成地得到一個類的完整結構,通過一系列API
  6. Class對象是存放在堆的
  7. 類的位元組碼二進位數據,是存放在方法區的,有的地方稱為類的元數據(包括方法代碼,變數名,方法名,訪問許可權等等)

2.1 Class類的常用方法

方法名 功能說明
static Class forName(String name) 返回指定類名name的Class對象
Object getConstructor().newInstance() 調用預設構造函數,返回該Class對象的一個實例
getName() 返回此Class對象所表示的實體(類、介面、數組類、基本類型等)名稱
class getSuperclass() 返回當前Class對象的超類的Class對象
Class[] getInterface() 返回當前Class對象的介面
ClassLoader getClassLoader() 返回該類的類載入器
Constructor[] getConstructors() 返回一個包含某些Constructor對象的數組
Field[] getDeclaredFields() 返回Field對象的一個數組
Method getMethod(String name, Class ... paramType) 返回一個Method對象,此對象的形參為paramType

2.2 獲取Class對象的6中方式

  1. 前提:已知一個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()獲取,可能拋出ClassNotFoundException。多用於配置文件,讀取類全路徑,載入類。實例:
Class clazz = Class.forName("java.lang.Cat");
  1. 前提:已知具體的類,通過類的class獲取,該方式最為安全可靠,程式性能最高。多用於參數傳遞,比如通過反射得到對應構造器對象。實例:
Class<Cat> clazz = Cat.class;
//用來傳遞參數
Constructor constructor = clazz.getConstructor(String.class, Integer.class);
  1. 前提:已知某個類的實例,調用該實例的getClass()方法獲取Class對象,多用於通過創建好的對象,獲取Class對象。實例:
Class<> clazz = instance.getClass();
  1. 其他方式:通過類載入器(有4種)獲取Class對象
ClassLoader cl = instance.getClass().getClassLoader();
Class clazz = cl.loadClass("classFullPath");
  1. 基本數據(int,char,boolean,float,double,byte,long,short)按如下方式得到Class對象:
Class<Integer> clazz = int.class;
  1. 基本數據類型對應的包裝類,可以通過.type得到Class對象:
Class<Integer> clazz = Integer.TYPE;

2.3 哪些類型有Class對象

  1. 外部類,成員內部類,靜態內部類,局部內部類,匿名內部類
  2. interface:介面
  3. 數組
  4. enum:枚舉
  5. annotation:註解
  6. 基本數據類型
  7. void

2.3 通過反射獲取類的結構信息

java.lang.Class類

  1. getName:獲取全類名
  2. getSimpleName:獲取簡單類名
  3. getFields:獲取所有public修飾的屬性,包含本類以及父類的
  4. getDeclaredFields:獲取本類中所有屬性
  5. getMethods:獲取所有public修飾的方法,包含本類以及父類的
  6. getDeclaredMethods:獲取本類中所有的方法
  7. getConstructors:獲取本類所有public修飾的構造器,不包含父類的
  8. getDeclaredConstructors:獲取本類中所有的構造器
  9. getPackage:以Package的形式返回包信息
  10. getSuperClass:以Class形式返回父類信息
  11. getInterfaces:以Class形式返回介面信息
  12. getAnnotations:以Annotations[]形式返回註解信息

java.lang.reflect.Field類

  1. getModifiers:以interesting形式返回修飾符【說明:預設修飾符是0,public是1,private是2,protected是4,static是8,final是16】:public static... -> 1+8 = 9
  2. getType:以Class形式返回類型
  3. getName:返回屬性名

java.lang.reflect.Method類

  1. getModifiers:以int形式返回修飾符【說明:預設修飾符是0,public是1,private是2,protected是4,static是8,final是16】:public static... -> 1+8 = 9
  2. getReturnType:以Class形式獲取 返回類型
  3. getName:返回方法名
  4. getParameterTypes:以Class[]形式返回參數類型數組

java.lang.reflect.Constructor類

  1. getModifiers:以int形式返回修飾符
  2. getName:返回構造器名(全類名)
  3. getParameterType:以Class[]返回參數類型數據

2.4. 通過反射創建對象

  1. 方法一:調用類中的public修飾的無參構造器
  2. 方法二:調用類中的指定構造器
  3. Class類相關方法:
    • getDeclaredConstructor().newInstance():調用類中的無參構造器,獲取相應類的對象
    • getConstructor(Class...clazz):根據參數列表,獲取對應的構造器對象
    • getDeclaredConstructor(Class...clazz):根據參數列表,獲取對應的構造器對象
  4. Constructor類相關方法:
    • setAccessible:暴破
    • newInstance(Object...obj):調用構造器

2.5 通過反射訪問類中的成員

  1. 根據屬性名獲取Field對象:Field f = clazz.getDeclaredField(屬性名);
  2. 暴破: f.setAccessible(true); //f是Field對象
  3. 訪問 f.set(instance,value); f.get(instance);
  4. 註意如果是靜態屬性,則set和get中的參數o,可以寫成null

2.6 通過反射訪問類中的方法

  1. 根據方法名和參數列表獲取Method方法對象:
    Method m = clazz.getDeclaredMethod(方法名, XX.class);
  2. 獲取對象:Object o = clazz.getDeclaredConstructor().newInstance();
  3. 爆破:m.setAccessible(true);
  4. 訪問:Object returnValue = m.invoke(o, 實參列表);
  5. 在反射中,如果方法有返回值,統一返回Object
  6. 註意:如果是靜態方法,則invoke的參數o,也可以寫成null

3 類載入

基本說明:反射機制是java實現動態語言的關鍵,也就是通過反射實現類動態載入。

  1. 靜態載入:編譯時載入相關的類,如果沒有則報錯,依賴性太強
  2. 動態載入:運行時載入需要的類,如果運行時不用該類,即使不存在該類,也不會報錯,降低了依賴性

3.1 類載入時機

  1. 當創建對象時(new)//靜態載入
  2. 當子類被載入時,父類也被載入//靜態載入
  3. 調用類的靜態成員時//靜態載入
  4. 通過反射//動態載入

3.2 類的載入過程

類載入過程


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

-Advertisement-
Play Games
更多相關文章
  • package.json 備忘清單 如果你以前用過 Node.js,則可能會遇到 package.json 文件。它是一個 JSON 文件,位於項目的根目錄中。你的 package.json 包含關於項目的重要信息。它包含關於項目的使人類可讀元數據(如項目名稱和說明)以及功能元數據(如程式包版本號和 ...
  • # 請先使用命令 pip install sxtwl 安裝依賴庫後,再執行以下腳本 import sxtwl ymc = ["正", "二", "三", "四", "五", "六", "七", "八", "九", "十" ,"冬", "臘"] rmc = ["初一", "初二", "初三", &qu ...
  • 源代碼下載鏈接: 一、賓館客房管理系統開發初衷 隨著互聯網技術的迅速發展,電腦技術的普及以及信息化時代的推波助瀾,賓館客房需求的逐漸增大,這也是挑戰了賓館客房管理方面的技術,以前的人工管理方式已經不再適應現在的環境,取而代之的是先進的賓館客房管理系統,提高了賓館的工作效率,為想要入住賓館的人提供更 ...
  • 代碼如下: import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxin ...
  • 併發工具類 通常我們所說的併發包也就是java.util.concurrent(JUC),集中了Java併發的各種工具類, 合理地使用它們能幫忙我們快速地完成功能 。 作者: 博學谷狂野架構師 GitHub:GitHub地址 (有我精心準備的130本電子書PDF) 只分享乾貨、不吹水,讓我們一起加油 ...
  • 好消息:與上題的Emergency是同樣的方法。壞消息:又錯了&&c++真的比c方便太多太多。 A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members ...
  • 安裝Zookeeper和Kafka集群 本文介紹如何安裝Zookeeper和Kafka集群。為了方便,介紹的是在一臺伺服器上的安裝,實際應該安裝在多台伺服器上,但步驟是一樣的。 安裝Zookeeper集群 下載安裝包 從官網上下載安裝包: curl https://dlcdn.apache.org/ ...
  • 原文鏈接:https://www.zhoubotong.site/post/94.html 說下背景吧,大家在開發中可能在不同的目錄(package)下定義了相同的struct(屬性參數完全一樣如名字、個數和類型),在方法調用傳參數的時候,可能是用到了其中某一個struct的引用。 那麼這裡就牽扯到 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...