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
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...