java註解與反射

註解與反射

**本文學自b站狂神:b站狂神–註解與反射篇**

看本文前建議看下JVM篇(對類加載器的詳細說明):JVM學習

一、什麼是註解

  • Annotation是從JDK5.0開始引入的新技術.
  • Annotation的作用:
    • 不是程序本身,可以對程序做出解釋.(這一點和註釋(comment)沒什麼區別)
    • 可以被其它程序(比如:編譯器等)讀取.
  • Annotation的格式:
    • 註解是以“@註釋名”在代碼中存在的,還可以添加一些參數值,例如:@SuppressWarnings(value=”unchecked”).
  • Annotation在哪裏使用?
    • 可以附加在package,class,method,field等上面,相當於給他們添加了額外的輔助信息。
    • 我們可以通過反射機制編程實現對這些元素據的訪問

二、內置註解

@Override:定義在java.lang.Override,此註解只適用於修辭方法,表示一個方法,表示一個方法聲明打算重寫超類種的另一個方法聲明。

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

**@SupperessWarnings:**定義在java.lang.SuppressWarnings中,用來抑制編譯時的警告信息,

  • 與前兩個註釋有所不同,你需要添加一個參數才能正確使用,這些參數都是已經定義好了的,我們選擇性使用就好了。
  • @SuppressWarnings(“all”)
  • @SuppressWarnings(“unchecked”)
  • @SuppressWarnings(value={“unchecked”,“deprecation”})
  • 等等…

三、元註解

  • 元註解的作用就是負責註解其它註解,Java定義了4個標準的meta-annotation類型,他們被用來對其它的anntation類型作說明
  • 這些類型和它們所支持的類在java.lang.annotation包中可以找到(@Target@Retention@Documented,@Inherited
  • @Target:用於描述註解的使用範圍(即被描述的註解可以用在什麼地方)
  • @Retention:表示需要在什麼級別保存該註釋信息,用於描述註解的生命週期(SOURCE<CLASS<RUNTIME
  • @Document:說明該註解將包含在javadoc中
  • @Inherited:說明子類可以繼承父類中的該註解
package com.lxf.annotation;

//測試元註解



import java.lang.annotation.*;

@MyAnnotation
public class Test01 {
    
    @MyAnnotation
    public void test(){
        
    }
}

//定義一個註解
//Target 表示我們的註解可以用到什麼地方
//TYPE(類),FIELD(字段), METHOD(方法),PARAMETER(參數),CONSTRUCTOR(構造函數),PACKAGE(包),
@Target(value={ElementType.METHOD,ElementType.TYPE})
//Retention 表示我們的註解在什麼地方還有效
//SOURCE(未編譯)<CLASS(編譯+未編譯)<RUNTIME(運行時有效)
@Retention(value = RetentionPolicy.RUNTIME)
//表示是否將我們的註解生成在Javadoc中
@Documented
//子類可以繼承父類的註解
@Inherited
@interface MyAnnotation{
    
}

自定義註解:

package com.lxf.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class Test02 {
    @MyAnnotation2(name = "lxf", schools = {"新東方學校","北大青鳥"})
    public  void test(){

    }

    //只有一個值,可以不寫對應字段
    @MyAnnotation3("張三")
    public  void test2(){
    }
}

@Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    //註解的參數:參數類型+參數名();
    String name() default "";//如果沒有默認值,我們就必須給註解下的類或方法賦值
    int age() default  0;
    int id() default -1;//如果默認值未-1,代表不存在

    String[] schools();
}

@Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
    String value();
}

四、反射概述

4.1、靜態VS動態語言

動態語言

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

靜態語言

  • 與動態語言相對應的,運行時結構不可變的語言就是靜態語言。如:Java、C、C++。
  • Java不是動態語言,但Java可以稱之爲“準動態語言”。即Java有一定的動態性,我們可以利用反射機制獲得類似動態語言的特性。Java的動態性讓編程的時候更加靈活!
4.2、反射概述
  • Reflection(反射)是Java被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Refletion API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法。
Class c=Class.forName("java.lang.String")
  • 加載完類之後,在堆內存的方法區中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象就包含了完整的類的結構信息。我們可以通過這個對象看到的類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構,所以,我們形象的稱之爲:反射

正常方式:引入需要的“包類”名稱–>通過new實例化–>取得實例化對象

反射方式:實例化對象–>getClass()方法–>得到完整的“包類”名稱

4.3、java反射機制研究及應用
  • 在運行時判斷一個對象所屬的類
  • 在運行時構造任意一個類的對象
  • 在運行時判斷任意一個類所具有的成員變量和方法
  • 在運行時獲取泛型信息
  • 在運行時調用任意一個對象的成員變量和方法
  • 在運行時處理註解
  • 生成動態代理
4.4、Java反射的優點和缺點
  • 優點:可以實現動態創建對象和編譯,體現出很大的靈活性
  • 缺點:對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且它滿足我們的要求。這類操作總是慢於直接執行相同的操作。
4.5、反射相關的主要API
  • java.lang.Class:代表一個類
  • java.lang.reflect.Method:代表類的方法
  • java.lang.reflect.Field:代表類的成員變量
  • java.lang.reflect.Constructor:代表類的構造器
4.6、Class類

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

  • Class本身也是一個類
  • Class對象只能由系統建立對象
  • 一個加載的類在JVM中只會有一個Class實例
  • 一個Class對象對應的是一個加載到JVM中的一個.class文件
  • 每個類的實例都會記得自己是由哪個Class實例所生成
  • 通過Class可以完整地得到一個類中的所有被加載的結構
  • Class類時Reflection的根源,針對任何你想動態加載、運行的類,唯有先獲得相應的Class對象
4.7、Class類的常用方法
方法名 功能說明
static ClassforName(String name) 返回指定類名name的Class對象
Object newInstance() 調用缺省構造函數,返回Class對象的一個實例
getName() 返回此Class對象所表示的實體(類,接口,數組類或void)的名稱。
Class getSuperClass() 返回當前Class對象的父類的Class對象
Class[] getinterfaces() 返回當前Class對象的父類的Class對象
ClassLoader getClassLoader() 返回該類的類加載器
Constructor[] getConstructors() 返回一個包含某些Constructor對象的數組
Method getMethod(String name,class… T) 返回一個Method對象,此對象的形參類型爲paramType
Field[] getDeclaredFields() 返回Field對象的一個數組
4.8、獲取Class類的實例

a)若已知具體的類,通過類的class屬性獲取,該方法最爲安全可靠,程序性能最高。

Class clazz=Person.class

b)已知某個類的實例,調用該實例的getClass()方法獲取Class對象

Class clazz=Person.get();

c)已知一個類的全類名,且在該類的類路徑下,可通過Class類的靜態方法forName()獲取,可能拋出ClassNotFoundException

Class clazz=Class.forName("demo01.student");

d)內置基本數據類型可以直接用類名.Type

e)還可以利用ClassLoader

package com.lxf.reflection;

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.lxf.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="老師";
    }
}
4.9、哪些類型可以有Class對象?
  • class:外部類,成員(成員內部類,靜態內部類),局部內部類,匿名內部類。
  • interface:接口
  • []數組
  • enum:枚舉
  • annotation:註解@interface
  • primitive type:基本數據類型
  • void

練習:

package com.lxf.reflection;

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;
        //
        Class c9=Class.class;

        System.out.println("c1="+c1);
        System.out.println("c2="+c2);
        System.out.println("c3="+c3);
        System.out.println("c4="+c4);
        System.out.println("c5="+c5);
        System.out.println("c6="+c6);
        System.out.println("c7="+c7);
        System.out.println("c8="+c8);
        System.out.println("c9="+c9);

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

結果:

c1=class java.lang.Object
c2=interface java.lang.Comparable
c3=class [Ljava.lang.String;
c4=class [[I
c5=interface java.lang.Override
c6=class java.lang.annotation.ElementType
c7=class java.lang.Integerjava
c8=void
c9=class java.lang.Class
356573597
356573597

五、Java內存分析、類的加載過程、類初始化、類加載器的作用

5.1、java內存

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-akr95XXx-1592488733386)(E:\typoraPic\java內存.jpg)]

5.2、類的加載過程(瞭解)

當成主動使用某個類時,如果該類還未被加載到內存中,則系統會通過如下三個步驟來對該類進行初始化。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xHDIsIRO-1592488733399)(E:\typoraPic\類的加載過程-1592230841454.jpg)]

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

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類靜態代碼塊初始化");
        m=300;
    }
    static int m=100;
    public A(){
        System.out.println("A類的無參構造初始化");
    }
}
5.4、什麼時候會發生類初始化?
  • 類的主動引用(一定會發生類的初始化)
    • 當虛擬機啓動,先初始化main方法所在的類
    • new 一個類的對象
    • 調用類的靜態成員(除了final常量)和靜態方法
    • 使用java.lang.reflect包的方法對類進行反射調用
    • 當初始化一個類,如果其父類沒有被初始化,則先會初始化它的父類
  • 類的被動引用(不會發生類的初始化)
    • 當訪問一個靜態域時,只有真正聲明這個域的類纔會被初始化。如:當通過子類引用父類的靜態變量,不會導致子類初始化
    • 通過數組定義引用,不會觸發此類的初始化
    • 引用常量不會觸發此類的初始化(常量的鏈接階段就存入調用類的常量池中了)
package com.lxf.reflection;

//測試類什麼時候會初始化
public class Test06 {
    static {
        System.out.println("Main類被加載");
    }

    public static void main(String[] args) throws ClassNotFoundException {
        //1.主動引用:Main類加載-->Father類被加載-->Son類被加載
        //Son son=new Son();

        //2.反射也會產生主動引用:Main類被加載-->Father類被加載-->Son類被加載
        //Class<?> aClass = Class.forName("com.lxf.reflection.Son");

        //3.不會產生類的引用的方法:

        //3.1、子類調用父類的靜態域,Main類被加載-->父類被加載
        //System.out.println(Son.b);

        //3.2、類的數組定義:Main類被加載
        //Son[] array=new Son[5];

        //3.3、引用該類的常量:Main類被加載
        //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;
}
5.5、類加載器的作用
  • 類加載器的作用:將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區的運行時的數據結構,然後再在堆中生成一個代表這個類的java.lang.Class對象,作爲方法區中類數據的訪問入口。
  • 類緩存:標準的javaSE類加載器可以按要求查找類,但一旦某個類被加載到類加載器中,它將維持加載(緩存)一段時間。不過JVM垃圾回收機制可以回收這些Class對象

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XgvubT22-1592488733405)(E:\typoraPic\類加載器的作用.jpg)]

  • 類加載器的作用:將class文件字節碼內容加載到內存中,JVM規範定義瞭如下類型的類的加載器。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gymgMcGv-1592488733415)(E:\typoraPic\類加載器的作用2.jpg)]

package com.lxf.reflection;


public class Test07 {
    public static void main(String[] args) throws ClassNotFoundException {
        //獲取系統類的加載器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        //結果:sun.misc.Launcher$AppClassLoader@18b4aac2
        
        //獲取系統父類加載器的父類加載器-->擴展類加載器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);
        //結果:sun.misc.Launcher$ExtClassLoader@1540e19d
        
        //獲取擴展類加載器的父類加載器-->根加載器(C/C++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);
        //結果:null
        
        //測試當前類是哪個加載器加載的
        ClassLoader classLoader = Class.forName("com.lxf.reflection.Test07").getClassLoader();
        System.out.println(classLoader);
        //結果:sun.misc.Launcher$AppClassLoader@18b4aac2
        
        //測試JDK內置的類是誰加載的
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);
        //結果:null

        //如何獲得系統類加載器可以加載的路徑
        System.out.println(System.getProperty("java.class.path"));
        /**結果:
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;java
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;
         * C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;
         * C:\Users\dell\IdeaProjects\20200615-註解與反射\out\production\20200615-註解與反射;
         * C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\lib\idea_rt.jar
         */
        
        //雙親委派機制
    }
}

六、創建運行時類的對象

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

Field、Method、Constructor、Superclass、Interface、Annotation

  • 實現的全部接口
  • 所繼承的父類
  • 全部的構造器
  • 全部的方法
  • 全部的Field
  • 註解
  • 。。。
package com.lxf.reflection;

public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //通過反射獲取類的class對象
        Class<?> c1 = Class.forName("com.lxf.reflection.User");
        System.out.println(c1);

    }
}

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 +
                '}';
    }
}
package com.lxf.reflection;


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.lxf.reflection.User");

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

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

        //獲得類的屬性
        //Field[] fields1 = c1.getFields();只能找到public屬性

        Field[] fields2 = c1.getDeclaredFields();//獲取全部的屬性
        for (Field field : fields2) {
            System.out.println(field);
        }

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

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

        //獲得類的方法
        Method[] methods1 = c1.getMethods();//獲得本類及其父類的全部public方法
        for (Method method : methods1) {
            System.out.println("沒有加Declared:" + method);
        }

        Method[] methods2 = c1.getDeclaredMethods();//獲得本類的所有方法
        for (Method method : methods2) {
            System.out.println("加了Declared:" + 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[] constructors1 = c1.getConstructors();//獲得本類的所有public構造方法
        for (Constructor constructor : constructors1) {
            System.out.println("1:" + constructor);
        }

        Constructor[] constructors2 = c1.getDeclaredConstructors();//獲得本類的所有構造方法

        for (Constructor constructor : constructors2) {
            System.out.println("2:" + constructor);
        }

        //獲得指定的構造器
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println("指定:"+constructor);
        
        //getFields()與getDeclaredFields()區別:
		//getFields()只能訪問類中聲明爲公有的字段,私有的字段它無法訪問,能訪問從其它類繼承來的公有方法.
		//getDeclaredFields()能訪問類中所有的字段,與public,private,protect無關,不能訪問從其它類繼承來的方法

        //getMethods()與getDeclaredMethods()區別:
        //getMethods()只能訪問類中聲明爲公有的方法,能訪問從其它類繼承來的公有方法.
        //getDeclaredFields()能訪問類中所有的字段,與public,private,protect無關,不能訪問從其它類繼承來的方法

        //getConstructors()與getDeclaredConstructors()區別:
        //getConstructors()只能訪問類中聲明爲public的構造函數.
        //getDeclaredConstructors()能訪問類中所有的構造函數,與public,private,protect無關

    }
    }

七、有了Class對象,能做什麼?

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

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

步驟如下:

  • 通過Class類的getDeclaredConstructor(Class…parameterTypes)取得本類的指定形參類型的構造器
  • 向構造器的形參中傳遞一個對象數組進去,裏面包含了構造器中所需的各個參數。
  • 通過Constructor實例化對象
package com.lxf.reflection;

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

public class Test09 {
    public static void main(String[] args) throws  Exception {
        //獲得Class對象
        Class c1 = Class.forName("com.lxf.reflection.User");

        //構造一個對象
        //User user1 = (User) c1.newInstance();//本質是調用無參構造器,如果沒有
        //System.out.println(user1);

        //通過構造器構造
        Constructor constructor = c1.getConstructor(String.class, int.class, int.class);
        User user2 = (User) constructor.newInstance("劉一手", 001, 18);
        System.out.println(user2);

        //通過反射調用普通方法
        System.out.println("=========================================");
        User user3 = (User) c1.newInstance();
        //通過反射獲取一個方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke:激活的意思
        //(對象,"方法的值")
        setName.invoke(user3,"劉二手");
        System.out.println(user3);

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

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

7.2、setAccessible
  • Method和Field、Constructor對象都有setAccessible()方法。
  • setAccessible作用是啓動和禁用訪問安全檢測的開關。
  • 參數值爲true則指示反射的對象在使用時應該取消Java語言訪問檢查。
    • 提高反射的效率。如果代碼中必須用反射,而該句代碼需要頻繁的被調用,那麼請設置true。
    • 使得原本無法訪問的私有成員也可以訪問
  • 參數值爲false則指示反射的對象應該實施Java語言的訪問檢查
  • 開啓setAccessible要比不開啓setAccessible效率高几倍,而普通方式獲取的對象比前者速度快上幾百倍
7.3、反射操作泛型
  • Java採用泛型擦除的機制來引入泛型,java中的泛型僅僅是給編譯器javac使用的,確保數據的安全性和免去強制類型轉換問題,但是,一旦編譯完成,所有和泛型有關的類型全部擦除
  • 爲了通過反射操作這些類型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildardType幾種類型來代表不能被歸一到Class類中的類型但是又和原始類型齊名的類型
  • ParameterizedType:表示一種參數化類型,比如Collection<String>
  • TypeVarialbe:是各種類型變量的公共父接口
  • WildcardType:代表一種通配符類型表達式
package com.lxf.reflection;

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 Test10 {
    public  void test01(Map<String,User> map, List<User> list){

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

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = Test10.class.getMethod("test01", Map.class, List.class);

        //獲取參數類型
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        //遍歷參數類型
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("genericParameterType = " + genericParameterType);
            //獲取泛型
            if(genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("actualTypeArgument = " + actualTypeArgument);
                }
            }
        }


    }
}

7.4、反射操作註解
package com.lxf.reflection;

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.lxf.reflection.Student2");

        //通過反射獲得註解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("annotation = " + annotation);
        }
        //獲取註解的value的值
        TableLxf tableLxf= (TableLxf) c1.getAnnotation(TableLxf.class);
        String value = tableLxf.value();
        System.out.println("value = " + value);

        //獲得類中(此處獲取字段上)指定的註解
        Field name = c1.getDeclaredField("name");
        FieldLxf annotation1 = name.getAnnotation(FieldLxf.class);
        System.out.println("annotation1-columnName = " + annotation1.columnName());
        System.out.println("annotation1-type = " + annotation1.type());
        System.out.println("annotation1-length = " + annotation1.length());

        Field id = c1.getDeclaredField("id");
        FieldLxf  annotation2= id.getAnnotation(FieldLxf.class);
        System.out.println("annotation2 = " + annotation2);
        System.out.println("annotation2-columnName = " + annotation2.columnName());
        System.out.println("annotation2-type = " + annotation2.type());
        System.out.println("annotation2-length = " + annotation2.length());

        Field age = c1.getDeclaredField("age");
        FieldLxf annotation3 = age.getAnnotation(FieldLxf.class);
        System.out.println("annotation3 = " + annotation3);
        System.out.println("annotation3-columnName = " + annotation3.columnName());
        System.out.println("annotation3-type = " + annotation3.type());
        System.out.println("annotation3-length = " + annotation3.length());
    }
}

@TableLxf("db_student")
class Student2{
    @FieldLxf(columnName = "db_id",type = "int",length = 10)
    private int id;
    @FieldLxf(columnName = "db_age",type = "int",length = 10)
    private int age;
    @FieldLxf(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 TableLxf{
    String value();
}

//屬性的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldLxf{
    String columnName();
    String type();
    int length();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章