java反射機制詳細解析(總結分享)

2022-05-17 19:00:49
本篇文章給大家帶來了關於的相關知識,其中主要介紹了關於反射機制的詳細解析,包括了反射機制的概述、class類的理解、建立執行時類的物件等等內容,下面一起來看一下,希望對大家有幫助。

推薦學習:《》

一、Java反射機制概述

1. Java Reflection

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

(2)載入完類之後,在堆記憶體的方法區中就產生了一個Class型別的物件(一個類只有一個Class物件),這個物件就包含了完整的類的結構資訊。我們可以通過這個物件看到類的結構。這個物件就像一面鏡子,透過這個鏡子看到類的結構,所以,我們形象的稱之為:反射。
在這裡插入圖片描述

2. 動態語言 vs 靜態語言

(1)動態語言

是一類在執行時可以改變其結構的語言:例如新的函數、物件、甚至程式碼可以 被引進,已有的函數可以被刪除或是其他結構上的變化。通俗點說就是在執行時程式碼可以根據某些條件改變自身結構。

主要動態語言:Object-C、C#、JavaScript、PHP、Python、Erlang。

(2)靜態語言

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

(3)Java反射機制研究及應用

Java反射機制提供的功能

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

反射相關的主要API

  1. java.lang.Class:代表一個類
  2. java.lang.reflect.Method:代表類的方法
  3. java.lang.reflect.Field:代表類的成員變數
  4. java.lang.reflect.Constructor:代表類的構造器  … …

二、 Class類的理解

1. 類的載入過程

1.1 初步瞭解

程式經過javac.exe命令以後,會生成一個或多個位元組碼檔案(.class結尾)。
接著我們使用java.exe命令對某個位元組碼檔案進行解釋執行。相當於將某個位元組碼檔案載入到記憶體中。此過程就稱為類的載入。載入到記憶體中的類,我們就稱為執行時類,此執行時類,就作為Class的一個範例。

換句話說,Class的範例就對應著一個執行時類。

載入到記憶體中的執行時類,會快取一定的時間。在此時間之內,我們可以通過不同的方式來獲取此執行時類。

1.2 類的載入過程圖解

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

在這裡插入圖片描述

類的載入:將class檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉換成方法區的執行時資料結構,然後生成一個代表這個類的java.lang.Class物件,作為方法區中類資料的存取入口(即參照地址)。所有需要存取和使用類資料只能通過這個Class物件。這個載入的過程需要類載入器參與。

類的連結:將Java類的二進位制程式碼合併到JVM的執行狀態之中的過程。
● 驗證:確保載入的類資訊符合JVM規範,例如:以cafe開頭,沒有安全方面的問題
● 準備:正式為類變數(static)分配記憶體並設定類變數預設初始值的階段,這些記憶體 都將在方法區中進行分配。
● 解析:虛擬機器器常數池內的符號參照(常數名)替換為直接參照(地址)的過程。

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

public class ClassLoadingTest {
public static void main(String[] args) {
System.out.println(A.m);
} }
class A {
static { m = 300;
}
static int m = 100;
}
//第二步:連結結束後m=0
//第三步:初始化後,m的值由<clinit>()方法執行決定
// 這個A的類構造器<clinit>()方法由類變數的賦值和靜態程式碼塊中的語句按照順序合併產生,類似於
// <clinit>(){
// m = 300;
// m = 100;
// }

1.3 瞭解:什麼時候會發生類初始化?

類的主動參照(一定會發生類的初始化)

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

類的被動參照(不會發生類的初始化)

  1. 當存取一個靜態域時,只有真正宣告這個域的類才會被初始化
  2. 當通過子類參照父類別的靜態變數,不會導致子類初始化
  3. 通過陣列定義類參照,不會觸發此類的初始化
  4. 參照常數不會觸發此類的初始化(常數在連結階段就存入呼叫類的常數池中了)

1.4 類載入器的作用

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

在這裡插入圖片描述

1.5 JVM中不同型別的類的載入器

在這裡插入圖片描述

1.6 程式碼演示

不同型別的類的載入器:

 @Test
    public void test1(){
        //對於自定義類,使用系統類載入器進行載入
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2:系統類載入器
        //呼叫系統類載入器的getParent():獲取擴充套件類載入器
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@279f2327:擴充套件類載入器
        //呼叫擴充套件類載入器的getParent():無法獲取引導類載入器
        //引導類載入器主要負責載入java的核心類庫,無法載入自定義類的。
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);//null

        ClassLoader classLoader3 = String.class.getClassLoader();
        System.out.println(classLoader3);//null

    }

使用系統類載入器讀取Properties組態檔。

 /*
    Properties:用來讀取組態檔。

     */
    @Test
    public void test2() throws Exception {

        Properties pros =  new Properties();
        //此時的檔案預設在當前的module下。
        //讀取組態檔的方式一://        FileInputStream fis = new FileInputStream("jdbc.properties");//        FileInputStream fis = new FileInputStream("src\\jdbc1.properties");//        pros.load(fis);

        //讀取組態檔的方式二:使用ClassLoader
        //組態檔預設識別為:當前module的src下
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
        pros.load(is);


        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        System.out.println("user = " + user + ",password = " + password);
    }}

2. 何為Class類?

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

public final Class getClass()

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

在這裡插入圖片描述

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

Class本身也是一個類

Class 物件只能由系統建立物件

一個載入的類在 JVM中只會有一個Class範例

一個Class物件對應的是一個載入到JVM中的一個.class檔案

每個類的範例都會記得自己是由哪個Class 範例所生成

通過Class可以完整地得到一個類中的所有被載入的結構

Class類是Reflection的根源,針對任何你想動態載入、執行的類,唯有先獲得相應的

3. Class類的常用方法方法

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

3. 哪些型別可以有Class物件?

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

三、獲取Class類範例的四種方法

1. 呼叫執行時類的屬性:.class

前提:若已知具體的類,通過類的class屬性獲取,該方法最為安全可靠, 程式效能最高
範例: Class clazz1 = String.class;

2. 通過執行時類的物件,呼叫getClass()

前提:已知某個類的範例,呼叫該範例的getClass()方法獲取Class物件
範例:Class clazz = 「www.atguigu.com」.getClass();

3.呼叫Class的靜態方法:forName(String classPath)

前提:已知一個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName() 獲取,可能丟擲ClassNotFoundException
範例: Class clazz = Class.forName(「java.lang.String」);

4. 使用類的載入器:ClassLoader

範例:
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(「類的全類名」);

5. 程式碼演示

@Testpublic void test1() throws ClassNotFoundException {
            //方式一:呼叫執行時類的屬性:.class
            Class clazz1 = Person.class;
            System.out.println(clazz1);//class com.jiaying.java1.Person
            //方式二:通過執行時類的物件,呼叫getClass()
            Person p1 = new Person();
            Class clazz2 = p1.getClass();
            System.out.println(clazz2);//class com.jiaying.java1.Person

            //方式三:呼叫Class的靜態方法:forName(String classPath)
            Class clazz3 = Class.forName("com.jiaying.java1.Person");
            Class clazz5 = Class.forName("java.lang.String");
            System.out.println(clazz3);//class com.jiaying.java1.Person
            System.out.println(clazz5);//class java.lang.String

            System.out.println(clazz1 == clazz2);//true
            System.out.println(clazz1 == clazz3);//true

            //方式四:使用類的載入器:ClassLoader  (瞭解)
            ClassLoader classLoader = ReflectionTest.class.getClassLoader();
            Class clazz4 = classLoader.loadClass("com.jiaying.java1.Person");
            System.out.println(clazz4);//class com.jiaying.java1.Person
            System.out.println(clazz1 == clazz4);//true}

四、 建立執行時類的物件

1. 引入

有了Class物件,能做什麼?

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

  1. 類必須有一個無引數的構造器。
  2. 類的構造器的存取許可權需要足夠。

難道沒有無參的構造器就不能建立物件了嗎?
不是!只要在操作的時候明確的呼叫類中的構造器,並將引數傳遞進去之後,才可以範例化操作。
步驟如下:

  1. 通過Class類的getDeclaredConstructor(Class … parameterTypes)取得本類的指定形參型別的構造器
  2. 向構造器的形參中傳遞一個物件陣列進去,裡面包含了構造器中所需的各個引數。
  3. 通過Constructor範例化物件。

2. 語法步驟

(1)根據全類名獲取對應的Class物件

String name = 「atguigu.java.Person";Class clazz = null;clazz = Class.forName(name);

(2)呼叫指定引數結構的構造器,生成Constructor的範例

Constructor con = clazz.getConstructor(String.class,Integer.class);

(3)通過Constructor的範例建立對應類的物件,並初始化類屬性

Person p2 = (Person) con.newInstance("Peter",20);System.out.println(p2);

3. 程式碼演示

 @Test
    public void test1() throws IllegalAccessException, InstantiationException {

        Class<Person> clazz = Person.class;
        /*
        newInstance():呼叫此方法,建立對應的執行時類的物件。內部呼叫了執行時類的空參的構造器。

        要想此方法正常的建立執行時類的物件,要求:
        1.執行時類必須提供空參的構造器
        2.空參的構造器的存取許可權得夠。通常,設定為public。


        在javabean中要求提供一個public的空參構造器。原因:
        1.便於通過反射,建立執行時類的物件
        2.便於子類繼承此執行時類時,預設呼叫super()時,保證父類別有此構造器

         */
        Person obj = clazz.newInstance();
        System.out.println(obj);

    }

4. 體會反射的動態性

//體會反射的動態性
    @Test
    public void test2(){

        for(int i = 0;i < 100;i++){
            int num = new Random().nextInt(3);//0,1,2
            String classPath = "";
            switch(num){
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "com.atguigu.java.Person";
                    break;
            }

            try {
                Object obj = getInstance(classPath);
                System.out.println(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /*
    建立一個指定類的物件。
    classPath:指定類的全類名
     */
    public Object getInstance(String classPath) throws Exception {
       Class clazz =  Class.forName(classPath);
       return clazz.newInstance();
    }}

五、獲取執行時類的完整結構

提供具有豐富內容的Person

//介面public interface MyInterface {
    void info();}//註解@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.RUNTIME)public @interface MyAnnotation {
    String value() default "hello";}//父類別public class Creature<T> implements Serializable {
    private char gender;
    public double weight;

    private void breath(){
        System.out.println("生物呼吸");
    }

    public void eat(){
        System.out.println("生物吃東西");
    }}//Person類@MyAnnotation(value="hi")public class Person extends Creature<String> implements Comparable<String>,MyInterface{

    private String name;
    int age;
    public int id;

    public Person(){}

    @MyAnnotation(value="abc")
    private Person(String name){
        this.name = name;
    }

     Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    @MyAnnotation
    private String show(String nation){
        System.out.println("我的國籍是:" + nation);
        return nation;
    }

    public String display(String interests,int age) throws NullPointerException,ClassCastException{
        return interests + age;
    }


    @Override
    public void info() {
        System.out.println("我是一個人");
    }

    @Override
    public int compareTo(String o) {
        return 0;
    }

    private static void showDesc(){
        System.out.println("我是一個可愛的人");
    }

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

1. 獲取當前執行時類的屬性結構

方法作用
public Field[] getFields()返回此Class物件所表示的類或介面的publicField
public Field[] getDeclaredFields()返回此Class物件所表示的類或介面的全部Field
  • Field方法中:
方法作用
public int getModifiers()以整數形式返回此Field的修飾符
public Class<?> getType()得到Field的屬性型別
public String getName()返回Field的名稱
    @Test
    public void test1(){

        Class clazz = Person.class;

        //獲取屬性結構
        //getFields():獲取當前執行時類及其父類別中宣告為public存取許可權的屬性
        Field[] fields = clazz.getFields();
        for(Field f : fields){
            System.out.println(f);
        }
        System.out.println();

        //getDeclaredFields():獲取當前執行時類中宣告的所有屬性。(不包含父類別中宣告的屬性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
            System.out.println(f);
        }
    }

    //許可權修飾符  資料型別 變數名
    @Test
    public void test2(){
        Class clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
            //1.許可權修飾符
            int modifier = f.getModifiers();
            System.out.print(Modifier.toString(modifier) + "\t");

            //2.資料型別
            Class type = f.getType();
            System.out.print(type.getName() + "\t");

            //3.變數名
            String fName = f.getName();
            System.out.print(fName);

            System.out.println();
        }
    }}

2. 獲取當前執行時類的方法結構

方法作用
public Method[] getMethods()返回此Class物件所表示的類或介面的public的方法
public Method[] getDeclaredMethods()返回此Class物件所表示的類或介面的全部方法
  • Method類中:
方法作用
public Class<?> getReturnType()取得全部的返回值
public Class<?>[] getParameterTypes()取得全部的引數
public int getModifiers()取得修飾符
public Class<?>[] getExceptionTypes()取得異常資訊
    @Test
    public void test1(){

        Class clazz = Person.class;

        //getMethods():獲取當前執行時類及其所有父類別中宣告為public許可權的方法
        Method[] methods = clazz.getMethods();
        for(Method m : methods){
            System.out.println(m);
        }
        System.out.println();
        //getDeclaredMethods():獲取當前執行時類中宣告的所有方法。(不包含父類別中宣告的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m : declaredMethods){
            System.out.println(m);
        }
    }
  /*
    @Xxxx
    許可權修飾符  返回值型別  方法名(引數型別1 形參名1,...) throws XxxException{}
     */
    @Test
    public void test2(){
        Class clazz = Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m : declaredMethods){
            //1.獲取方法宣告的註解
            Annotation[] annos = m.getAnnotations();
            for(Annotation a : annos){
                System.out.println(a);
            }

            //2.許可權修飾符
            System.out.print(Modifier.toString(m.getModifiers()) + "\t");

            //3.返回值型別
            System.out.print(m.getReturnType().getName() + "\t");

            //4.方法名
            System.out.print(m.getName());
            System.out.print("(");
            //5.形參列表
            Class[] parameterTypes = m.getParameterTypes();
            if(!(parameterTypes == null && parameterTypes.length == 0)){
                for(int i = 0;i < parameterTypes.length;i++){

                    if(i == parameterTypes.length - 1){
                        System.out.print(parameterTypes[i].getName() + " args_" + i);
                        break;
                    }

                    System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
                }
            }

            System.out.print(")");

            //6.丟擲的異常
            Class[] exceptionTypes = m.getExceptionTypes();
            if(exceptionTypes.length > 0){
                System.out.print("throws ");
                for(int i = 0;i < exceptionTypes.length;i++){
                    if(i == exceptionTypes.length - 1){
                        System.out.print(exceptionTypes[i].getName());
                        break;
                    }

                    System.out.print(exceptionTypes[i].getName() + ",");
                }
            }


            System.out.println();
        }



    }}

3. 獲取當前執行時類的構造器結構

方法作用
public Constructor<T>[] getConstructors()返回此 Class 物件所表示的類的所有public構造方法。
public Constructor<T>[] getDeclaredConstructors()返回此 Class 物件表示的類宣告的所有構造方法。
  • Constructor類中:
方法作用
public int getModifiers()取得修飾符
public String getName()取得方法名稱
public Class<?>[] getParameterTypes()取得引數的型別
/*
    獲取構造器結構

     */
    @Test
    public void test1(){

        Class clazz = Person.class;
        //getConstructors():獲取當前執行時類中宣告為public的構造器
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor c : constructors){
            System.out.println(c);
        }

        System.out.println();
        //getDeclaredConstructors():獲取當前執行時類中宣告的所有的構造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for(Constructor c : declaredConstructors){
            System.out.println(c);
        }

    }
 /*
    獲取執行時類的父類別

     */
    @Test
    public void test2(){
        Class clazz = Person.class;

        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }

    /*
    獲取執行時類的帶泛型的父類別

     */
    @Test
    public void test3(){
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
    }

    /*
    獲取執行時類的帶泛型的父類別的泛型


    程式碼:邏輯性程式碼  vs 功能性程式碼
     */
    @Test
    public void test4(){
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        //獲取泛型型別
        Type[] actualTypeArguments = paramType.getActualTypeArguments();//        System.out.println(actualTypeArguments[0].getTypeName());
        System.out.println(((Class)actualTypeArguments[0]).getName());
    }/*
    獲取執行時類實現的介面
     */
    @Test
    public void test5(){
        Class clazz = Person.class;

        Class[] interfaces = clazz.getInterfaces();
        for(Class c : interfaces){
            System.out.println(c);
        }

        System.out.println();
        //獲取執行時類的父類別實現的介面
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for(Class c : interfaces1){
            System.out.println(c);
        }

    }
    /*
        獲取執行時類所在的包

     */
    @Test
    public void test6(){
        Class clazz = Person.class;

        Package pack = clazz.getPackage();
        System.out.println(pack);
    }

    /*
        獲取執行時類宣告的註解

     */
    @Test
    public void test7(){
        Class clazz = Person.class;

        Annotation[] annotations = clazz.getAnnotations();
        for(Annotation annos : annotations){
            System.out.println(annos);
        }
    }}

六、呼叫執行時類的指定結構

關於setAccessible方法的使用

MethodFieldConstructor物件都有setAccessible()方法。

setAccessible啟動和禁用存取安全檢查的開關。

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

提高反射的效率。如果程式碼中必須用反射,而該句程式碼需要頻繁的被 呼叫,那麼請設定為true,使得原本無法存取的私有成員也可以存取,引數值為false則指示反射的物件應該實施Java語言存取檢查。

1. 呼叫執行時類中指定的屬性

在反射機制中,可以直接通過Field類操作類中的屬性,通過Field類提供的set()get()方法就可以完成設定和取得屬性內容的操作。

方法作用
public Field getField(String name)返回此Class物件表示的類或介面的指定的publicField
public Field getDeclaredField(String name)返回此Class物件表示的類或介面的指定的Field

在Field中:

方法作用
public Object get(Object obj)取得指定物件obj上此Field的屬性內容
public void set(Object obj,Object value)設定指定物件obj上此Field的屬性內容

程式碼演示:

public class ReflectionTest {
    @Test
    public void testField() throws Exception {
        Class clazz = Person.class;

        //建立執行時類的物件
        Person p = (Person) clazz.newInstance();


        //獲取指定的屬性:要求執行時類中屬性宣告為public
        //通常不採用此方法
        Field id = clazz.getField("id");

        /*
        設定當前屬性的值

        set():引數1:指明設定哪個物件的屬性   引數2:將此屬性值設定為多少
         */

        id.set(p,1001);

        /*
        獲取當前屬性的值
        get():引數1:獲取哪個物件的當前屬性值
         */
        int pId = (int) id.get(p);
        System.out.println(pId);


    }
    /*
    如何操作執行時類中的指定的屬性 -- 需要掌握
     */
    @Test
    public void testField1() throws Exception {
        Class clazz = Person.class;

        //建立執行時類的物件
        Person p = (Person) clazz.newInstance();

        //1. getDeclaredField(String fieldName):獲取執行時類中指定變數名的屬性
        Field name = clazz.getDeclaredField("name");

        //2.保證當前屬性是可存取的
        name.setAccessible(true);
        //3.獲取、設定指定物件的此屬性值
        name.set(p,"Tom");

        System.out.println(name.get(p));
    }

2. 呼叫執行時類中的指定的方法

通過反射,呼叫類中的方法,通過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[] argsnull
若原方法宣告為private,則需要在呼叫此invoke()方法前,顯式呼叫 方法物件的setAccessible(true)方法,將可存取private的方法。

程式碼演示:

 /*
    如何操作執行時類中的指定的方法 -- 需要掌握
     */
    @Test
    public void testMethod() throws Exception {

        Class clazz = Person.class;

        //建立執行時類的物件
        Person p = (Person) clazz.newInstance();

        /*
        1.獲取指定的某個方法
        getDeclaredMethod():引數1 :指明獲取的方法的名稱  引數2:指明獲取的方法的形參列表
         */
        Method show = clazz.getDeclaredMethod("show", String.class);
        //2.保證當前方法是可存取的
        show.setAccessible(true);

        /*
        3. 呼叫方法的invoke():引數1:方法的呼叫者  引數2:給方法形參賦值的實參
        invoke()的返回值即為對應類中呼叫的方法的返回值。
         */
        Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
        System.out.println(returnValue);

        System.out.println("*************如何呼叫靜態方法*****************");

        // private static void showDesc()

        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        //如果呼叫的執行時類中的方法沒有返回值,則此invoke()返回null//        Object returnVal = showDesc.invoke(null);
        Object returnVal = showDesc.invoke(Person.class);
        System.out.println(returnVal);//null

    }

3. 呼叫執行時類中的指定的構造器

程式碼演示:

  /*
    如何呼叫執行時類中的指定的構造器
     */
    @Test
    public void testConstructor() throws Exception {
        Class clazz = Person.class;

        //private Person(String name)
        /*
        1.獲取指定的構造器
        getDeclaredConstructor():引數:指明構造器的參數列
         */

        Constructor constructor = clazz.getDeclaredConstructor(String.class);

        //2.保證此構造器是可存取的
        constructor.setAccessible(true);

        //3.呼叫此構造器建立執行時類的物件
        Person per = (Person) constructor.newInstance("Tom");
        System.out.println(per);

    }}

推薦學習:《》

以上就是java反射機制詳細解析(總結分享)的詳細內容,更多請關注TW511.COM其它相關文章!