註解與反射

註解Annotation

  • Annotation的作用:
    • 不是程式本身,可以對程式作出解釋
    • 可以被其他程式(比如:編譯器等)讀取
  • Annotation的格式:
    • 如@SuppressWarnings(value=”unchecked”)
  • 在哪裡使用:
    • package, class, method, field
  • 可通過反射機制訪問

內置註解

import java.util.ArrayList;
import java.util.List;

public class Test01 extends Object{

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

    //@Deprecated 不推薦程式設計師使用,但是可以使用,或者存在更好的方式
    @Deprecated
    public static void test(){
        System.out.println("Deprecated");
    }

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

    public static void main(String[] args) {
        test();
    }
}

元註解

  • 元註解的作用就是負責註解其他註解,Java定義了4個標準的meta-annotation類型,他們被用來提供對其他annotation類型作說明
  • 這些類型和它們所支援的類在java.lang.annotation包中可以找到(@Target, @Retention, @Documented, @Inherited)
    • @Target:用於描述註解的適用範圍
    • @Retention:表示需要在什麼級別保存該注釋資訊
      • (SOURCE<CLASS<RUNTIME)
    • @Document:說明該註解將被包含在javadoc中
    • @Inherited:說明子類可以繼承父類中的該註解

自定義註解

  • 使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation介面
  • 分析:
    • @interface用來聲明一個註解,格式:public @ interface註解名 {定義內容}
    • 其中的每一個方法實際上是聲明了一個配置參數
    • 方法的名稱就是參數的名稱
    • 返回值類型就是參數的類型 {返回值只能是基本類型,Class,String,enum}
    • 可以通過default來聲明參數的默認值
    • 如果只有一個參數成員,一般參數名為value
    • 註解元素必須要有值,我們定義註解元素時,經常使用空字元串0作為默認值
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//自定義註解
public class Test03 {
    //註解可以顯式賦值,如果沒有默認值,我們就必須給註解複製
    @MyAnnotation2(age = 18)
    public void test(){}

    @MyAnnotation3("秦疆")
    public void test2(){

    }
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    //註解的參數:參數類型 + 參數名();
    String name() default "";
    int age();
    int id() default -1;//如果默認值為-1,代表不存在

    String[] schools() default {"西部開源","清華大學"};
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
    String value();//如果只有一個參數時,將參數名設置為value則默認可以省略該參數
}

反射

  • 動態語言,運行時可以改變其結構的語言,如Object-C,C#,JavaScript,PHP,Python
  • 靜態語言,運行時結構不可變的語言,如Java,C,C++
  • Java具有一定的動態性,我們可以利用反射機制獲得類似動態語言的特性

Java Reflection

  • 反射機制允許程式在執行期間藉助於Reflection API取得任何類的內部資訊,並能直接操作任意對象的內部屬性及方法

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

  • Java反射機制提供的功能

    • 在運行時判斷任意一個對象所屬的類
    • 在運行時構造任意一個類的對象
    • 在運行時判斷任意一個類所具有的成員變數和方法
    • 在運行時獲取泛型資訊
    • 在運行時調用任意一個對象的成員變數和方法
    • 在運行時處理註解
    • 生成動態代理
  • 反射相關的主要API

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

Class類

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

public final Class getClass()
  • 以上的方法返回值的類型是一個Class類,此類事Java反射的源頭,實際上所謂反射從程式的運行結果來看也很好理解,即:可以通過對象反射求出類的名稱
//什麼叫反射
public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
        //通過反射獲取類的Class對象
        Class c1 = Class.forName("com.annotation_reflection.reflection.User");
        System.out.println(c1);

        Class c2 = Class.forName("com.annotation_reflection.reflection.User");
        Class c3 = Class.forName("com.annotation_reflection.reflection.User");
        Class c4 = Class.forName("com.annotation_reflection.reflection.User");

        //一個類在記憶體中只有一個Class對象
        //一個類被載入後,類的整個結構都會被封裝在Class對象中
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
        System.out.println(c4.hashCode());
    }
}

//實體類:pojo, entiry
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;
    }

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

  • 對照鏡子後可以得到的資訊:某個類的屬性、方法和構造器、某個類到底實現了哪些介面。對於每個類而言,JRE都為其保留一個不變的Class類型的對象。
    • Class本身也是一個類
    • Class對象只能由系統建立
    • 一個載入的類在JVM中只會有一個Class實例
    • 一個Class對象對應的是一個載入到JVM中的一個.class文件
    • 每個類的實例都會記得自己是由哪個Class實例所生成
    • 通過Class可以完整地得到一個類中的所有被載入的結構
    • Class類是Reflection的根源,針對任何你想動態載入、運行的類,唯有先獲得相應的Class對象
方法名 功能說明
static ClassforName(String name) 返回指定類名name的Class對象
Object newInstance() 調用預設構造函數,返回Class對象的一個實例
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類的創建方式
//測試Class類的創建方式有哪些
public class Test03 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("這個人是:"+person.name);

        //方式一:通過對象獲得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

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

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

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

        //獲得父類類型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}

class Person{
    public String name;

    public Person(){
    }

    public Person(String name){
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Student extends Person{
    public Student(){
        this.name = "學生";
    }
}

class Teacher extends Person{
    public Teacher(){
        this.name = "老師";
    }
}
  • 哪些類型可以有Class對象
    • class:外部類,成員(成員內部類,靜態內部類),局部內部類,匿名內部類
    • interface:介面
    • []:數組
    • enum:枚舉
    • annotation:註解@interface
    • primitive type:基本數據類型
    • void
import java.lang.annotation.ElementType;

//所有類型的Class
public class Test04 {
    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

        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());
    }
}

類載入記憶體分析

Java記憶體分析:

類的載入過程:

類的載入與ClassLoader的理解

  • 載入:將class文件位元組碼內容載入到記憶體中,並將這些靜態數據轉換成方法區的運行時數據結構,然後生成一個代表這個類的java.lang.Class對象
  • 鏈接:將Java類的二進位程式碼合併到JVM的運行狀態之中的過程
    • 驗證:確保載入的類資訊符合JVM規範,沒有安全方面的問題
    • 準備:正式為類變數(static)分配記憶體並設置類變數默認初始值的階段,這些記憶體都將在方法區中進行分配
    • 解析:虛擬機常量池內的符號引用(常量名)替換為直接引用(地址)的過程
  • 初始化:
    • 執行類構造器()方法的過程。類構造器()方法是由編譯器自動收集類中所有類變數的賦值動作和靜態程式碼塊中的語句合併產生的。(類構造器是構造類資訊的,不是構造該類對象的構造器)。
    • 當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化。
    • 虛擬機會保證一個類的()方法在多執行緒環境中被正確加鎖和同步。

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

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

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

    static int m = 100;

    public A() {
        System.out.println("A類的無參構造初始化");
    }
}

什麼時候會發生類初始化

  • 類的主動引用(一定會發生類的初始化)
    • 當虛擬機啟動,先初始化main方法所在的類
    • new一個類的對象
    • 調用類的靜態成員(除了final常量)和靜態方法
    • 使用java.lang.reflect包的方法對類進行反射調用
    • 當初始化一個類,如果其父類沒有被初始化,則先會初始化它的父類
  • 類的被動引用(不會發生類的初始化)
    • 當訪問一個靜態域時,只有真正聲明這個域的類才會被初始化。如:當通過子類引用父類的靜態變數,不會導致子類初始化
    • 通過數組定義類引用,不會觸發此類的初始化
    • 引用常量不會觸發此類的初始化(常量在鏈接階段就存入調用類的常量池中了)
//測試類什麼時候會初始化
public class Test06 {

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

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

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

        //不會產生類的引用的方法
        //System.out.println(Son.b);

        //Son[] array = new Son[5];

        System.out.println(Son.M);
    }

}

class Father{

    static int b = 2;

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

class Son extends Father{

    static {
        System.out.println("子類被載入");
        m = 300;
    }

    static int m = 100;
    static final int M = 1;
}

類載入器的作用

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

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

public class Test07 {
    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);

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

        //測試JDK內置的類是誰載入的
        classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader);

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

        //雙親委派機制    多重檢測,保證安全性,重複則無效
            //java.lang.String-->
        /*
        D:\environment\java\java_update\jre\lib\charsets.jar;
        D:\environment\java\java_update\jre\lib\deploy.jar;
        D:\environment\java\java_update\jre\lib\ext\access-bridge-64.jar;
        D:\environment\java\java_update\jre\lib\ext\cldrdata.jar;
        D:\environment\java\java_update\jre\lib\ext\dnsns.jar;
        D:\environment\java\java_update\jre\lib\ext\jaccess.jar;
        D:\environment\java\java_update\jre\lib\ext\jfxrt.jar;
        D:\environment\java\java_update\jre\lib\ext\localedata.jar;
        D:\environment\java\java_update\jre\lib\ext\nashorn.jar;
        D:\environment\java\java_update\jre\lib\ext\sunec.jar;
        D:\environment\java\java_update\jre\lib\ext\sunjce_provider.jar;
        D:\environment\java\java_update\jre\lib\ext\sunmscapi.jar;
        D:\environment\java\java_update\jre\lib\ext\sunpkcs11.jar;
        D:\environment\java\java_update\jre\lib\ext\zipfs.jar;
        D:\environment\java\java_update\jre\lib\javaws.jar;
        D:\environment\java\java_update\jre\lib\jce.jar;
        D:\environment\java\java_update\jre\lib\jfr.jar;
        D:\environment\java\java_update\jre\lib\jfxswt.jar;
        D:\environment\java\java_update\jre\lib\jsse.jar;
        D:\environment\java\java_update\jre\lib\management-agent.jar;
        D:\environment\java\java_update\jre\lib\plugin.jar;
        D:\environment\java\java_update\jre\lib\resources.jar;
        D:\environment\java\java_update\jre\lib\rt.jar;
        D:\environment\java_project\jdbc\out\production\基礎語法;
        D:\environment\java_project\javase\基礎語法\src\com\lib\commons-io-2.6.jar;
        D:\environment\IntelliJ IDEA 2021.2.1\lib\idea_rt.jar
         */
    }
}

獲取運行時類的完整結構

通過反射獲取運行時類的完整結構

Field、Method、Constructor、Superclass、Interface、Annotation

  • 實現的全部介面
  • 所繼承的父類
  • 全部的構造器
  • 全部的方法
  • 全部的Field
  • 註解
  • 。。。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

//獲得類的資訊
public class Test08 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("com.annotation_reflection.reflection.User");

        User user = new User();
        c1 = user.getClass();

        //獲得類的名字
        System.out.println(c1.getName());//獲得包名 + 類名
        System.out.println(c1.getSimpleName());//獲得類名

        //獲得類的屬性
        System.out.println("===============================================");
        Field[] fields = c1.getFields();//只能找到public屬性

        fields = c1.getDeclaredFields();//找到全部的屬性
        for (Field field : fields) {
            System.out.println(field);
        }

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

        //獲得類的方法
        System.out.println("===============================================");
        Method[] methods = c1.getMethods();//獲得本類及其父類的全部public方法
        for (Method method : methods) {
            System.out.println("正常的:"+method);
        }
        methods = c1.getDeclaredMethods();//獲得本類的所有方法
        for (Method method : methods) {
            System.out.println("getDeclaredMethods:"+method);
        }

        //獲得指定方法
        //重載
        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);


    }
}

有了Class對象,能做什麼?

  • 創建類的對象:調用Class對象的newInstance()方法
    • 1)類必須有一個無參數的構造器
    • 2)類的構造器的訪問許可權需要足夠

思考?難道沒有無參的構造器就不能創建對象了嗎?只要在操作的時候明確的調用類中的構造器,並將參數傳遞進去之後,才可以實例化操作。

  • 步驟如下:
    • 1)通過Class類的getDeclatedConstructor(Class … parameterTypes)取得本類的指定形參類型的構造器
    • 2)向構造器的形參中 傳遞一個對象數組進去,裡面包含了構造器中所需的各個參數
    • 3)通過Constructor實例化對象

調用指定的方法

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

  1. 通過Class類的getMethod(String name,Class…parameterTypes)方法取得一個Method對象,並設置此方法操作時所需要的參數類型。
  2. 之後使用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對象都有setAccessible()方法
  • setAccessible作用是啟動和禁用訪問安全檢查的開關
  • 參數值為true則指示反射的對象在使用時應該取消Java語言訪問檢查
    • 提高反射的效率。如果程式碼中必須用反射,而該句程式碼需要頻繁的被調用,那麼請設置為true
    • 使得原本無法訪問的私有成員也可以訪問
  • 參數值為false則指示反射的對象應該實施Java語言訪問檢查
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//動態的創建對象,通過反射
public class Test09 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //獲得Class對象
        Class c1 = Class.forName("com.annotation_reflection.reflection.User");

        //構造一個對象
        //User user = (User) c1.newInstance();//本質是調用了類的無參構造器
        //System.out.println(user);

        //通過構造器創建對象
        //Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        //User user2 = (User)constructor.newInstance("秦疆",001,18);
        //System.out.println(user2);

        //通過反射調用普通方法
        User user3 = (User)c1.newInstance();
        //通過反射獲取一個方法
        Method setName = c1.getDeclaredMethod("setName", String.class);

        //invoke: 激活的意思
        //(對象,」方法的值「)
        setName.invoke(user3,"狂神");
        System.out.println(user3.getName());

        //通過反射操作屬性
        System.out.println("==============================================================");
        User user4 = (User)c1.newInstance();
        Field name = c1.getDeclaredField("name");

        //不能直接操作私有屬性,我們需要關閉程式的安全檢測,屬性或者方法的setAccessible(true)
        name.setAccessible(true);//
        name.set(user4,"狂神2");
        System.out.println(user4.getName());

    }
}

不同方式執行方法的性能對比

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

//分析性能問題
public class Test10 {

    //普通方式調用
    public static void test01(){
        User user = new User();

        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; 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.getDeclaredMethod("getName",null);

        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1000000000; 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.getDeclaredMethod("getName",null);
        getName.setAccessible(true);

        long startTime = System.currentTimeMillis();

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

        long endTime = System.currentTimeMillis();

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

    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        test01();
        test02();
        test03();
    }
}

反射操作泛型

  • Java採用泛型擦除的機制來引入泛型,Java中的泛型僅僅是給編譯器javac使用的,確保數據的安全性和免去強制類型轉換問題,但是,一旦編譯完成,所有和泛性有關的類型全部擦除
  • 為了通過反射操作這些類型,Java新增了ParameterizedType, GenericArrayType, TypeVariable和WildcardType幾種類型來代表不能被歸一到Class類中的類型但是又和原始類型齊名的類型
  • ParameterizedType:表示一種參數化類型,比如Collection
  • GenericArrayType:表示一種元素類型是參數化類型或者類型變數的數組類型
  • TypeVariable:是各種類型變數的公共父介面
  • WilecardType:代表一種通配符類型表達式
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 Test11 {

    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 = Test11.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 = Test11.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);
            }
        }

    }
}

反射操作註解

  • getAnnotations
  • getAnnotation

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

//練習反射操作註解
public class Test12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.annotation_reflection.reflection.Student2");

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

        //獲得註解的value的值
        Tablekuang tablekuang = (Tablekuang)c1.getAnnotation(Tablekuang.class);
        String value = tablekuang.value();
        System.out.println(value);

        //獲得類指定的註解
        Field f = c1.getDeclaredField("name");
        Fieldkuang annotation = f.getAnnotation(Fieldkuang.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());


    }
}

@Tablekuang("db_student")
class Student2{

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

    public Student2(){
    }

    public Student2(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 "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

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

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