java註解與反射

来源:https://www.cnblogs.com/rainaftersummert/archive/2023/05/30/17444816.html
-Advertisement-
Play Games

# java註解與反射 - java註解與反射十分重要,是很多框架的底層 ## 註解(Annotataion) - 註解的作用: 1. 不是程式本身,可以對程式作出解釋 1. 可以被其他程式讀取 - 註解的格式:@註釋名,如@override表示重寫方法,而且有些還可以添加一些參數值,如@Suppr ...


java註解與反射

  • java註解與反射十分重要,是很多框架的底層

註解(Annotataion)

  • 註解的作用:

       1. 不是程式本身,可以對程式作出解釋
       1. 可以被其他程式讀取
    
  • 註解的格式:@註釋名,如@override表示重寫方法,而且有些還可以添加一些參數值,如@SuppressWarnings(value="unchecjed")

  • 註解可以附加在package,class,method,field等上面,相當於添加了額外的輔助信息。可以通過反射機制編程實現對這些元數據的訪問

內置註解

  1. @Override:重寫方法
  2. @Deprecated:表示不被鼓勵使用或者已廢棄已過時
  3. @SuppressWarning:用來抑制編譯時的警告信息,但是需要添加一個或多個參數:如("all"),("unchecked")等,平時最好還是不要用這個

使用方法:

@Override
public  void run() {
}

其他類似。

元註解

  • 負責註解其他註解
  • 包括(@Target,@Retention,@Documented,@Inherited),分別表示
    1. 描述註解使用範圍
    2. 需要上面級別保存該註釋信息,用於描述註解的生命周期(SOURCE<CLASS<RUNTIME,分別表示源代碼,編譯後的class文件,運行時)
    3. 說明該註解將被包含在javadoc中
    4. 說明子類可以繼承父類中的該註解
//下麵只能用到方法上,value的值可以取多個至於那些可以取CTRL+左鍵點擊ElementType類去看,另外可以省略value=
@Target(value = ElementType.METHOD)
//只能存在於源碼上。值有那些同上,自己點進去看RetentionPolicy類,一般取RUNTIME
@Retention(value = RetentionPolicy.SOURCE)
//這兩個不多說
@Documented
@Inherited
//自定義註解
@interface MyAnnotation{

}

自定義註解

  • 使用@interface自定義註解,格式 public @interface 註解名{}
  • 其中每一個方法實際上是聲明瞭一個配置參數
  • 方法名稱就是參數名稱
  • 返回值類型就是參數類型(返回值只能是基本類型或class,String,enum)
  • 可以用default來聲明參數的預設值
  • 如果只有一個參數,一般參數名為value,只有一個參數時使用可以不寫參數名=
  • 註解元素必須有值,我們定義註解元素時,經常使用空字元串,預設值為0
//元註解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
//自定義註解
@interface MyAnnotation{
//    註解參數:參數類型+參數名(),可以設置預設值,沒預設值的話使用時必須傳參
    String name() default "";
    int a();
    int c() default 1;
}

//使用
@MyAnnotation(name = "lihua",a = 1)

反射(Reflection)

java是一個靜態語言(還有c,c++),因為它相對於動態語言來說,運行時結構不變,但是java可以利用反射來獲得類似動態語言的特性,成為“準動態語言”。

動態語言就是運行時可以改變其結構的語言,如:c#,js,php,python等

  • 反射機制運行程式在執行的過程中通過反射取得任何類的內部信息,並且能夠直接操作任意對象的內部屬性及方法。

  • 主要API是Class

  • 反射是什麼?

參考一下Class對象在java編譯運行的那個部分:

反射提供的部分功能:

  • 優點與缺點

    優點:動態編譯和創建,體現很大的靈活性

    缺點:對性能有影響

Class類

除了之前描述的,Class類還有一些特征:

  • Class 本身也是一個類
  • Class對象只能有系統建立對象
  • 一個載入的類在JVM中只會有一個Class實例
  • 一個Class對象對應的是一個載入到JVM中的一個.class文件
  • 每個類的實例都會記得自己是由那個Class實例所生成的
  • 通過Class可以完整得到一個類中的所有被載入結構
  • Class類是反射的根源,,你要進行的任何針對類的動態載入運行等反射操作,都需要先得到該類的Class對象
public static void main(String[] args) throws ClassNotFoundException {
    /*1.下麵是通過反射獲取Class對象的一種方式,參數是文件路徑
      2.一個類在記憶體中只有一個Class類,在多創建幾個該類的Class
     也是同一個。
      3一個類被載入後,類的整個結構都會被封裝在Class對象中
      4.我們經常用的Object的getClass方法就是返回一個Class對象,也是創建Class對象的一種常用方式
      5.反射在某種情況下可以理解為通過一個對象獲得類
     */
    Class<?> aClass = Class.forName("com.xxx.MyThread");
    System.out.println(aClass);
}

//out:class com.xxx.MyThread

Class類的常用方法:

Class類的四種創建方式

雖然上面可能已經描述過Class對象的創建方式的兩種,這裡還是再來具體寫一下:

public class Test {

    public static void main(String[] args) throws ClassNotFoundException {
        Teacher teacher = new Teacher();
//        方式一:通過對象
        Class aClass = teacher.getClass();
        System.out.println(aClass);
//        方式二:forname
        Class<?> class1 = Class.forName("com.xxx.Teacher");
        System.out.println(class1);
//        比較兩者
        System.out.println(aClass==class1);
//        方式三:通過類名.class獲得
        Class<Teacher> teacherClass = Teacher.class;
        System.out.println(teacherClass);
//        比較
        System.out.println(teacherClass==class1);
//        方式四:基本數據類型的包裝類的type屬性
        Class<Integer> type = Integer.TYPE;
        System.out.println(type);
//        得到父類Class類型
        Class<?> superclass = class1.getSuperclass();
        System.out.println(superclass);
//     補充一下,獲得註解和void和Class本身的Class對象
        // Override.class
        // void.class
        //Class.class

    }
}

class Teacher extends  Person{}

/*
class com.xxx.Teacher
class com.xxx.Teacher
true
class com.xxx.Teacher
true
int
class com.xxx.Person

那些類型可以有Class對象?

包括以下:

註意:數組長度即使不一致,只要類型和維度一樣,就是同一個Class

Class類的方法

獲得類完整結構
public class MyThread {
    private  int a;
    public  void run(String a){}

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public MyThread(int a) {
        this.a = a;
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        Class c1 = Class.forName("com.xxx.MyThread");
        //獲取類名
        System.out.println(c1.getName());
        System.out.println(c1.getSimpleName());
//        獲得類屬性
        Field[] fields = c1.getFields();//只能找到public屬性
        fields = c1.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
//        獲得類方法
        System.out.println("-----------------------");
        Method[] methods = c1.getMethods();//包括繼承的方法
        methods=c1.getDeclaredMethods();//只包括本類的方法
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("-------------------------------");
//        獲得指定方法:沒s,兩個參數分別是方法名和方法參數類型的Class
        System.out.println(c1.getMethod("run",String.class));
//        獲得構造器
        System.out.println("----------------");
        Constructor[] constructors = c1.getConstructors();//獲得public構造器
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
//        同理getDeclaredConstructors()獲得的所有構造器,

        //獲得指定的構造器,參數為構造器參數的Class
        System.out.println("--------------------------");
        Constructor constructor = c1.getConstructor(int.class);
        System.out.println(constructor);
    }
}
/*out:

public void com.xxx.MyThread.run(java.lang.String)
public void com.xxx.MyThread.setA(int)
public int com.xxx.MyThread.getA()
-------------------------------
public void com.xxx.MyThread.run(java.lang.String)
----------------
public com.xxx.MyThread(int)
--------------------------
public com.xxx.MyThread(int)
操作類結構
public class MyThread {
    private  int a;
    public  void run(String a){
        System.out.println(a);
    }

    public int getA() {
        return a;
    }

    public MyThread() {
    }

    public void setA(int a) {
        this.a = a;
    }

    public MyThread(int a) {
        this.a = a;
    }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
        Class<?> aClass = Class.forName("com.xxx.MyThread");
//        構建一個對象:本質是調用無參構造器,其該類必須有無參構造器
//        MyThread myThread = (MyThread)aClass.newInstance();
//        通過構造器創建對象,可以用有參構造函數
//       Constructor constructor= aClass.getDeclaredConstructor(int.class);
//       MyThread myThread1=(MyThread)constructor.newInstance(1);

//       通過反射調用方法
//        通過反射獲取一個方法
        MyThread myThread = (MyThread) aClass.newInstance();
        Method setName = aClass.getMethod("run", String.class);
//        invoke:激活,參數:對象名,方法參數
        setName.invoke(myThread,"helloworld");

//        通過反射操作屬性
        Field a=aClass.getDeclaredField("a");
//        取消安全檢查,否則無法操作私有屬性.method,field,consructor都有它,關閉也可以提高反射效率
        a.setAccessible(true);
        a.set(myThread,5);
        System.out.println(myThread.a);
    }
    
   /*out:
   helloworld
	5

類的載入過程

解釋一下符號引用和直接引用:

符號引用你可以理解為要引用的類目前在記憶體中還沒被載入出來,JVM當然不知道它在哪裡。所以就用唯一的符號來表示就好像給它賦了一個變數,等到鏈接的時候類載入器解析地址完成變成直接引用

直接引用就是存儲引用類在真實記憶體中的地址

總而言之還是這張圖:

什麼時候會發生類的初始化

主動引用比較好理解,下麵只演示一下被動引用“

public class Main {
       //    初始化時執行一次靜態代碼塊
    static {
        System.out.println("main函數所在類被載入了");
    }
    public static void main(String[] args) throws ClassNotFoundException {
       System.out.println(son.sonAge);
       /*
       out:
       main函數所在類被載入了
       父類被載入了
       子類被載入了
       10
        */
//------------------------------------------------------
//        這就是第一條的意思
        System.out.println(son.fatherAge);
        /*
            out:
             main函數所在類被載入了
             父類被載入了
             23
         */
//--------------------------------------------------------
//      第二條:本質上只是開闢了一片空間
        son[] arr = new son[5];
        /*
        out:
        main函數所在類被載入了
         */
//--------------------------------------------------------
        //常量是不會引起初始化的
        System.out.println(son.M);
        /*
        out:
        main函數所在類被載入了
        1
         */
    }
}

class  father {
    static int fatherAge = 23;

    //    初始化時執行一次靜態代碼塊
    static {
        System.out.println("父類被載入了");
    }
}
class son extends father{
    static  int sonAge = 10;
    static  final  int M = 1;
    static {
        System.out.println("子類被載入了");
    }
}

類載入器

  • 類載入的作用:將class文件位元組碼內容載入到記憶體中,並且將這些靜態數據轉換成方法區的運行時數據結構,然後在堆中生成一個代表這個類的java.lang.Class 對象,作為方法區中類數據的訪問入口。簡單來說,類載入器的作用就是把類(class)裝進記憶體
  • 類緩存:標準的JavaSE類載入器可以按照要求查找類,但一旦某個類被載入到類載入器中,它將維持載入(緩存)一段數據。不過JVM垃圾回收機制可以回收這些Class對象

類載入器有那些?

java的主要jar包就是rt.jar,引導類載入器就是載入這一類核心jar包

 public static void main(String[] args) throws ClassNotFoundException {
//        獲取系統類的載入器
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        System.out.println(classLoader);

//        獲取系統類載入器的父類載入器-->拓展載入器
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);
//        獲得拓展類載入器的父類載入器-->根載入器(c/c++):這個java是無法獲得的
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);

        System.out.println("------------------------");

//        測試當前類是那個載入器載入的
        ClassLoader classLoader3 = Class.forName("com.xxx.MyThread").getClassLoader();
        System.out.println(classLoader3);
//        測試jdk內部類是誰載入的:根載入器,輸出不出來
        ClassLoader classLoader4 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader4);

        //如何獲得系統類載入器可以載入的路徑
        System.out.println(System.getProperty("java.class.path"));

    }

/*out:
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
jdk.internal.loader.ClassLoaders$PlatformClassLoader@5594a1b5
null
------------------------
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
null
本處按道理來說應該輸出所有jar包路徑,但是只說出了文件根目錄,可能是因為jdk版本的原因,不確定,本處存疑

反射操作泛型

public class MyThread {
   public void test01(Map<String,MyThread> map, List<MyThread> list){
   }
   public  Map<String,Map> test02(){
       return  null;
   }

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
       //獲得參數泛型
       Method method = MyThread.class.getMethod("test01", Map.class,List.class);
       //得到的是方法的參數Map,List
        Type[] genericExceptionTypes = method.getGenericParameterTypes();
        for (Type genericExceptionType : genericExceptionTypes) {
            if(genericExceptionType instanceof ParameterizedType){
                System.out.println(genericExceptionType);
                //得到的是方法參數的泛型
                Type[] actualTypeArguments = ((ParameterizedType) genericExceptionType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }
        //獲得返回值泛型
        Method method1 = MyThread.class.getMethod("test02");
        Type genericReturnType = method1.getGenericReturnType();
        if(genericReturnType instanceof  ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }

}

/*
java.util.Map<java.lang.String, com.xxx.MyThread>
class java.lang.String
class com.xxx.MyThread
java.util.List<com.xxx.MyThread>
class com.xxx.MyThread
class java.lang.String
interface java.util.Map

反射操作註解

ORM->對象關係映射

public class MyThread {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class<?> aClass = Class.forName("com.xxx.Teacher");
//        通過反射獲得註解
        Annotation[] annotations = aClass.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
//        獲得註解的value值
        Table table = (Table) aClass.getAnnotation(Table.class);
        String value = table.value();
        System.out.println(value);

//        獲得類指定註解
        Field f = aClass.getDeclaredField("id");
        Table annotation = f.getAnnotation(Table.class);
        System.out.println(annotation.value());


    }
}
@Table(value = "aaa")
class Teacher{
    @Table2(value = "aaa")
    private int id;
    @Override
    public String toString() {
        return super.toString();
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String value();
        }
//屬性的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Table2{
    String value();
}

/*
@com.xxx.Table(value="aaa")
aaa

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

-Advertisement-
Play Games
更多相關文章
  • Flutter開發中三棵樹的重要性不言而喻,瞭解其原理有助於我們開發出性能更優的App,此文主要從源碼角度介紹Element樹的管理類BuildOwner。 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 核心 使用CSS變數, 準備兩套CSS顏色, 一套是在 light模式下的顏色,一套是在dark模式下的顏色 dark模式下的 CSS 權重要比 light 模式下的權重高, 不然當我們給html添加自定義屬性[data-theme='d ...
  • # VuePress v2.0 項目創建 參考:[VuePress v2.0 文檔](https://v2.vuepress.vuejs.org/zh/guide/getting-started.html) ### 1.創建文件夾 我創建了一個文件夾,然後在文件夾中打開了powershell ``` ...
  • 有時候在系統中需要一次性下載多個文件,但逐個下載文件比較麻煩。這時候,最好的解決辦法是將所有文件打包成一個壓縮文件,然後下載這個壓縮文件,這樣就可以一次性獲取所有所需的文件了。 下麵是一個名為CompressUtil的工具類的代碼,它提供了一些方法來處理文件壓縮和下載操作: ```java impo ...
  • 在寫這篇文章的時候,查看了下electron最新穩定版本由幾天前24.4.0升級到了25了,不得不說electron團隊迭代速度之快! 前幾天有分享一篇electron24整合vite4全家桶技術構建桌面端vue3應用示常式序。 https://www.cnblogs.com/xiaoyan2017 ...
  • JavaScript的事件監聽是一種機制,用於在HTML文檔中的元素上註冊事件處理程式,以便在特定事件發生時執行相應的JavaScript代碼。 事件監聽的基本思想是將事件處理程式(也稱為事件回調函數)綁定到特定的事件上。所謂特定的事件是當用戶與頁面進行交互時,比如點擊按鈕、鍵盤輸入、滑鼠移動等,瀏 ...
  • 技術架構師,將整間企業的IT開發流程至維運管理,視為一個大型系統進行規劃。並分為四個面向進行發展: - [開發平臺]:構建高度重用的共用模組和服務,並在多個專案項目和應用系統中使用,以提高開發效率並降低維護成本。 - [DevOps平臺]:建構連續集成、連續交付的工作環境,將開發與維運團隊更緊密地連 ...
  • 在Java中,可以使用以下方法中斷線程: 1. 使用`interrupt()`方法:每個線程對象都有一個`interrupt()`方法,用於中斷該線程。當調用線程的`interrupt()`方法時,它會設置線程的中斷狀態為"中斷",但並不會立即停止線程的執行。線程在執行過程中可以通過檢查中斷狀態來決 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...