註解和反射學習筆記

qian_qh發表於2020-12-09

註解和反射

本文章參考https://www.bilibili.com/video/BV1p4411P7V3

進行學習和記筆記

註解

什麼是註解

Annotation是從JDK5.0開始引|入的新技術.

Annotation的作用:

  • 不是程式本身,可以對程式作出解釋.(這一點和註釋(comment)沒什麼區別)
  • 可以被其他程式(比如:編譯器等)讀取.

Annotation的格式:

  • 註解是以"@註釋名"在程式碼中存在的, 還可以新增一些引數值,例
    如:@SuppressWarnings(value=“unchecked”).

◆Annotation在哪裡使用?

  • 可以附加在package , class , method , field等上面,相當於給他們新增了額外的輔助資訊,
    我們可以通過反射機制程式設計實現對這些後設資料的訪問
//什麼是註解
public class Test {

    //  @Override 重寫的註解
    @Override
    public String toString() {
        return super.toString();
    }
}

內建註解

  • @Override :定義在java.lang.Override中,此註釋只適用於修辭方法,表示一個方法宣告打算
    重寫超類中的另一個方法宣告.

  • @Deprecated :定義在java.lang.Deprecated中,此註釋可以用於修辭方法,屬性,類,表示不
    鼓勵程式設計師使用這樣的元素,通常是因為它很危險或者存在更好的選擇.

  • @SuppressWarnings :定義在java.lang.SuppressWarnings中,用來抑制編譯時的警告資訊.
    與前兩個註釋有所不同,你需要新增一個引數才能正確使用,這些引數都是已經定義好了的,
    我們選擇性的使用就好了.
    @SuppressWarningsl"all")
    @SuppressWarnings(“unchecked”)
    @SuppressWarnings(value={“unchecked”,“deprecation”})

    等等

//什麼是註解
public class Test {

    //  @Override 重寫的註解
    @Override
    public String toString() {
        return super.toString();
    }

    // @Deprecated 不推薦程式設計師使用,但是可以使用,或者存在更好的
    @Deprecated
    public void test(){

    }

    //鎮壓警告
    @SuppressWarnings("all")
    public void test02(){
        List list = new ArrayList();
    }
    
}

元註解

  • 元註解的作用就是負責註解其他註解, Java定義了4個標準的meta-annotation型別,他們被用來
    提供對其他annotation型別作說明.
  • 這些型別和它們所支援的類在java.lang.annotation包中可以找到.( @Target , @Retention,
    @Documented , @Inherited )
    • @Target :用於描述註解的使用範圍(即:被描述的註解可以用在什麼地方)
    • @Retention :表示需要在什麼級別儲存該註釋資訊,用於描述註解的生命週期
      (SOURCE < CL ASS < RUNTIME)
    • @Document:說明該註解將被包含在javadoc中
    • @Inherited: 說明子類可以繼承父類中的該註解
//定義一個註解
//@Target 表示註解可以用在哪些地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})

//@Retention 表示註解在什麼地方還有效
// runtime>class>source
@Retention(value = RetentionPolicy.RUNTIME)

//@Documented 表示是否將註解生成在javadoc中
@Documented

//@Inherited 子類可以繼承父類的註解
@Inherited
public @interface Myannotation {

}
//測試註解

@Myannotation
public class Test {

    @Myannotation
    public void test(){
    }
}

自定義註解

使用@interface自定義註解時,自動繼承了java.lang .annotatlon.Annotation介面
分析:
@ interface用來宣告一個註解,格式: public @ interface註解名{定義內容}
其中的每一個方法實際上是宣告瞭一個配置引數.
方法的名稱就是引數的名稱.
返回值型別就是引數的型別(返回值只能是基本型別,Class , String , enum ).
可以通過default來宣告引數的預設值
如果只有一個引數成員, 一般引數名為value
註解元素必須要有值,我們定義註解元素時,經常使用空字串,0作為預設值.


//自定義註解
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    //註解的引數:型別+引數名 (); 可以加預設值
    String name() default "";
    int age() default 0;
    int id() default 1; //如果預設值為 -1 表示不存在

    String[] value() default {"張三","lisi"};
}
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation01 {

    String value();
}
@MyAnnotation
public class Test {

    //註解可以顯示賦值,如果沒有預設值,我們就必須給註解賦值
    @MyAnnotation
    public void test(){

    }
    //註解只有有個引數,且為value 可以省略不寫引數名
    @MyAnnotation01("")
    public void test2(){
    }
}

反射

動態語言

  • 是一類在執行時可以改變其結構的語言:例如新的函式、物件、甚至程式碼可以被
    引進,已有的函式可以被刪除或是其他結構上的變化。通俗點說就是在執行時代
    碼可以根據某些條件改變自身結構。
  • 主要動態語言: Object-C. C#、 JavaScript. PHP、 Python等。

靜態語言

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

Reflection (反射)

是Java被視為動態語言的關鍵,反射機制允許程式在執行期藉助於Reflection API取得任何類的內部資訊,並能直接操作任意物件的內部屬性及方法。

Class C = Class. forName(java.lang.String")

載入完類之後,在堆記憶體的方法區中就產生了一個Class型別的物件(一個類只有一個Class物件),這個物件就包含了完整的類的結構資訊。我們可以通過這個物件看到類的結構。這個物件就像一面鏡子, 透過這個鏡子看到類的結構,所以,我們形象的稱之為:反射
正常方式:引入 需要的"包類”名稱一之通過new例項化取 得例項化物件
演示二
反射方式:例項化物件——>getClass()方法 ——>得到完整的 “包類”名稱

Java反射機制提供的功能

在執行時判斷任意一個物件所屬的類
在執行時構造任意一個類的物件丫
在執行時判斷任意一個類所具有的成員變數和方法
在執行時獲取泛型資訊
在執行時呼叫任意一個物件的成員變數和方法
在執行時處理註解
生成動態代理

反射優點和缺點

優點:

​ 可以實現動態建立物件和編譯,體現出很大的靈活性

缺點:

​ 對效能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且

​ 它滿足我們的要求’這類操作總是慢於直接執行相同的操作。


Class類

在Object類中定義了以下的方法,此方法將被所有子類繼承

public final Class getClass()

以上的方法返回值的型別是一個Class 類,此類是Java反射的源頭,實際上所謂反射從程式的執行結果來看也很好理解,即:可以通過物件反射求出類的名稱。

在這裡插入圖片描述

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

  1. Class本身也是一 個類
  2. Class 物件只能由系統建立物件
  3. 一個載入的類在JVM中只會有一個Class例項
  4. 一個Class物件對應的是一個載入到JVM中的一 個.class檔案
  5. 每個類的例項都會記得自己是由哪個Class例項所生成
  6. 通過Class可以完整地得到一個類中的所有被載入的結構
  7. Class類是Reflection的根源,針對任何你想動態載入、執行的類,唯有先獲得相應的Class物件

Class類的常用方法

方法名功能說明
static Class.forName(String name)返回指定類名name的Class物件
Object newlnstance()呼叫預設建構函式,返回Class物件的一個例項
String getName()返回此Class物件所表示的實體(類,介面,陣列類或void)的名稱。
Class getSuperClass()返回當前Class物件的父類的Class物件
Class[] getInterfaces()獲取當前Class物件的介面
ClassLoader getClassLoader()返回該類的類載入器
Constructor[] getConstructors()返回一個包含某些Constructor物件的陣列
Method getMothed(String name,Class… T)返回一個Method物件,此物件的形參型別為paramType
Field[] getDeclaredFields()返回Field物件的一一個陣列

獲取Class類的例項

  1. 若已知具體的類,通過類的class屬性獲取,該方法最為安全可靠,程式效能最高。
Class clazz = Person.class;
  1. 已知某個類的例項, 呼叫該例項的getClass()方法獲取Class物件
Class clazz = person.getClass();
  1. 已知一 個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()獲取,
    可能丟擲ClassNotFoundException
Class clazz = Class.forName("demo01 .Student");
  1. 內建基本資料型別可以直接用類名.Type

  2. 還可以利用ClassLoader

//class類的建立方式,
public class Test {

    public static void main(String[] args) throws ClassNotFoundException {

        Person person = new Student();
        System.out.println("我是一個:"+person.name);

        //Class物件只有一個
        //方式一:通過物件獲得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

        //方式二:forName獲得
        Class c2 = Class.forName("com.qh.reflection.demo02.Student");
        System.out.println(c2.hashCode());

        //方式三:通過類名.class獲得
        Class<Student> c3 = Student.class;
        System.out.println(c3.hashCode());

        //方式四:基本內建型別的包裝類都有一個Type屬性
        Class<Integer> c4 = Integer.TYPE;
        System.out.println(c4); //int

        //獲取父類的型別
        Class c5 = c1.getSuperclass();
        System.out.println(c5);

        //獲取該物件的類路徑
        System.out.println(c1.getName());

        //返回該類的類載入器
        System.out.println(c1.getClassLoader());
    }
}

哪些型別可以有Class物件

class:外部類,成員(成員內部類,靜態內部類),區域性內部類(匿名內部類。
interface:介面
[]:陣列
enum:列舉
annotation: )註解@interface
primitive type:基本資料型別
void

import java.lang.annotation.ElementType;

//所有型別的Class
public class Test {

    public static void main(String[] args) {

        Class c1 = Object.class; //類
        Class c2 = Comparable.class; //介面
        Class c3 = String[].class; //一維陣列
        Class c4 = int[][].class; //二維陣列
        Class c5 = Override.class; //註解
        Class c6 = ElementType.class; //列舉
        Class c7 = Integer.class; //基本資料型別
        Class c8 = void.class; //void
        Class c9 = Class.class; //Class

        //快捷鍵,按住Alt用滑鼠可以多行選中
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);

        //只要元素型別和維度一樣,就是同一個Class
        int[] a = new int[10];
        int[] b = new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());

    }
}

類載入過程

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

在這裡插入圖片描述

類的載入與ClassLoader的理解

載入:將class檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉換成方法區的執行時資料 結構, 然 後生成一個代表這個類的java.lang.Class物件.

連結: 將Java類的二進位制程式碼合併到JVM的執行狀態之中的過程。

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

初始化:

  • 執行類構造器< clinit> ()方法的過程。類構造器< clinit> ()方法是由編譯期自動收集類中所有類變數的賦值動作和靜態
  • 程式碼塊中的語句合併產生的。(類構造器是構造類資訊的,不是構造該類物件的構造器)。
  • 當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化。
  • 虛擬機器會保證一個類的 ()方法在多執行緒環境中被正確加鎖和同步。

在這裡插入圖片描述

/**
 * 分析:
 * 1.載入到記憶體,會產生一個類對應的Class物件
 * 2.連結,連結結束後 m=0;
 * 3.初始化:
 *      <clinit>(){
 *           System.out.println("A類靜態程式碼塊初始化");
 *           m=300;
 *           m=100;
 *      }
 *      m=100;
 */
public class A {

    static {
        System.out.println("A類靜態程式碼塊初始化");
        m=300;
    }

    /*m = 300
      m = 100
    */

    static int m=100;

    public A(){
        System.out.println("A類的無參建構函式初始化");
    }
}
//類載入記憶體分析
public class Test {

    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.m);
    }
}

結果

A類靜態程式碼塊初始化
A類的無參建構函式初始化
100

分析類的初始化

類的主動引用(一定會發生類的初始化)
1.當虛擬機器啟動,先初始化main方法所在的類
2.new一個類的物件
3.呼叫類的靜態成員(除了final常量)和靜態方法.
4.使用java.lang.reflect包的方法對類進行反射呼叫
5.當初始化一個類, 如果其父類沒有被初始化,則先會初始化它的父類

類的被動引用(不會發生類的初始化)
1.當訪問一個靜態域時,只有真正宣告這個域的類才會被初始化。如:當通過子類引用父類 的靜態變數,不會導致子類初始化
2.通過陣列定義類引用,不會觸發此類的初始化
3.引用常量不會觸發 此類的初始化(常量在連結階段就存入呼叫類的常量池中了)

public class Test {

    static{
        System.out.println("Main類被載入");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        //1.主動引用
        //Son son = new Son();

        //反射也會產生主動引用
        //Class.forName("com.qh.reflection.demo05.Son");

        //不會產生類的引用的方法,通過子類引用父類的靜態變數
        //System.out.println(Son.b);

        //陣列定義類
        //Son[] array = new Son[5];

        //引用常量
        System.out.println(Son.M);

    }
}

類載入器的作用

  • 類載入的作用:將class檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉換成方法區的執行時資料結構,然後在堆中生成一個代表這個類的java.lang.Class物件作為方法區中類資料的訪問入口。
  • 類快取:標準的JavaSE類載入器可以按要求查詢類,但一旦某個類被載入到類載入器中,它將維持載入(快取) 一段時間。 不過JVM垃圾回收機制可以回收這些Class物件
    在這裡插入圖片描述

類載入器的作用是用來把類(class)裝在進記憶體的。JVM規範定義瞭如下型別的類載入器

  • 引導類載入器:用C++編寫的,是JVM自帶的類載入器負責Java平臺核心庫,用來裝載核心類庫。該載入器無法直接獲取
  • 擴充套件類載入器:負責jre/lib/ext目錄下的jar包或 -D java.ext.dirs指定日錄下的jar包裝入工作庫
  • 系統類載入器:負責java - classpath或 -D java.class.path所指的目錄下的類與jar包裝入工作,是最常用的載入器

java的核心庫:rt.jar

public class Test {

    public static void main(String[] args) throws ClassNotFoundException {

        //獲取系統類的載入器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //獲取系統類的載入器的父類載入器--->擴充套件類載入器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        //獲取擴張類載入器的父類載入器--->根載入器(c/c++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1); //null,無法直接獲取

        //測試當前類是哪個載入器載入的
        ClassLoader classLoader = Class.forName("com.qh.reflection.demo06.Test").getClassLoader();
        System.out.println(classLoader);

        //測試JDK內建類是哪個載入器載入的
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);// null

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

        //雙親委派機制
    }
}

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

當某個類載入器需要載入某個.class檔案時,它首先把這個任務委託給他的上級類載入器,遞迴這個操作,如果上級的類載入器沒有載入,自己才會去載入這個類。

獲取執行時類的完整結構

通過反射獲取執行時類的完整結構
Field、Method, Constructor、 Superclass、 Interface、Annotation

//實體類
public class User {

    private String name;
    private int id;
    private int age;

    public User() {
    }

    public User(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;
    }


    private void show(){

    }

    private User(String name, int id) {
        this.name = name;
        this.id = id;
    }
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

//獲得類的資訊
public class Test {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        //獲取User的Class物件
        Class c1 = Class.forName("com.qh.reflection.demo07.User");

        //獲取類名
        String name = c1.getName(); //獲取 包名+類名
        String simpleName = c1.getSimpleName(); //獲取 類名
        System.out.println(simpleName);
        System.out.println(name);

        //獲取類中的屬性
        Field[] fields = c1.getFields(); //只能獲取public 屬性

        Field[] declaredFields = c1.getDeclaredFields(); //能獲取所有的屬性
        for(Field f : declaredFields){
            System.out.println(f);
        }

        //獲取指定屬性的值
        Field name1 = c1.getDeclaredField("name");
        System.out.println(name1);

        //獲取類的方法
        System.out.println("==============================");
        Method[] methods = c1.getMethods(); //獲取本類及其父類的所有public方法
        for(Method method: methods){
            System.out.println("-->"+method);
        }
        Method[] declaredMethods = c1.getDeclaredMethods(); //獲取本類的所有方法
        for(Method method : declaredMethods){
            System.out.println(method);
        }

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

        //獲取指定的方法,第二個引數為引數型別的.class,沒有則為null
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

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

        //獲得類的構造器
        Constructor[] constructors = c1.getConstructors();// 獲得public構造器
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }

        constructors = c1.getDeclaredConstructors();
        for (Constructor constructor : constructors) { //獲得所有構造器
            System.out.println("##"+constructor);
        }

        //獲取指定構造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println("指定:"+declaredConstructor);

    }
}

在實際的操作中,取得類的資訊的操作程式碼並不會經常開發。
一定要熟悉java.lang.reflect包的作用,反射機制。
如何取得屬性、方法、構造器的名稱,修飾符等。

有了Class物件,能做什麼

建立類的物件:呼叫Class物件的newInstance()方法

  1. 類必須有一-個無引數的構造器
  2. 類的構造器的訪問許可權需要足夠
    思考? 難道沒有無參的構造器就不能建立物件了嗎?只要在操作的時候明確的呼叫類中的構造器,
    並將引數傳遞進去之後,才可以例項化操作。
    步驟如下:
    1)通過Class類的getDeclaredConstructor(Class … parameterTypes)取得本類的指定形參型別的構造器
    2)向構造器的形參中傳遞一個物件陣列進去,裡面包含了構造器中所需的各個引數。
    3)通過Constructor例項化物件

呼叫指定的方法
通過反射,呼叫類中的方法,通過Method類完成。
①通過Class類的getMethod(String name,Cas…parameterTypes)方法取得
一個Method物件,並設定此方法操作時所需要的引數型別。
②之後使用Object invgke(Object obj, Object[] args)進行呼叫,並向方法中傳
遞要設定的obj物件的引數資訊。

在這裡插入圖片描述

setAccessible

  • Method和Field、 Constructor物件都有setAccessible方法。

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

  • 引數值為true則指示反射的物件在使用時應該取消Java語言訪問檢查。

    1. 提高反射的效率。如果程式碼中必須用反射,而該句程式碼需要頻繁的被呼叫,那麼請設定為true。

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

  • 引數值為false則指示反射的物件應該實施Java語言訪問檢查

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

//動態建立物件,通過反射
public class Test {

    public static void main(String[] args) throws Exception {

        //獲得Class物件
        Class c1 = Class.forName("com.qh.reflection.demo08.User");

        //建立一個物件
        //User user = (User)c1.newInstance();//本質上呼叫了類的無參構造器
        //System.out.println(user);

        //通過構造器建立物件
        //Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        //User jack =(User) constructor.newInstance("jack", 001, 20);
        //System.out.println(jack);

        //通過反射呼叫普通方法
        User user2 = (User)c1.newInstance();
        //通過反射獲取一個方法
        Method setName = c1.getMethod("setName", String.class);

        //invoke 啟用的意思
        //(物件,“方法的值”)
        //把這個值賦值給哪個物件;
        setName.invoke(user2,"jack");
        System.out.println(user2.getName());

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

        //通過反射操作屬性
        User user3 = (User)c1.newInstance();
        //獲取屬性
        Field name = c1.getDeclaredField("name");

        //不能直接操作私有屬性,我們需要關閉程式的安全檢測,屬性或方法的setAccessible(true);
        name.setAccessible(true); //不加,許可權不足
        name.set(user3,"jack2");
        System.out.println(user3.getName());

    }
}

效能對比分析

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//分析效能問題
public class Test {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Test01();
        Test02();
        Test03();
    }

    //普通方式呼叫
    public static void Test01(){
        User user = new User();
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 100000000; i++) {
            user.getName();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("普通方式執行10億次"+(endTime-startTime)+"ms");
    }

    //反射方式呼叫
    public static void Test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();

        //獲取方法
        Method getName = c1.getMethod("getName", null);
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 100000000; i++) {
           getName.invoke(user,null);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("反射方式執行10億次"+(endTime-startTime)+"ms");
    }

    //反射方式呼叫,關閉檢測
    public static void Test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();

        //獲取方法
        Method getName = c1.getMethod("getName", null);
        getName.setAccessible(true);//關閉檢測
        
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 100000000; i++) {
            getName.invoke(user,null);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("關閉檢測執行10億次"+(endTime-startTime)+"ms");
    }

}

反射操作泛型

  • Java採用泛型擦除的機制來引|入泛型, Java中的泛型僅僅是給編譯器javac使用的,確保資料的安全性和免去強制型別轉換問題,但是,一旦編譯完成,所有和泛型有關的型別全部擦除
  • 為了通過反射操作這些型別, Java新增了ParameterizedType, GenericArrayType,
    TypeVariable和WildcardType幾種型別來代表不能被歸一到Class類中的型別但是又和原始型別齊名的型別.
  • ParameterizedType :表示一種引數化型別,比如Collection
  • GenericArrayType :表示一種元素型別是引數化型別或者型別變數的陣列
  • TypeVariable :是各種型別變數的公共父介面
  • WildcardType :代表一種萬用字元型別表示式
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class Test {

    public void test01(Map<String,User> map , List<User> list){
        System.out.println("test01");
    }

    public Map<String,User> test02(){
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {

        //獲取方法
        Method method = Test.class.getMethod("test01", Map.class, List.class);
        //獲取泛型的引數資訊
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("#"+genericParameterType);
            //如果泛型的引數型別屬於引數化型別
            if(genericParameterType instanceof ParameterizedType){
                //獲得真實的引數資訊
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }

        method = Test.class.getMethod("test02", null);
        //獲取返回值型別
        Type genericReturnType = method.getGenericReturnType();

        if(genericReturnType instanceof ParameterizedType){
            //獲得真實的引數資訊
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("--"+actualTypeArgument);
            }
        }

    }
}

反射操作註解

瞭解什麼是ORM?

​ Object relationship Mapping —> 物件關係對映

在這裡插入圖片描述

  • 類和表結構對應
  • 屬性和欄位對應
  • 物件和記錄對應

要求:利用註解和反射完成類和表結構的對映關係

import java.lang.annotation.*;
import java.lang.reflect.Field;

//練習反射操作註解
public class Test {

    public static void main(String[] args) throws NoSuchFieldException {
        Class c1 = Student.class;

        //通過反射獲得註解
        Annotation[] declaredAnnotations = c1.getDeclaredAnnotations();
        for (Annotation declaredAnnotation : declaredAnnotations) {
            System.out.println(declaredAnnotation);
        }

        //獲得註解的value的值
        TabStudent tabStudent =(TabStudent) c1.getDeclaredAnnotation(TabStudent.class);
        String value = tabStudent.value();
        System.out.println(value);

        //獲得屬性註解的值
        Field f = c1.getDeclaredField("name");
        FieldStudent declaredAnnotation = f.getDeclaredAnnotation(FieldStudent.class);
        System.out.println(declaredAnnotation.columnName());
        System.out.println(declaredAnnotation.type());
        System.out.println(declaredAnnotation.length());

        

    }
}


@TabStudent("db_student")
class Student{

    @FieldStudent(columnName = "db_id",type = "int",length = 10)
    private int id;
    @FieldStudent(columnName = "db_age",type = "int",length = 10)
    private int age;
    @FieldStudent(columnName = "db_name",type = "varchar",length = 3)
    private String name ;

    public Student() {
    }

    public Student(int id, int age, String name) {
        this.id = id;
        this.age = age;
        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;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

//類名的註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TabStudent{
    String value();
}

//屬性的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldStudent{
    String columnName();
    String type();
    int length();
}

相關文章