天天看點

Java反射機制的認識反射機制

學習筆記輸出來源:拉勾教育Java就業急訓營

修改時間:2021年1月8日

作者:pp_x

郵箱:[email protected]

文章目錄

  • 反射機制
    • 基本概念
    • Class類
      • 基本概念
      • 擷取Class對象方式
      • 常用方法
    • Constructor類
      • 基本概念
      • Class常用方法
      • Constructor類常用的方法
    • Field類
      • 基本概念
      • Class的常用方法
      • Field常用方法
    • Method類
      • 基本概念
      • Class類的常用方法
      • Method類的常用方法
    • 擷取其他結構資訊

反射機制

基本概念

  • 通常情況下編寫代碼都是固定的,無論運作多少次執行的結果也是固定的,在某些特殊場合中編寫代碼時不确定要建立什麼類型的對象,也不确定要調用什麼樣的方法,這些都希望通過運作時傳遞的參數來決定,該機制叫做動态程式設計技術,也就是反射機制。
  • 通俗來說,反射機制就是用于動态建立對象并且動态調用方法的機制。
  • 目前主流的架構底層都是采用反射機制實作的。

Class類

基本概念

  • java.lang.Class

    類的執行個體可以用于描述Java應用程式中的類和接口,也就是一種資料類型
  • 該類沒有公共構造方法,該類的執行個體由Java虛拟機和類加載器自動構造完成,本質上就是加載到記憶體中的運作時類。

擷取Class對象方式

  • 使用

    資料類型.class

    的方式擷取對應類型的Class對象
  • 使用

    對象/引用.getClass()

    方式擷取對應類型的Class對象
  • 使用

    包裝類.TYPE

    的方式可以擷取基本資料類型的Class對象
  • 使用使用

    Class.forName()

    的方式來擷取參數指定類型的Class對象
  • 使用類加載器

    ClassLoader

    的方式擷取指定類型的Class對象。

常用方法

方法聲明 功能
static Class<?> forName(String className) 用于擷取參數指定類型對應的Class對象并傳回
T newInstance() 用于建立該Class對象所表示類的新執行個體
package com.lagou.classtest;

public class ClassTest {

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

        // 1.使用資料類型.class的方式可以擷取對應類型的Class對象
        Class c1 = String.class;
        System.out.println("c1 = " + c1); // 自動調用toString方法  class java.lang.String
        c1 = int.class;
        System.out.println("c1 = " + c1); // int
        c1 = void.class;
        System.out.println("c1 = " + c1); // void

        System.out.println("---------------------------------------------------");
        // 2.使用對象.getClass()的方式擷取對應的Class對象
        String str1 = new String("hello");
        c1 = str1.getClass();
        System.out.println("c1 = " + c1); // class java.lang.String

        Integer it1 = 20;
        c1 = it1.getClass();
        System.out.println("c1 = " + c1); // class java.lang.Integer

        int num = 5;
        //num.getClass(); Error: 基本資料類型的變量不能調用方法

        System.out.println("---------------------------------------------------");
        // 3.使用包裝類.TYPE的方式來擷取對應基本資料類型的Class對象
        c1 = Integer.TYPE;
        System.out.println("c1 = " + c1); // int

        c1 = Integer.class;
        System.out.println("c1 = " + c1); // class java.lang.Integer

        System.out.println("---------------------------------------------------");
        // 4.調用Class類中的forName方法來擷取對應的Class對象
        //c1 = Class.forName("String"); // Error  要求寫完整的名稱:包名.類名
        c1 = Class.forName("java.lang.String");
        System.out.println("c1 = " + c1); // class java.lang.String

        c1 = Class.forName("java.util.Date");
        System.out.println("c1 = " + c1); // class java.util.Date

        //c1 = Class.forName("int");
        //System.out.println("c1 = " + c1); // 不能擷取基本資料類型的Class對象

        System.out.println("---------------------------------------------------");
        // 5.使用類加載器的方式來擷取Class對象
        ClassLoader classLoader = ClassTest.class.getClassLoader();
        System.out.println("classLoader = " + classLoader); // null
        c1 = classLoader.loadClass("java.lang.String");
        System.out.println("c1 = " + c1); // class java.lang.String
    }
}
           

Constructor類

基本概念

  • java.lang.reflect.Constructor

    類主要用于描述擷取到的構造方法資訊

Class常用方法

方法聲明 功能
Constructor getConstructor(Class<?>… parameterTypes) 用于擷取此Class對象所表示類型中參數指定的公共構造方法
Constructor<?>[] getConstructors() 用于擷取此Class對象所表示類型中所有的公共構造方法

Constructor類常用的方法

方法聲明 功能
T newInstance(Object… initargs) 使用此Constructor對象描述的構造方法來構造Class對象代表類型的新執行個體
int getModifiers() 擷取方法的通路修飾符
String getName() 擷取方法的名稱
Class<?>[] getParameterTypes() 擷取方法所有參數的類型
package com.lagou.classtest;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Scanner;

public class PersonConstructorTest {
    public static void main(String[] args) throws Exception {
        //  1、使用原始形式以無參構造形式構造Person的對象
        System.out.println("----------------使用原始形式以無參構造形式構造Person的對象--------------");
        Person p1 = new Person();
        System.out.println(p1.toString());

        // 2、使用反射機制以無參形式構造Person的對象
        System.out.println("----------------------使用反射機制以無參形式構造Person的對象---------------");
        // 建立對象的類型可以從鍵盤輸入
//        System.out.println("請輸入要建立對象的類型:");
//        Scanner sc = new Scanner(System.in);
//        String str1 = sc.next();
//        Class c1 = Class.forName("com.lagou.classtest.Person");
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("d:/a.txt")));
        String str1 = br.readLine();
        Class c1 = Class.forName(str1);
        //System.out.println("無參方式建立的對象是:" + c1.newInstance()); // null 0
        // 擷取Class對象對應類中的無參構造方法,也就是Person類中的無參構造方法
        Constructor constructor = c1.getConstructor();
        // 使用擷取到的無參構造方法來構造對應類型的對象,也就是Person類型的對象
        System.out.println("無參方式建立的對象是:" + constructor.newInstance());
        br.close();
        //3、
        System.out.println("--------------------使用原始方式以有參方式構造Person類型的對象并列印-------------------------------");
        // 3.使用原始方式以有參方式構造Person類型的對象并列印
        Person p2 = new Person("liuxiao", 18);
        System.out.println("有參方式構造的對象是:" + p2); // zhangfei 30
        System.out.println("---------------------4.使用反射機制以有參方式構造Person類型的對象并列印------------------------------");
    // 4.使用反射機制以有參方式構造Person類型的對象并列印
        // 擷取Class對象對應類中的有參構造方法,也就是Person類中的有參構造方法
        Constructor constructor1 = c1.getConstructor(String.class, int.class);
        // 使用擷取到的有參構造方法來構造對應類型的對象,也就是Person類型的對象
        // newInstance方法中的實參是用于給有參構造方法的形參進行初始化的,也就是給name和age進行初始化的
        System.out.println("有參方式構造的對象是:" + constructor1.newInstance("zhangfei", 30)); // zhangfei 30

        System.out.println("--------------------// 5.使用反射機制擷取Person類中所有的公共構造方法并列印-------------------------------");
         5.使用反射機制擷取Person類中所有的公共構造方法并列印
        Constructor[] constructors = c1.getConstructors();
        for (Constructor ct: constructors
             ) {
            System.out.println("構造方法的通路修飾符是:"+ct.getModifiers());
            System.out.println("構造方法的名稱是:"+ct.getName());
            Class[] parameterTypes = ct.getParameterTypes();
            for (Class c:parameterTypes
                 ) {
                System.out.print(c +", ");
            }
            System.out.println();
        }
    }
}

           

Field類

基本概念

  • java.lang.reflect.Field

    類主要用于描述擷取到的單個成員變量

Class的常用方法

方法聲明 功能
Field getDeclaredField(String name) 用于擷取此Class對象所表示類中參數指定的單個成員變量資訊
Field[] getDeclaredFields() 用于擷取此Class對象所表示類中所有成員變量資訊

Field常用方法

方法聲明 功能
Object get(Object obj) 擷取對象obj中此Field類所代表的成員變量的數值
void set(Object obj, Object value) 修改對象obj中此Field類中所代表成員變量的數值為value
void setAccessible(boolean flag) 當flag為true時,反射對象在使用時 應該取消Java語言通路檢查
int getModifiers() 擷取成員變量的通路修飾符
Class<?> getType() 擷取成員變量的資料類型
String getName() 擷取成員變量的名稱
package com.lagou.classtest;

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

public class PersonFieldTest {

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

        // 1.使用原始方式來構造對象以及擷取成員變量的數值并列印
        Person p1 = new Person("zhangfei", 30);
        //System.out.println("擷取到的成員變量數值為:" + p1.name); // zhangfei

        System.out.println("-------------------------------------------------------");
        // 2.使用反射機制來構造對象以及擷取成員變量的數值并列印
        // 2.1 擷取Class對象
        Class c1 = Class.forName("com.lagou.classtest.Person");
        // 2.2 根據Class對象擷取對應的有參構造方法
        Constructor constructor = c1.getConstructor(String.class, int.class);
        // 2.3 使用有參構造方法來得到Person類型的對象
        Object object = constructor.newInstance("zhangfei", 30);
        // 2.4 根據Class對象擷取對應的成員變量資訊
        Field field = c1.getDeclaredField("name");
        // 設定Java語言通路檢查的取消  暴力反射
        field.setAccessible(true);
        // 2.5 使用Person類型的對象來擷取成員變量的數值并列印
        // 擷取對象object中名字為field成員變量的數值,也就是成員變量name的數值
        System.out.println("擷取到的成員變量數值為:" + field.get(object)); // zhangfei

        System.out.println("-------------------------------------------------------");
        // 3.使用原始方式修改指定對象中成員變量的數值後再次列印
        //p1.name = "guanyu";
        //System.out.println("修改後成員變量的數值為:" + p1.name); // guanyu

        System.out.println("-------------------------------------------------------");
        // 4.使用反射機制修改指定對象中成員變量的數值後再次列印
        // 表示修改對象object中名字為field成員變量的數值為guanyu,也就是成員變量name的數值為guanyu
        field.set(object, "guanyu");
        System.out.println("修改後成員變量的數值為:" + field.get(object)); // guanyu

        System.out.println("--------------------------擷取Class對象對應類中所有的成員變量-----------------------------");
        // 5.擷取Class對象對應類中所有的成員變量
        Field[] declaredFields = c1.getDeclaredFields();
        for (Field ft : declaredFields) {
            System.out.println("擷取到的通路修飾符為:" + ft.getModifiers());
            System.out.println("擷取到的資料類型為:" + ft.getType());
            System.out.println("擷取到的成員變量名稱是:" + ft.getName());
            System.out.println("-----------------");
        }
    }
}

           

Method類

基本概念

  • java.lang.reflect.Method

    類主要用于描述單個成員方法的資訊

Class類的常用方法

方法聲明 功能
Method getMethod(String name, Class<?>… parameterTypes) 使用者擷取該Class對象表示類中名字為name,參數清單為parameterTypes的成員方法
Method[] getMethods() 用于擷取該Class對象表示類中所有公共成員方法

Method類的常用方法

方法聲明 功能
Object invoke(Object obj, Object… args) 使用obj對象來調用此類表示的方法,args代表方法需要傳遞的實參清單,傳回方法的傳回值
int getModifiers() 擷取方法的通路修飾符
Class<?> getReturnType() 擷取方法的傳回類型
String getName() 擷取方法的名稱
Class<?>[] getParameterTypes() 擷取方法的參數清單
Class<?>[] getExceptionTypes() 擷取方法的異常資訊
package com.lagou.classtest;

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

public class PersonMethodTest {
    public static void main(String[] args) throws Exception{
        //1、使用原始方法構造對象并調用方法
        Person p1 = new Person("liuxiao",18);
        System.out.println("調用方法的傳回值是:"+p1.getName());
        System.out.println("--------------");
        //使用反射機制
        //擷取Class對象
        Class c1 = Class.forName("com.lagou.classtest.Person");
        //通過Class對象擷取對應的有參構造方法
        Constructor constructor = c1.getConstructor(String.class,int.class);
        //使用有參構造方法構造對象
        Object object = constructor.newInstance("liuxiao",18);
        //Object object2 = constructor.newInstance("xiaoliu",19);
        //根據Class對象擷取成員方法對象
        Method method = c1.getMethod("getName");
        //根據成員方法對象調用成員方法進行列印
        System.out.println("調用方法的傳回值是:"+method.invoke(object));
        System.out.println("------------------------使用反射機制來擷取類中的所有成員方法并列印------------------------------");
        // 3.使用反射機制來擷取類中的所有成員方法并列印
        Method[] methods = c1.getMethods();
        for (Method mt : methods) {
            System.out.println("成員方法的修飾符是:" + mt.getModifiers());
            System.out.println("成員方法的傳回值類型是:" + mt.getReturnType());
            System.out.println("成員方法的名稱是:" + mt.getName());
            System.out.println("成員方法形參清單的類型是:");
            Class<?>[] parameterTypes = mt.getParameterTypes();
            for (Class ct : parameterTypes) {
                System.out.print(ct + " ");
            }
            System.out.println();
            System.out.println("成員方法的異常類型清單是:");
            Class<?>[] exceptionTypes = mt.getExceptionTypes();
            for (Class ct: exceptionTypes) {
                System.out.print(ct + " ");
            }
            System.out.println();
            System.out.println("---------------------------------------------------");
        }
    }

}

           

擷取其他結構資訊

方法聲明 功能
Package getPackage() 擷取包資訊
Class<? super T> getSuperclass() 擷取父類資訊
Class<?>[] getInterfaces() 擷取接口資訊
Annotation[] getAnnotations() 擷取注解資訊
Type[] getGenericInterfaces() 擷取檢討資訊
package com.lagou.classtest;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

public class StudentTest {

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

        // 擷取Student類型的Class對象
        Class c1 = Class.forName("com.lagou.classtest.Student");
        System.out.println("擷取到的包資訊是:" + c1.getPackage());
        System.out.println("擷取到的父類資訊是:" + c1.getSuperclass());

        System.out.println("-------------------------------------------------");
        System.out.println("擷取到的接口資訊是:");
        Class[] interfaces = c1.getInterfaces();
        for (Class ct : interfaces) {
            System.out.print(ct + " ");
        }
        System.out.println();

        System.out.println("-------------------------------------------------");
        System.out.println("擷取到的注解資訊是:");
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation at : annotations) {
            System.out.print(at + " ");
        }
        System.out.println();

        System.out.println("-------------------------------------------------");
        System.out.println("擷取到的泛型資訊是:");
        Type[] genericInterfaces = c1.getGenericInterfaces();
        for (Type tt : genericInterfaces) {
            System.out.print(tt + " ");
        }
        System.out.println();
    }
}

           

執行結果

擷取到的包資訊是:package com.lagou.classtest
擷取到的父類資訊是:class com.lagou.classtest.Person
-------------------------------------------------
擷取到的接口資訊是:
interface java.lang.Comparable interface java.io.Serializable 
-------------------------------------------------
擷取到的注解資訊是:
@com.lagou.classtest.MyAnnotation() 
-------------------------------------------------
擷取到的泛型資訊是:
interface java.lang.Comparable interface java.io.Serializable