spring---反射(java.lang.reflect)


反射簡介

反射是Java的高級特性之一,但是在實際的開發中,使用Java反射的案例卻非常的少,但是反射確實在底層框架中被頻繁的使用。

比如:JDBC中的加載數據庫驅動程序,Spring框架中加載bean對象,以及態代理,這些都使用到反射,因為我們要想理解一些框架的底層原理,反射是我們必須要掌握的。

理解反射我們先從他的概念入手,那么什么是反射呢?

反射就是在運行狀態能夠動態的獲取該類的屬性和方法,並且能夠任意的使用該類的屬性和方法,這種動態獲取類信息以及動態的調用對象的方法的功能就是反射。

實現上面操作的前提是能夠獲取到該類的字節碼對象,也就是.class文件,在反射中獲取class文件的方式有三種:

  1. 類名.class 如:Person.class
  2. 對象.class 如:person.class
  3. Class.forName(全類名)獲取 如:Class.forName("ldc.org. demo.person")

這里有點區別的就是使用1,2(.class)方式獲取Class對象,並不會初始化Class對象,而使用3(forName("全類名"))的方式會自動初始化Class對象。


反射

反射對應到Java中的類庫就是在java.lang.reflect下,在該包下包含着FieldMethodConstructor類。

Field: 表示一個類的屬性信息
Method: 表示類的方法信息
Constructor: 表示的是類的構造方法的信息

在反射中常用的方法,我這里做了一個列舉,當然更加詳細的可以查官方的API文檔進行學習。

方法名 作用
getConstructors() 獲取公共構造器
getDeclaredConstructors() 獲取所有構造器
newInstance() 獲取該類對象
getName() 獲取類名包含包路徑
getSimpleName() 獲取類名不包含包路徑
getFields() 獲取類公共類型的所有屬性
getDeclaredFields() 獲取類的所有屬性
getField(String name) 獲取類公共類型的指定屬性
getDeclaredField(String name) 獲取類全部類型的指定屬性
getMethods() 獲取類公共類型的方法
getDeclaredMethods() 獲取類的所有方法
getMethod(String name, Class[] parameterTypes) 獲得類的特定公共類型方法
getDeclaredClasses() 獲取內部類
getDeclaringClass() 獲取外部類
getPackage() 獲取所在包

User 類:

點擊查看代碼
package com.example.zhangchonghu.demo.controller.reflect;

/**
 * @Description:
 * @author: 張重虎
 * @Date: 2022/2/17 11:20
 * @Version 1.0
 */
public class User {
    private String name;
    public Integer age;

    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;

    }

    private void privateMethod() {
        System.err.println("私有方法執行了");
    }

    public void publicMethod(String param){
        System.err.println("公有方法執行了,參數為:"+param);
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


在User的實體類中,有兩個屬性age和name,並且除了有兩個測試方法privateMethod和publicMethod用於測試私有方法和公共方法的獲取。接着執行如下代碼:
點擊查看代碼
package com.example.zhangchonghu.demo;

import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @Description:
 * @author: 張重虎
 * @Date: 2022/2/17 11:54
 * @Copyright: Xi'an Dian Tong Software Co., Ltd. All Rights Reserved.
 * @Version 1.0
 */
public class TestReflect {
    @Test
    public void test() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException, ClassNotFoundException {
        //1、類名.class
        /**
         * Class clazz = User.class;
         */

        //2、對象.class
        /**
         * User user = new User();
         * Class clazz = user.getClass();
         */

        //3、Class.forName(全類名)獲取
        Class clazz = Class.forName("com.example.zhangchonghu.demo.controller.reflect.User");

        Constructor constructor = clazz.getConstructor(String.class, Integer.class);
        //獲取該類對象並設置屬性的值
        Object obj = constructor.newInstance("張三", 18);

        //獲得類全類名,既包含包路徑
        String fullClassName = clazz.getName();

        //獲得類名
        String className = clazz.getSimpleName();

        //獲取類中公共類型(public)屬性
        Field[] fields = clazz.getFields();
        String fieldName = "";
        for (Field field : fields) {
            //獲取屬性名
            fieldName = field.getName();
            System.out.println("public 屬性名為:" + fieldName);
        }

        //獲取類中所有的屬性,包括 private 屬性
        Field[] fieldsAll = clazz.getDeclaredFields();
        fieldName = "";
        for (Field field : fieldsAll) {
            fieldName = field.getName();
            System.out.println("private 屬性名為:" + fieldName);
        }

        //獲取指定公共屬性值
        Field age = clazz.getField("age");
        Object value = age.get(obj);
        System.err.println("公共指定屬性:" + value);


        //獲得指定的私有屬性值
        Field name = clazz.getDeclaredField("name");
        //設置為true才能獲取私有屬性
        name.setAccessible(true);
        Object value2 = name.get(obj);
        System.err.println("私有指定屬性值:" + value2);

        //獲取所有公共類型方法   這里包括 Object 類的一些方法
        Method[] methods = clazz.getMethods();
        String methodsName = "";
        for (Method method : methods) {
            methodsName = method.getName();
            System.out.println("公開的方法:" + methodsName);
        }

        //獲取該類中的所有方法(包括private)
        Method[] methodsAll = clazz.getDeclaredMethods();
        methodsName = "";
        for (Method method : methodsAll) {
            methodsName = method.getName();
            System.out.println("私有的方法:" + methodsName);
        }

        //獲取並使用指定方法
        Method privateMethod = clazz.getDeclaredMethod("privateMethod");//獲取無參私有方法
        privateMethod.setAccessible(true);
        privateMethod.invoke(obj);//調用方法

        Method publicMethod = clazz.getMethod("publicMethod", String.class);//獲取有參數方法
        publicMethod.invoke(obj, "張三");//調用有參方法
    }
}


運行結果(多次運行結果順序不相同,盲猜是和多線程有關。埋個雷,沒有深入挖掘):
image


反射在jdk 1.5的時候允許對Class對象能夠支持泛型,也稱為泛化Class,具體的使用如下:

Class<User> user= User.class;
//泛化class可以直接得到具體的對象,而不再是Object
Useruser= user.newInstance();

泛化實現了在獲取實例的時候直接就可以獲取到具體的對象,因為在編譯器的時候就會做類型檢查。當然也可以使用通配符的方式,例如:Class<?>


反射優點和缺點

優點:反射可以動態的獲取對象,調用對象的方法和屬性,並不是寫死的,比較靈活,比如你要實例化一個bean對象,你可能會使用new User()寫死在代碼中。

但是使用反射就可以使用class.forName(user).newInstance(),而變量名user可以寫在xml配置文件中,這樣就不用修改源代碼,靈活、可配置。

缺點:反射的性能問題一直是被吐槽的地方,反射是一種解釋操作,用於屬性字段和方法的接入時要遠遠慢於直接使用代碼,因此普通程序也很少使用反射。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM