Java基礎--註解、反射

来源:https://www.cnblogs.com/l-y-h/archive/2019/06/30/11111539.html
-Advertisement-
Play Games

一、註解(Annotation) 1、什麼是註解? 從JDK5開始,Java增加了Annotation(註解),Annotation是代碼里的特殊標記,這些標記可以在編譯、類載入、運行時被讀取,並執行相應的處理。 2、Annotation與註釋的區別: (1)Annotation不是程式本身,可以對 ...


一、註解(Annotation)

1、什麼是註解?

  從JDK5開始,Java增加了Annotation(註解),Annotation是代碼里的特殊標記,這些標記可以在編譯、類載入、運行時被讀取,並執行相應的處理。

2、Annotation與註釋的區別:

  (1)Annotation不是程式本身,可以對程式進行解釋,此處可以理解為註釋。但是Annotation可以被其他程式(比如編譯器)讀取,併進行處理。
  (2)註解與註釋最大的區別就是註解存在被處理的流程,即註解是會被程式處理的。

3、註解的格式:

  (1)以 “@註釋名” 的形式在代碼中存在。
  (2)註解可以附加在程式元素( 包、類、構造器、方法、成員變數、參數、局域變數 )上面,為其添加額外的輔助信息,可以通過反射機制訪問這些數據。
  (3)Annotation不能運行,其只有成員變數,沒有方法。Annotation與public、final等修飾符地位類似,屬於程式元素的一部分,但不能作為程式元素使用。

4、常見註解:

  (1)@Override
    定義在java.lang.Override中,此註釋只用於修飾方法,表示重寫一個父類的方法。

【舉例:】
 @Override
 public String toString() {
     return "Hello";
}

  (2)@Deprecated

    定義在java.land.Deprecated中,此註釋可用於修飾方法、屬性、類,表示該方法、類、屬性不推薦使用(廢棄)。在方法、類、屬性上會有一條刪除線(形如toString())。

【舉例:】
@Deprecated
public String toString() {
    return "TimerTaskDemo []";
}

  (3)@SuppressWarnings
    定義在java.lang.SuppressWarnings中,用來阻止編譯時的警告信息。其使用時需要設置參數。

【參數為:】
deprecation,使用了過時的類或方法的警告。
unchecked,執行了未檢查的轉換時的異常,比如集合未指定泛型。
fallthrough,當在switch語句發生case穿透時的警告。
path,當類路徑、源文件路徑不存在時的警告。
serial,可序列化類缺少serialVersionUID時的警告。
finally,任何finally不能完成時的警告。
all,以上所有警告。

【格式:】
@SuppressWarnings("all")
或者
@SuppressWarnings(value = { "serial", "unchecked" })

 

5、元註解:

  (1)元註解的作用就是負責註解其他的註解。Java5.0定義了4個標準的meta-annotation類型,它們被用來提供對其它 annotation類型作說明。存在於java.lang.annotation中。

  (2)元註解分類:
    @Target
    @Retention
    @Documented
    @Inherited

  (3)@Target元註解:
    用於描述註解的使用範圍。Annotation可被用於 packages、types(類、介面、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變數、枚舉值)、方法參數和本地變數(如迴圈變數、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。

【格式:】
public @interface Target
{
  ElementType[] value();
}

【參數:ElementType】
CONSTRUCTOR:用於描述構造器
FIELD:用於描述成員變數
LOCAL_VARIABLE:用於描述局部變數
METHOD:用於描述方法
PACKAGE:用於描述包
PARAMETER:用於描述參數
TYPE:用於描述類、介面(包括註解類型) 或enum聲明

【舉例:】
@Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE})

  (4)@Retention元註解:
    用於描述註解的聲明周期。某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請註意並不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。

【格式:】
public @interface Retention
{
  RetentionPolicy value();
}

【參數:RetentionPoicy】
SOURCE:在源文件中有效(即源文件保留)
CLASS:在class文件中有效(即class保留)
RUNTIME:在運行時有效(即運行時保留,可以通過反射機制讀取)

【舉例:】
@Retention(RetentionPolicy.SOURCE)

  (5)@Documented元註解:
    用於描述其它類型的annotation應該被作為被標註的程式成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記註解,沒有成員。

  (6)@Inherited元註解:
    @Inherited 元註解是一個標記註解,@Inherited闡述了某個被標註的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。
註意:
    @Inherited annotation類型是被標註過的class的子類所繼承。類並不從它所實現的介面繼承annotation,方法並不從它所重載的方法繼承annotation。
    當@Inherited annotation類型標註的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation類型被髮現,或者到達類繼承結構的頂層。

 

6、自定義註解:

  (1)自定義註解時,需要使用@interface用來聲明一個註解,其會自動繼承java.lang.annotation.Annotation介面。

【格式:】
public @interface 註解名 {定義體}
【或者:】
public @interface 註解名 {
  類型 value() default 預設值;   //這裡是參數,不是抽象方法。
}

其中定義體實質是聲明瞭一個配置參數(註:此處不是抽象方法)。
1、方法名指的是參數名。
2、返回值類型指的是參數的類型(只能為基本類型、Class、String、enum、Annotation類型、以及以上所有類型的數組)。
3、可以通過default來聲明參數的預設值。
4、如果只有一個參數,那麼參數名(方法名)一般為value。
5、只能使用public, default兩個許可權修飾符。  

  (2)方法:

判斷類或者方法是否有註解
    boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

獲得註解對象
    <A extends Annotation> A getAnnotation(Class<A> annotationClass)  //獲取指定註解
    Annotation[] getAnnotations()  //獲取當前元素上的所有註解
【舉例:】
package com.test;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldDemo {
    String columnName() default "";

    String type() default "";

    int length() default 10;
}

@Table("student")
class Student {
    @FieldDemo(columnName = "id", length = 10, type = "int")
    private int id;

    @FieldDemo(columnName = "name", length = 20, type = "varchar")
    private String name;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

public class AnnotationDemo {
    public static void main(String[] args) {
        try {
            // 獲取Student類的信息
            Class classDemo = Class.forName("com.test.Student");
            // Class<Student> classDemo = (Class<Student>)Class.forName("com.test.Student");
            System.out.println(classDemo); // 輸出class com.test.Student

            // 獲取當前元素上的所有註解,此時獲取的是@Table
            Annotation[] annotations = classDemo.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            } // 輸出@com.test.Table(value=student)

            // 直接獲取指定的某註解
            Table table = (Table) classDemo.getAnnotation(Table.class);
            System.out.println(table.value()); // 輸出student

            // 獲取類的屬性的註解
            Field field = classDemo.getDeclaredField("name");
            // 獲取指定註解
            FieldDemo fieldDemo = field.getAnnotation(FieldDemo.class);
            // 輸出name varchar 20
            System.out.println(fieldDemo.columnName() + " " + fieldDemo.type() + " " + fieldDemo.length());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

二、反射機制

1、什麼是反射?

  JAVA反射機制指的是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;即這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。
  JAVA中與反射相關的類,放在java.lang.reflect包中。

2、Class類

  Class對象包含了一個類的完整的結構信息。通過Class對象,可以對類進行操作,即為反射。
  (1)規則:
    Class類擁有泛型作為定義。
    Class 類的實例表示正在運行的 Java 應用程式中的類和介面。
    Class類沒有public構造方法。
    Class對象是在載入類時由Java 虛擬機以及通過調用類載入器中的defineClass 方法自動構造的。
    一個類只有一個Class對象。

  (2)內置class實例(class對象):

byte.classshort.classint.classlong.classchar.classfloat.classdouble.classboolean.classvoid.class.
註:
int.class != Integer.classint.class == Integer.Type  。

  (3)對於數組類型class實例:
    每個數組屬於被映射為 Class 對象的一個類,所有具有相同元素類型和維數的數組都共用該Class 對象。

int[] a = new int[100];
int[] b = new int[10];
long[] c = new long[10];
int[][] d = new int[10][2];
System.out.println(a.getClass());//輸出class [I
System.out.println(b.getClass());//輸出class [I
System.out.println(c.getClass());//輸出class [J
System.out.println(d.getClass());//輸出class [[I
System.out.println(a.getClass() == b.getClass());//輸出true

  (4)獲取Class實例的方法:

【方法1:】
根據傳入的參數動態裝載一個類,並且做類的初始化。
    Class.forName()方法

【方法2:】
獲得對象運行時所指的真正對象(多態的場合返回子類的類名)。
    Class.getClass() 方法

【方法3:】
JVM將使用類A的類裝載器,將類A裝入記憶體(前提是:類A還沒有裝入記憶體),不對類A做類的初始化工作.返回類A的Class的對象。
    A.class屬性

  (5)通過Class實例創建對象:

Class.newInstance()方法 。調用預設構造函數,獲得一個實例

Class.newInstance方法與new的區別
newInstance: 弱類型。低效率。只能調用無參構造。
new:強類型。相對高效。能調用任何public構造。

  (6)常用方法:

【獲得構造器:】
Constructor<T> getDeclaredConstructor(Class<?>...)  獲得指定構造方法
Constructor<?>[] getDeclaredConstructors()    獲得所有構造方法(聲明順序)
Constructor<T> getConstructor(Class<?>...)    獲得許可權為public的指定構造方法
Constructor<?>[] getConstructors()    獲得許可權為public的所有構造方法

【獲得普通方法(成員方法):】
Method[] getDeclaredMethods()   獲得該類中定義的所有方法(不包含父類繼承)
Method getDeclaredMethod(String name, Class<?>... parameterTypes)    根據該類中定義的指定方法(不包含父類繼承)
Method[] getMethods()  獲得許可權為public的所有的方法 (包含父類繼承)
Method getMethod(String name, Class<?>... parameterTypes)    獲得許可權為public的指定的方法 (包含父類繼承)

【獲得屬性(成員變數):】
Field[] getDeclaredFields()   獲得該類中定義的所有屬性(不包含繼承)
Field getDeclaredField(String name)獲得該類中定義的指定屬性(不包含繼承)
Field[] getFields()  獲得該類中所有public的屬性(包含繼承)
Field getField (String name)  獲得該類中指定的public屬性(包含繼承)

【獲得內部類:】
Class<?>[] getDeclaredClasses()  獲得所有內部類 (不包含繼承)
Class<?>[] getClasses()   獲得所有許可權為public的內部類(包含繼承)

【其他:】
Package getPackage()   獲得Package對象
String getName()     獲得類的全稱,即包名+類名
String getSimpleName()  獲得類的簡稱,即類名
Class<? super T> getSuperclass()  獲得繼承的類
Class<?>[] getInterfaces()  獲得實現的介面

  (7)獲得構造器後,可以進行的操作

  (8)獲得成員方法後,可以進行的操作

  (9)獲得成員變數後,可以進行的操作

package com.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

class Teacher {
    private String name;
    private int age;

    public Teacher() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

public class ReflectionDemo {
    public static void main(String[] args) {
        try {
            // 載入Teacher.class對象
            Class<Teacher> teacherClass = (Class<Teacher>) Class.forName("com.test.Teacher");

            // 獲取無參構造器,若Teacher類沒有無參構造方法,則會報錯
            Teacher teacher = teacherClass.newInstance();
            System.out.println(teacher + ", " + teacher.getName() + ", " + teacher.getAge());

            // 獲取有參構造器
            Constructor<Teacher> constructor = teacherClass.getDeclaredConstructor(String.class, int.class);
            Teacher teacher2 = constructor.newInstance("tom", 20);
            System.out.println(teacher2 + ", " + teacher2.getName() + ", " + teacher2.getAge());

            // 獲取成員方法
            Teacher teacher3 = teacherClass.newInstance();
            Method method = teacherClass.getDeclaredMethod("setAge", int.class);
            method.invoke(teacher3, 30);
            System.out.println(teacher3.getAge());

            Method method2 = teacherClass.getDeclaredMethod("getAge");
            System.out.println(method2.invoke(teacher3));

            // 獲取成員變數
            Teacher teacher4 = teacherClass.newInstance();
            Field field = teacherClass.getDeclaredField("age");
            field.setAccessible(true);// 忽略安全檢查,可以獲取private類型的數據,破壞封裝性。
            System.out.println(field.get(teacher4));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【結果:】
com.test.Teacher@2a139a55, null, 0
com.test.Teacher@15db9742, tom, 20
30
30
0

3、反射機制性能問題

  setAccessible,是啟用和禁用安全檢查的開關,其值為true時,表示禁用Java語言訪問的安全性檢查,為false時,表示啟用安全性檢查,將其值設為true,可以提高反射的效率。

 

未完待續。。。。

 


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

-Advertisement-
Play Games
更多相關文章
  • Python反射和內置方法(雙下方法) 一、反射 1. 什麼是反射 反射的概念是由Smith在1982年首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式語言的設計領域所採用,併在Lisp和麵向對象 ...
  • 依賴註入的概念 當一個對象要調用另一個對象時,一般是new一個被調用的對象,示例: class A{ private B b=new B(); public void test(){ b.say(); } } A類的對象依賴於B類對象,如果沒有B類對象,A類對象就不能正常工作,稱A依賴於B。 以上方 ...
  • 外觀模式:為子系統中的一組介面提供一個一致的界面,該模式定義了一個高層介面,這個介面使這個子系統更加容易使用。 我們先看一個使用外觀模式的例子,首先定義了三個子系統類 使用外觀模式定義了一個高層介面,這個介面定義了調用子系統的方法 介面調用子系統的具體實現 使用外觀模式的調用方法 執行結果: met ...
  • 上一篇學習了多線程的一些基礎知識:多線程的基本概念,及創建和操作多線程。內容相對簡單,但多線程的知識肯定不會這麼簡單,否則我們也不需要花這麼多心思去學習,因為多線程中容易出現線程安全問題。 那麼什麼是線程安全呢,定義如下: 當多個線程訪問同一個對象時,如果不用考慮這些線程在運行時環境下的調度和交替運 ...
  • django 中間件 [TOC] 自定義中間件 中間件可以定義五個方法,分別是:(主要的是process_request和process_response) process_request(self,request) process_view(self, request, view_func, vi ...
  • MyBatis 類型處理器(typeHandlers)簡介及源碼分析 ...
  • 新聞 "逐漸演化的.NET Core框架" "Visual Studio提示與技巧" "Windows Termina(預覽)" "Microsoft在GitHub上的工程師從2000名增加至25000名" 視頻及幻燈片 "實用IRL" "Visual Studio的F 工具解析" "事件溯源DIY ...
  • 1)ThreadLocal如何回收value,什麼時候回收?從ThreadLocal中的內部類分析:① ThreadLocalMap是一個定製的哈希映射,僅適用於維護線程本地值。為了幫助處理非常大和長期使用的用法,哈希表條目使用weakreferences作為鍵。但是,由於不使用引用隊列,因此只有當 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...