JAVASE:註解與反射筆記

来源:https://www.cnblogs.com/wutong666/archive/2023/04/01/17278796.html
-Advertisement-
Play Games

JavaSE:註解與反射(Annotation & Reflection) ​ 註解和框架是所有框架的底層,如Mybatis,spring。框架的底層實現機制就是註解和反射。註解相比於註釋,除了能較為直接的表示出這部分模塊的功能,也能實現一定的具體功能。 01 初識註解 1.1 什麼是註解 Anno ...


JavaSE:註解與反射(Annotation & Reflection)

​ 註解和框架是所有框架的底層,如Mybatis,spring。框架的底層實現機制就是註解和反射。註解相比於註釋,除了能較為直接的表示出這部分模塊的功能,也能實現一定的具體功能。

01 初識註解

1.1 什麼是註解

  • Annotation是從JDK5.0引入的新技術

  • Annotation的作用:

    • 不是程式本身,但可以對程式做出解釋。(這一點和註釋comment沒什麼區別)

    • 可以被其他程式(比如:編譯器等)讀取。

  • Annotation的格式:

    • 註解是以“@註釋名”在代碼中存在的,還可以添加一定參數值,如
@Override
@SuppressWarnings(Value="unchecked")
  • Annotation在哪裡使用?
    • 可以附加在package,class,method,field等上面,相當於給他們添加了額外的輔助信息,我們可以通過反射機制編程實現對這些元數據的訪問。

1.2 內置註解

  • @Override
    
    • 定義在java.lang.Overide中,此註釋只適用於修辭方法,表明一個方法聲明打算重寫超類中的另一個方法申明。
  • @Deprecated
    
    • 定義在java.lang.Deprecated中,此註釋可適用於修辭方法,屬性,類,表示不鼓勵程式員使用這樣的元素,通常因為該方法比較危險或者存在更好的方法
  • @SupperWarnings
    
    • 定義在java.lang.SupperWarnings中,用來抑制編譯時的告警信息。

    • 但與前兩個註解不同的是,這個註解需要參數,具體如何設置參看JDK說明文檔

    • @SupperWarnings("all")
      
    • @SupperWarnings("unchecked")
      
    • @SupperWarning("unchecked","deprecation")
      
    • 等等....

1.3 元註解

  • 元註解的作用就是負責註解其他註解,Java定義了4個標準的meta-annotation類型。它們被用來提供對其他annotation類型做說明。

  • 這些類型和它們所支持的類在java.kang.annotation包中可以找到(@Target,@Retention,@Documented,@inherited)

    • @Target:用於描述註解的使用範圍(即:被描述的註解可以用在什麼地方)

    • @Retention:表示需要在什麼級別保存該註釋信息,用於描述註解的生命周期

      • (SOURCE(源代碼)<CLASS(Javac編譯文件)<RUNTIME(軟體運行))
    • @Documented:說明該註解將被包含在javadoc中

    • @Inherited:說明子類可以繼承父類中的註解

1.4 自定義註解

  • 使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation介面

  • 分析:

    • @interface用來聲明一個註解,格式:public @interface 註解名

      • 需要註意,如果該文件中已有public,則註解定義需去掉public,一個文件中只能有一個public方法
    • 其中的每一個方法實際上是聲明瞭一個配置參數

    • 方法的名稱就是參數的名稱

    • 返回值類型就是參數的類型(返回值只能是基本類型,Class,String,enum)

    • 可以通過添加default來聲明參數的預設值

    • 如果只有一個參數成員,一般參數名為value

      • 如果參數名為value,則可以在賦值時省略"value = ",直接寫賦值內容即可
    • 註解元素必須要有值,我們定義註解元素時,經常使用空字元串,0作為預設值

02 反射

2.1 反射概述

2.1.1 靜態 vs 動態語言

  • 動態語言

    • 是一類在運行時可以改變其結構的語言,例如新的函數、對象、甚至代碼可以被引進,已有的函數可以被刪除或是其他結構上的變化。通俗點說就是在運行時代碼可根據某些條件改變自身結構。

    • 主要動態語言:Object-C、C#、JavaScript、PHP、Python等

  • 靜態語言

    • 與動態語言相對應,運行時結構不可變的語言就是靜態語言。如Java、C、C++。
  • Java不是動態語言,但Java可以稱之為“準動態語言”。即Java有一定的動態性,我們可以利用反射機制獲得類似動態語言的特性。Java的動態性讓編程的時候更加靈活!

2.1.2 Java Reflection

  • Reflection(反射)是Java被視為“準動態語言的”關鍵,反射機制允許程式在執行期藉助Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法。

    Class c = Class.forName("java.lang.String")
    
  • 載入完類之後,在堆記憶體的方法區中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象就包含了完整的類的結構信息。我們可以通過這個對象看到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構。所以,人們形象稱之為反射

2.1.3 Java反射機制研究及應用

  • Java反射機制提供的功能:

    • 在運行時判斷任意一個對象所屬的類
    • 在運行時構造任意一個類的對象
    • 在運行時判斷任意一個類所具有的的成員變數和方法
    • 在運行時獲取泛型信息
    • 在運行時調用任意一個對象的成員變數和方法
    • 在運行時處理註解
    • 生成動態代理
  • Java反射的優點和缺點

    • 優點:

      • 可以實現動態創建對象和編譯,體現出很大的靈活性
    • 缺點:

      • 對性能會有影響。使用反射基本上是一個解釋操作,我們可以告訴JVM,我們希望做什麼並且讓它滿足我們的需求。這類操作總是慢於直接執行相同的操作。
  • 反射相關的主要API

    • java.lang.Class:代表一個類
      
    • java.lang.reflect.Method:代表類的方法
      
    • java.lang.reflect.Field:代表類的成員變數
      
    • java.lang.reflect.Constructor:代表類的構造器
      
  • Class類

    • 在Object類中定義瞭如下的方法,此方法將被所有子類繼承

      public final Class getClass()
      
    • 以上的返回值類型是一個Class類,此類是Java反射的源頭,實際上所謂反射從程式的運行結果來看也很好理解,即:可以通過對象反射求出類的名稱。

    • 對象照鏡子後可以得到的信息包括:某個類的屬性、方法和構造器、某個類到底實現了哪些介面。對於每個類而言,JRE都為其保留一個不變的Class類型的對象。一個Class對象包含了特定的某個結構(class/interface/enum/annotation/primitive type/void/[])的有關信息

      • Class本身也是一個類
      • Class對象只能由系統建立對象

  • 哪些類型可以有Class對象?

    • class:外部類,成員(成員內部類,靜態內部類),局部內部類、匿名內部類

    • interface:介面

    • []:數組

    • enum:枚舉

    • annotation:註解@interface

    • primitive type:基本數據類型

    • void

2.2 Java記憶體分析

2.2.1 Java記憶體分配

2.2.2 類的載入過程

​ 當程式主動使用某個類時,如果該類還未被載入到記憶體中,則系統會通過如下三個步驟來對該類進行初始化。

2.2.3 類載入器

  • 類載入器的作用:將class文件位元組碼內容載入到記憶體中,並將這些靜態數據轉換成方法區的運行時數據結構,然後在堆中生成一個代表這個類的java.lan.Class對象,作為方法區中類數據的訪問入口。

  • 類緩存:標準的JavaSE類載入器可以按要求查找類,但一旦某個類被載入到類載入器中,它將維持載入(緩存)一段時間。不過JVM垃圾回收機制可以回收這些Class對象。

  • JVM規範定義瞭如下類型的類的載入器:

    • 引導類載入器:用C++編寫的,是JVM自帶的類載入器,負責Java平臺核心庫,用來裝載核心類庫。需註意此庫無法直接獲取。

    • 擴展類載入器:負責jre/lib/ext目錄下的jar包或-D java.ext.dirs指定目錄下的jar包裝入工作庫

    • 系統類載入器:負責java -classpath或-D java.class.path所指的目錄下的類與jar包裝入工作,是最常用的載入器。

  • 雙親委派機制:是防止同名包、類與 jdk 中的相衝突,實際上載入類的時候,先通知 appLoader,看 appLoader 是否已經緩存,沒有的話,appLoader 又委派給他的父類載入器(extLoader)詢問,看他是不是能已經緩存載入,沒有的話,extLoader 又委派他的父類載入器(bootstrapLoader)詢問,BootstrapLoader看是不是自己已緩存或者能載入的,有就載入,沒有再返回 extLoader,extLoader 能載入就載入,不能的話再返回給 appLoader 載入,再返回的路中,誰能載入,載入的同時也加緩存里。正是由於不停的找自己父級,所以才有 Parents 載入機制,翻譯過來叫 雙親委派機制

2.2.4 類的載入與ClassLoader的理解

  • 載入:將class文件位元組碼載入到記憶體中,並將這些靜態數據轉換成方法區的運行時數據結構,然後生成一個代表這個類的java.lang.Class對象

  • 鏈接:將Java類的二進位代碼合併到JVM的運行狀態之中的過程

    • 驗證:確保載入的類的信息符合JVM規範,沒有安全方面的問題
    • 準備:正式為類變數(static)分配記憶體並設置類變數預設初始值的階段,這些記憶體都將在方法區中進行分配。
    • 解析:虛擬機常量池內的符號引用(常量名)替換為直接引用(地址)的過程
  • 初始化:

    • 執行類構造器()方法。類構造器()方法是由編譯器自動收集類中所有類變數的賦值動作和靜態代碼塊中的語句合併產生的。(類構造器是構造類信息的,不是構造該類對象的構造器)

    • 當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化。

    • 虛擬機會保證一個類的()方法在多線程環境中被正確加鎖和同步

2.2.5 獲取運行時類的完整結構

  • 通過反射

    • 包括如下信息:Field、Method、Constructors、Superclass、Interface、Annotation

    • 實現的全部介面

    • 所繼承的父類

    • 全部的構造器

    • 全部的方法

    • 全部的Field

    • 註解···

小結

  • 在實際的操作中,取得類的信息的操作代碼,並不會經常開發

  • 一定要熟悉java.lang.reflect包的作用和反射機制

  • 如何取得屬性、方法、構造器的名稱,修飾等。

2.3 動態創建對象執行方法

  • 創建類的對象:調用Class對象的newInstance()方法

    • 1)類必須有一個無參數的構造器。(無參構造器必須有)

    • 2)類的構造器的訪問許可權需要足夠

  • 除了調用無參構造器創建對象外,也可以

    • 1)通過Class類的getDeclaredConstructor(Class...parameterTypes)取得本類的制定形參類型的構造器

    • 2)向構造器的形參中傳遞一個對象數組進去,裡面包含了構造器中所需的各個參數

    • 3)通過Constructor實例化對象

  • 利用反射調用指定的方法

    通過反射,調用類中的方法,通過Method類完成。

    • 通過Class類的getMethod(String name,Class...parameterTypes)方法取得一個Method對象,並設置此方法操作時所需要的參數類型(防止出現方法重寫,利用參數類型和方法名確定具體的方法)

    • 之後使用Object invoke(Object obj , Object[] args)進行調用,並向方法中傳遞要設置的obj對象的參數信息。

    • Object invoke(Object obj,Object...args)

      • Object對應原方法的返回值。若原方法無返回值,此時返回null

      • 原方法若為靜態方法,此時形參Object obj可為null

      • 若原方法形參列表為空,則Object[] args為null

      • 若原方聲明為private,則需要在調用次invoke()方法前,顯式調用方法對象的setAccessible(true),將可訪問private的方法

    • SetAccessible

      • Method和Field、Constructor對象都有setAccesible()方法。

      • setAccessible作用是啟動和禁用訪問安全檢查的開關。

      • 參數值為true則指示反射的對象在使用時應當取消Java語言訪問檢查。

        • 提高反射的效率。如果代碼中必須用反射,而該句代碼需要頻繁被調用,那麼設置為true。

        • 使得原本無法訪問的私有成員也可以訪問。

      • 參數值為false則指示反射的對象應該實施Java語言訪問檢查

2.4 反射操作泛型

  • Java採用泛型擦除機制來引入泛型。Java中的泛型僅僅是給編譯器javac使用的,確保數據的安全性和免去強制類型轉換的問題,但是,一旦編譯完成,所有和泛型有關的類型全部擦除

  • 為了通過反射操作這些類型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType幾種類型來代表不能被歸一到Class類中的類型但是又和原始類型齊名的類型

    • ParameterizedType:表示一種參數化類型,如Collection

    • GenericArrayType:表示一種元素類型是參數化類型或者類型變數的數組類型

    • TypeVariable:是各種類型變數的公共父介面

    • WildcardType:代表一種通配符類型表達式

2.5 反射操作註解

  • 這部分很重要,開始和後面框架出現結合(熟知註解對於後期框架學習很重要)
  • 放一個案例在這裡說明相關功能
  • 日後將在框架學習筆記中進一步闡述用反射操作註解的重要性
  • 先定義一個類註解(關註Target裡面參數設置)
//類名的註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TypeTong{
    String value();
}
  • 再定義一個屬性的註解(需要註意自定義的兩個註解都有參數)
//屬性的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTong{
    String colunmName();
    String type();
    int length();

}
  • 此後定義一個student類,用於後面的反射
@TypeTong(value = "db_student")
class student2{

    @FieldTong(colunmName = "db_name", type = "varchar",length = 3 )
    private String name;

    @FieldTong(colunmName = "db_id", type = "int",length = 10 )
    private  int id;

    @FieldTong(colunmName = "db_age", type = "int",length = 10 )
    private  int age;

    public student2() {
    }

    public student2(String name, int id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "student2{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }
}
  • main函數編製
 public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
      Class s1 = Class.forName("AnnotationandReflection.Demo03.student2");

      //通過反射獲得註解
        Annotation[] annotations = s1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
      //通過反射獲得指定註解屬性值
        System.out.println("==========================");
        TypeTong typeTong = (TypeTong)s1.getAnnotation(TypeTong.class);
        String value = typeTong.value();
        System.out.println(value);

      //獲得類指定的註解
        System.out.println("==========================");
        Field f = s1.getDeclaredField("id");
        FieldTong annotation = f.getAnnotation(FieldTong.class);
        System.out.println(annotation.colunmName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());

    }
  • 程式運行後結果如下:


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

-Advertisement-
Play Games
更多相關文章
  • 項目背景 在java項目部署過程中,由於內外部各種因素,可能會遇到一些感覺操作不便捷的場景,例如 jar包未隨系統自動啟動需要每次手動重啟 系統vpn堡壘機多重防禦更新繁瑣 系統無圖形化界面命令行操作複雜 等等...... 在工作中之前也總結了windows的Jar包部署工具與linux下的jar包 ...
  • Java記憶體區域 說一下 JVM 的主要組成部分及其作用? JVM包含兩個子系統和兩個組件,兩個子系統為Class loader(類裝載)、Execution engine(執行引擎);兩個組件為Runtime data area(運行時數據區)、Native Interface(本地介面)。 ●C ...
  • 在使用codeium這個AI提示插件的過程中,使用中文註釋,智能提示的提示語,會有可能展示為亂碼、方塊字 如下圖中的灰色提示語: tab以後,就展示正常了。 在中文網上搜了下,沒有相關資料,去codeium的discord頻道問了下,找到瞭解答: 解答為:將首選項->編輯器->字體從“JetBrai ...
  • 1.相關組件 |組件 | 說明 |版本地址| | | | | |Nacos |配置及註冊中心 |https://github.com/alibaba/nacos/releases| ps: SpringBoot、SpringCloud和nacos集成版本對應關係對照(版本若對應不上,應用可能會啟動報 ...
  • #案例一 列印排序好的數據 #列表方式 lst_name=['林黛玉','薛寶釵','賈元春','賈探春','史湘雲'] lst_sign=['①','②','③','④','⑤'] for i in range(5): print(lst_sign[i],lst_name[i]) print(' ...
  • 7.1 潰壩 官網 目錄:$FOAM_TUTORIALS/multiphase/interFoam/laminar/damBreak 7.1.1 介紹 本案例使用interFoam兩相演算法,基於流體體積分數(VOF)法,每個網格中的相體積分數(alpha)通過求解一個組分運輸方程確定。物理屬性基於這 ...
  • 1、MySQL 中有哪幾種鎖? (1)表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最 高,併發度最低。 (2)行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最 低,併發度也最高。 (3)頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表 鎖 ...
  • 原創:扣釘日記(微信公眾號ID:codelogs),歡迎分享,非公眾號轉載保留此聲明。 上個月,我們一個java服務上線後,偶爾會發生記憶體OOM(Out Of Memory)問題,但由於OOM導致服務不響應請求,健康檢查多次不通過,最後部署平臺kill了java進程,這導致定位這次OOM問題也變得困 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...