java 反射 reflection

靜態語言 & 動態語言

靜態語言

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

動態語言

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

1、 Java 反射機制概述

1.1 Java Reflection

Reflection(反射)是Java被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於ReflectionAPI獲取任何類的內部信息,並能直接操作任意對象的內部屬性及方法

加載完類之後,在方法區 中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象就包含了完整的類的結構信息。我們可以通過這個對象看到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構,所以稱爲:反射

1.2 Java 反射機制研究及應用

Java 反射機制提供的功能

  • 在運行時判斷任意一個對象所屬的類
  • 在運行時構造任意一個類的對象
  • 在運行時判斷任意一個類所具有的成員變量和方法
  • 在運行時獲取泛型信息
  • 在運行時調用任意一個對象的成員變量和方法
  • 在運行時處理註解
  • 生成動態代理 AOP

1.3 Java 反射優點和缺點

優點

  • 可以實現動態創建對象和編譯,體現出很大的靈活性

缺點

  • 對性能有影響
  • 使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且它滿足我們的要求
  • 這類操作總是慢於直接執行的相同操作

1.4 反射相關主要的API

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

2、 理解Class類並獲取Class實例

2.1 Object 類

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

public final native Class<?> getClass();

以上方法返回值類型你一個Class類,此類是Java反射的源頭,所謂的反射從程序的運行結果來看也很好理解,即:可以通過對象求出類的名字

2.2 實體類 與 Class類

package com.ctra.reflection;
//什麼叫反射
public class test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("com.ctra.reflection.test01");

        Class c2 = Class.forName("com.ctra.reflection.test01");

        Class c3 = Class.forName("com.ctra.reflection.test01");
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());

    }
}

// 實體類:pojo entity
class User{
    private String name;
    private int id;
    private int age;

    public User() {
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public int getAge() {
        return age;
    }

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

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

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

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

輸出結果:

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

2.3 Class類

  • Class 本身也是一個類
  • Class 對象只能由系統建立對象
  • 一個加載的類在JVM中只會有一個Class實例
  • 一個Class對象對應的是一個加載到JVM中的一個.class文件
  • 每個類的實例都會記得自己是由哪個Class實例所生成
  • 通過Class可以完整地得到一個類中的所有被夾在的結構
  • Class類是Reflection的根源,針對任何逆向動態加載、運行的類、唯有先獲得相應的Class對象

2.4 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 getMethod(String name, Class<?>… parameterTypes) 返回一個Method對象,此對象的形參類型爲 paramType
Field[] getDeclaredFields() 返回Field對象的一個數組

2.5 獲取 Class類的實例

1)根據 .class 獲取

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

Class userClass = User.class;

2)根據 getClass() 方法獲取

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

Class c = userClass.getClass();

3)根據 靜態方法 forName() 方法獲取

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

Class c1 = Class.forName("com.ctra.reflection.test01");

4).Type

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

5)ClassLoader

還可以利用ClassLoader

上述幾個方式的實戰源碼

package com.ctra.reflection;

public class test02  {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person =new Student();
        System.out.println("這個人是:"+person.name);
        //  方式一:通過對象 getClass() 獲得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());
        //  方式二:通過 靜態方法 forname獲得
        Class c2 = Class.forName("com.ctra.reflection.Person"); // 繼承要看創建的是誰的對象(這裏是反例)
        Class c3 = Class.forName("com.ctra.reflection.Student");
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
        //  方式三:通過類名 .class 獲得
        Class c4 =Student.class;
        System.out.println(c4.hashCode());
        //  方式四:基本內置類型的包裝類都有一個Type屬性
        Class c5 =Integer.TYPE;
        System.out.println(c5);
        // 獲得父類類型
        Class c6 = c1.getSuperclass();
        System.out.println(c6);


    }
}

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="老師";
    }
}
輸出:
這個人是:學生
856419764
621009875
856419764
856419764
int
class com.ctra.reflection.Person

2.6 哪些類型可以有 Class 對象?

  • class:外部類,成員(成員內部類,靜態內部類),局部內部類,匿名內部類
  • interface:接口
  • []:數組
  • enum:枚舉
  • annotation:註解@interface
  • primitive type:基本數據類型
  • void

3、 類的加載與ClassLoader

3.1 類的加載過程

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

3.1 類的加載與ClassLoader的理解

step1:加載

將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區的運行時數據結構,然後生成一個代表這個類的 java.lang.Class 對象

step2:鏈接

將 Java類的二進制代碼合併到JVM的運行狀態之中的過程

  • 驗證:確保加載的類信息符合JVM規範,沒有安全方面的問題
  • 準備:正式爲類變量(static)分配內存並設置類變量默認初始值的階段,這些內存都將在方法區中進行分配
  • 解析:虛擬機常量池內的符號引用(常量名)替換爲直接飲用(地址)的過程

step3:初始化

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

3.3 類加載器的作用

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

3.4 類的加載器 ClassLoader

系統類的加載器、拓展類加載器、根加載器(C++)

package com.ctra.reflection;

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++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

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

        // 測試 JDK 內置的類 是誰加載  由於根加載器,無法加載
        classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader);

    }
}
輸出:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@330bedb4
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null

獲得系統類加載器可以加載的路徑

System.out.println(System.getProperty("java.class.path"));

*D:\Program Files\Java\jdk1.8.0_211\jre\lib\charsets.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\deploy.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\access-bridge-64.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\cldrdata.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\dnsns.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\jaccess.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\jfxrt.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\localedata.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\nashorn.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunec.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunjce_provider.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunmscapi.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunpkcs11.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\zipfs.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\javaws.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\jce.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\jfr.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\jfxswt.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\jsse.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\management-agent.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\plugin.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\resources.jar;
* D:\Program Files\Java\jdk1.8.0_211\jre\lib\rt.jar;
* E:\idea\mybatis\reflection\target\classes;
* C:\Users\Administrator.m2\repository\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;
* C:\Users\Administrator.m2\repository\org\mybatis\mybatis\3.5.2\mybatis-3.5.2.jar;
* C:\Users\Administrator.m2\repository\junit\junit\4.12\junit-4.12.jar;
* C:\Users\Administrator.m2\repository\org\hamcrest\hamcrest-core\2.1\hamcrest-core-2.1.jar;
* C:\Users\Administrator.m2\repository\org\hamcrest\hamcrest\2.1\hamcrest-2.1.jar;
* D:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar

一般在 rt.jar 後,則爲我們當前的類

3.5 類的初始化

1、類的主動引用(一定會發生類的初始化)

這裏參考了一下這個文章,大家感覺可以再閱讀一下
文章地址:https://blog.csdn.net/weixin_42636552/article/details/82949999

  1. 遇到 new,getstatic,putstatic,invokestatic這4條字節碼指令時,假如類還沒進行初始化,則馬上對其進行初始化工作。

    其實就是3種情況:
    1> 用new實例化一個類時
    2> 讀取或者設置類的靜態字段時(不包括被final修飾的靜態字段,因爲他們已經被塞進常量池了)
    3> 執行靜態方法的時候。

  2. 使用java.lang.reflect 包中的方法對類進行反射調用的時候,如果類還沒有進行過初始化,馬上對其進行初始化工作。

  3. 初始化一個類的時候,如果他父類還沒有被初始化,則先去初始化其父類。

  4. 當jvm啓動時,用戶指定一個要執行的主類(包含入口方法 static void main(String[] args)的那個類),則jvm會先去初始化這個類。

直接上代碼:

package com.ctra.reflection;

public class test06 {
    static{
        System.out.println("Main類被加載");
    }
    public static void main(String[] args) {
        // 1、 new 主動引用
        Son s= new Son();
        // 2、反射也會產生主動引用
        // Class c1 = Class.forName("com.ctra.reflection.Son");
    }
}

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;
}
輸出:
Main類被加載
父類被加載
子類被夾在

2、類的被動引用(不會發生類的初始化)

  • 當訪問一個靜態域時,只有真正聲明這個域的類纔會被初始化
  • 如:當通過子類引用父類的靜態變量,不會導致子類初始化(但是子類會被加載)
  • 通過數組定義類引用,不會觸發此類的初始化
  • 引用常量不會觸發此類的初始化(常量在鏈接階段就存入調用類的常量池中了)

代碼如下:
1) 當通過子類引用父類的靜態變量

package com.ctra.reflection;

public class test06 {
    static{
        System.out.println("Main類被加載");
    }
    public static void main(String[] args) throws ClassNotFoundException {
//        不會產生類的引用的方法
        System.out.println(Son.b);
    }
}

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;
}
輸出:
Main類被加載
父類被加載
2

2)通過數組定義類引用

package com.ctra.reflection;

public class test06 {
    static{
        System.out.println("Main類被加載");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        Son[] array = new Son[5];
    }
}
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;
}
輸出:
Main類被加載

3)引用常量不會觸發此類的初始化

package com.ctra.reflection;

import java.util.zip.CheckedOutputStream;

public class test06 {
    static{
        System.out.println("Main類被加載");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        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;
}
輸出:
Main類被加載
1

3.6 方法區

這是一段摘自《Java虛擬機規範(Java SE 8版)》的說明
在這裏插入圖片描述

4、 創建運行時類的對象

4.1 獲取運行時類的完整結構

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

在Class方法中 (如:getMethods、getDeclaredMethods)有Declared則爲獲取全部,沒有則爲獲取公有

package com.ctra.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.ctra.reflection.User");

        // 獲得類的名字
        System.out.println("================獲得類的名字 Simple & 非Simple============");
        System.out.println(c1.getName()); // 包名+類名
        System.out.println(c1.getSimpleName());//獲得類名

        // 獲得類的屬性
        System.out.println("=============獲得類的屬性 Declared &非Declared===============");
        Field[] fields = c1.getFields(); // 只能找到 public屬性
        for (Field field : fields) {
            System.out.println(field);
        }

        Field[] declaredFields = c1.getDeclaredFields(); // 找到全部的屬性
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

//        Field name = c1.getField("name");
//        System.out.println(name);

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

        // 獲得類的方法
        System.out.println("==============獲得類的方法 Declared &非Declared============");
        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);
        }


        // 獲得指定方法
        // 重載
        System.out.println("=================獲得 重載 get、set===================");
        Method getName = c1.getMethod("getName", null);
        System.out.println(getName);

        Method setName = c1.getMethod("setName", String.class);
        System.out.println(setName);


        // 獲得指定的構造器
        System.out.println("=================獲得指定的構造器 Declared &非Declared ===================");
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        constructors = c1.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("#"+constructor);
        }

        // 獲得指定的構造器
        System.out.println("===================獲得指定的構造器=================");
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println("指定:"+declaredConstructor);
    }
}
輸出:
================獲得類的名字 Simple & 非Simple============
com.ctra.reflection.User
User
=============獲得類的屬性 Declared &非Declared===============
private java.lang.String com.ctra.reflection.User.name
private int com.ctra.reflection.User.id
private int com.ctra.reflection.User.age
=============獲得類的屬性 getDeclaredField===============
private java.lang.String com.ctra.reflection.User.name
==============獲得類的方法 Declared &非Declared============
正常的:public java.lang.String com.ctra.reflection.User.toString()
正常的:public java.lang.String com.ctra.reflection.User.getName()
正常的:public int com.ctra.reflection.User.getId()
正常的:public void com.ctra.reflection.User.setName(java.lang.String)
正常的:public void com.ctra.reflection.User.setAge(int)
正常的:public int com.ctra.reflection.User.getAge()
正常的:public void com.ctra.reflection.User.setId(int)
正常的:public final void java.lang.Object.wait() throws java.lang.InterruptedException
正常的:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
正常的:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
正常的:public boolean java.lang.Object.equals(java.lang.Object)
正常的:public native int java.lang.Object.hashCode()
正常的:public final native java.lang.Class java.lang.Object.getClass()
正常的:public final native void java.lang.Object.notify()
正常的:public final native void java.lang.Object.notifyAll()
getDeclaredMethods:public java.lang.String com.ctra.reflection.User.toString()
getDeclaredMethods:public java.lang.String com.ctra.reflection.User.getName()
getDeclaredMethods:public int com.ctra.reflection.User.getId()
getDeclaredMethods:public void com.ctra.reflection.User.setName(java.lang.String)
getDeclaredMethods:private void com.ctra.reflection.User.test()
getDeclaredMethods:public void com.ctra.reflection.User.setAge(int)
getDeclaredMethods:public int com.ctra.reflection.User.getAge()
getDeclaredMethods:public void com.ctra.reflection.User.setId(int)
=================獲得 重載 get、set===================
public java.lang.String com.ctra.reflection.User.getName()
public void com.ctra.reflection.User.setName(java.lang.String)
=================獲得指定的構造器 Declared &非Declared ===================
public com.ctra.reflection.User()
public com.ctra.reflection.User(java.lang.String,int,int)
#public com.ctra.reflection.User()
#public com.ctra.reflection.User(java.lang.String,int,int)
===================獲得指定的構造器=================
指定:public com.ctra.reflection.User(java.lang.String,int,int)
  • 在實際操作中,取得類的信息的操作代碼,並不會經常開發
  • 一定要熟悉 Java.lang.reflect 包的作用,反射機制
  • 如何獲得屬性、方法、構造起的名稱,修飾符等

5、 獲取運行時類的完整結構

有了Class對象,能做什麼?

創建類的對象:調用Class 對象的newInstance()方法

  • 類必須有一個無參數的構造器
  • 類的構造器的訪問權限需要足夠

如何以上2個條件都不滿足,如何創建對象?

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

調用指定的方法

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

  1. 通過Class類的getMethod(String name,Class…parameterTypes)方法取得一個Method對象,並設置此方法操作時所需要的參數類型
  2. 之後使用Object invoke(Object obj,Object[] args)進行調用,並向方法中傳遞要設置的obj對象的參數信息

實體類 User (供下面代碼使用)

// 實體類:pojo entity
class User{
    private String name;
    private int id;
    private int age;

    public User() {
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }

    public int getAge() {
        return age;
    }

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

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

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

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

    private void test(){}

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

反射創建對象 方式一:通過newInstance()

前提:使用newInstance() ,實體類必須存在無參構造函數

package com.ctra.reflection;

import javax.jws.soap.SOAPBinding;
import java.lang.reflect.Constructor;
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, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        // 獲得 Class 對象
        Class c1 = Class.forName("com.ctra.reflection.User");

        // 構造一個實體對象
        // jdk9之後
        // User user1 = (User)c1.getDeclaredConstructor().newInstance();
        // jdk8
        User user = (User)c1.newInstance(); // 本質是調用了類的無參構造器
        System.out.println(user);
    
    }
}
輸出:User{name='null', id=0, age=0}

反射創建對象 方拾二:通過構造器getDeclaredConstructor()

前提:如果實體類沒有無參的構造函數,則可以使用getDeclaredConstructor() 獲取一個構造器,然後

package com.ctra.reflection;

import javax.jws.soap.SOAPBinding;
import java.lang.reflect.Constructor;
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, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        // 獲得 Class 對象
        Class c1 = Class.forName("com.ctra.reflection.User");

        // 此時註釋掉 無參構造方法
        // 通過構造器創建對象    (調用有參構造函數)
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user2 = (User)constructor.newInstance("ctra", 001, 18);
        System.out.println(user2);

    }
}
輸出:User{name='ctra', id=1, age=18}

反射調用普通方法

Object invoke(Object obj,Object … args)

  • Object 對應原方法的返回值,若原方法無返回值,此時返回null
  • 若原方法爲靜態方法,此時形參 Object obj可爲null
  • 若原方法形參列表爲空,則Object[] args爲null
  • 若原方法聲明爲 private,則需要在調用此invoke()方法前,顯式調用方法對象的 setAccessible(true)方法,將可訪問private的方法
package com.ctra.reflection;

import javax.jws.soap.SOAPBinding;
import java.lang.reflect.Constructor;
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, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        // 獲得 Class 對象
        Class c1 = Class.forName("com.ctra.reflection.User");

        User user3 = (User)c1.newInstance(); // 獲取對象
        //通過反射獲取一個方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //  invoke:激活
        setName.invoke(user3,"ctra"); // (對象,方法的值)
        System.out.println(user3.getName());

    }
}
輸出:ctra

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

  1. 通過Class類中的getMethod(String name,Class…parameterTypes)方法取得一個Method對象,並設置此方法操作時所需要的參數類型
  2. 之後使用 Object invoke(Object obje,Object[] args)進行調用,並向方法中傳遞要設置的obj對象的參數信息

反射操作屬性

當訪問私有屬性時,使用setAccessible(true),關閉程序安全監測

package com.ctra.reflection;

import javax.jws.soap.SOAPBinding;
import java.lang.reflect.Constructor;
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, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        // 獲得 Class 對象
        Class c1 = Class.forName("com.ctra.reflection.User");

        // 通過反射操作屬性
        User user4 = (User)c1.newInstance();
        Field name = c1.getDeclaredField("name");
        // 不能直接操作私有屬性,關閉程序安全監測可以
        name.setAccessible(true);
        name.set(user4,"CTRA_WL");
        System.out.println(user4.getName());
    }
}

setAccessible

  • Method 和 Field、Constructor 對象都有 setAccessible() 方法
  • setAccessible作用是啓動和禁用訪問安全檢查的開關
  • 參數值爲 true 則只是反射的對象在使用時應該取消Java語言訪問檢查

提高反射的效率。如果代碼中必須使用反射,而該句代碼需要頻繁的被調用,那麼請設置爲true
是的原本無法訪問的私有成員也可以訪問

  • 參數值爲false則指示反射的對象應該實施Java語言訪問檢查

性能對比

package com.ctra;

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

//分析性能問題
public class test0 {
    // 普通方式調用
    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((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 < 100000000; i++) {
            getName.invoke(user, null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println((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((endTime - startTime) + "ms");
    }

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

}


class User {
    private String name;

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

反射操作泛型

  • Java採用泛型擦除的機制來引入泛型,Java中的泛型僅僅是給編譯器javac使用的,確保數據的安全性和免去強制類型轉換問題,但是,一旦編譯完成,所有和泛型有關的類型全部擦除
  • 爲了通過反射操作這些類型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和 WildcardType 幾種類型來代表不能被歸一到Class類中的類型但是又和原始類型齊名的類型

  1. ParameterizedType:表示一種參數化類型,比如Collection< String>
  2. GenericArrayType:表示一種元素類型是參數化類型揮着類型變量的數組類型
  3. TypeVariable:是各種類型變量的公共父接口
  4. WildcardType :代表一種通配符類型表達式

反射操作註解

  • getAnnotations
  • getAnnotation

ORM

什麼是ORM?
objec relationship Mapping -->對象關係映射

package com.ctra.reflection;

import java.lang.annotation.*;

public class Test12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class<?> c1 = Class.forName("com.ctra.reflection.Student2");

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


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


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


    }
}

@Table("db_student")
class Student2{
    @Field(columnName = "db_age",type="int",length = 10)
    private int age;
    @Field(columnName = "db_id",type="int",length = 8)
    private int id;
    @Field(columnName = "db_name",type="int",length = 9)
    public String name;

    public Student2() {
    }

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

    public int getAge() {
        return age;
    }

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

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

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

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

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