首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 反射機制深度解析:從 Class 對象到動態創建、結構探測與屬性/方法操作實戰

     編輯:Java 程式設計入門

前言

反射是 Java 動態性的基石,框架(Spring、Hibernate、MyBatis)的核心實現均建立於此。本文從「什麼是反射」切入,系統講解 Class 對象獲取、動態實例化、結構探測(構造器、方法、字段、父類、接口)、私有成員訪問、數組操作等完整體系,並給出生產級代碼模板。

核心概念:反射的本質

反射 = 運行時根據已實例化對象(或類名)還原類的完整元數據,並支持動態創建實例、調用方法、訪問字段。

關鍵點:Class 對象是反射的入口,每個類加載後在堆中生成唯一的 Class 實例,包含類的完整定義信息。

Class 對象的三種獲取方式


public class ClassObtainDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式一:對象.getClass() —— 最常用,已有實例時
        String str = "hello";
        Class c1 = str.getClass();
        
        // 方式二:類.class —— 編譯期已知類,無需實例,性能最優
        Class c2 = String.class;
        
        // 方式三:Class.forName("全限定名") —— 運行時動態決定類名,框架核心用法
        Class c3 = Class.forName("java.lang.String");
        
        System.out.println(c1 == c2 && c2 == c3); // true:同一類加載器下 Class 單例
    }
}

動態實例化:無參與有參構造器

1. 無參構造:Class.newInstance() (已棄用) / Constructor.newInstance()


public class InstantiateDemo {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("java.lang.String");
        
        // 現代方式:獲取無參構造器 -> 實例化
        Constructor noArgConstructor = clazz.getDeclaredConstructor();
        String obj = (String) noArgConstructor.newInstance(); // ""
        System.out.println("無參構造: 長度=" + obj.length());
    }
}

2. 有參構造:精確匹配參數類型


// String(byte[] bytes) 構造器
Class clazz = Class.forName("java.lang.String");
Constructor constructor = clazz.getDeclaredConstructor(byte[].class);
byte[] bytes = {72, 101, 108, 108, 111}; // "Hello"
String obj = (String) constructor.newInstance((Object) bytes); // 注意:傳數組需轉 Object 避免展開
System.out.println("有參構造: " + obj); // Hello

類結構全維度探測

1. 構造器獲取


Class clazz = Boolean.class;
Constructor[] constructors = clazz.getDeclaredConstructors(); // 所有聲明構造器(含private)
// Constructor[] publicConstructors = clazz.getConstructors(); // 僅public
for (Constructor c : constructors) {
    System.out.println(c + " | 參數: " + Arrays.toString(c.getParameterTypes()));
}

2. 方法獲取與調用


Class clazz = String.class;

// 獲取所有聲明方法(含private)
Method[] methods = clazz.getDeclaredMethods(); 
// 僅public方法(含繼承)
Method[] publicMethods = clazz.getMethods(); 

// 精確獲取並調用:public void print(int i)
Method printMethod = clazz.getDeclaredMethod("print", int.class); // 假設類有此方法
printMethod.invoke(targetObject, 10); // 實例方法傳對象

// 靜態方法調用:第一參數傳 null
Method staticMethod = clazz.getDeclaredMethod("staticMethod", String.class);
staticMethod.invoke(null, "args");

3. 字段獲取與讀寫(含私有)


class Person {
    public String name;
    private int age;
    public Person(String name, int age) { this.name = name; this.age = age; }
}

Person p = new Person("張三", 25);
Class clazz = p.getClass();

// 讀取公有字段
Field nameField = clazz.getField("name");
System.out.println("姓名: " + nameField.get(p)); // 張三

// 讀寫私有字段:暴力反射
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // 關鍵:繞過訪問檢查
System.out.println("年齡: " + ageField.get(p)); // 25
ageField.set(p, 26); // 修改
System.out.println("修改後: " + ageField.get(p)); // 26

4. 繼承體系探測


Class clazz = ArrayList.class;

// 父類
Class superClass = clazz.getSuperclass(); // AbstractList
System.out.println("父類: " + superClass);

// 實現接口
Class[] interfaces = clazz.getInterfaces(); // List, Collection, Iterable, Serializable, Cloneable, RandomAccess
System.out.println("接口: " + Arrays.toString(interfaces));

// 泛型父類/接口 (ParameterizedType)
Type genericSuper = clazz.getGenericSuperclass();
Type[] genericInterfaces = clazz.getGenericInterfaces();

數組反射操作


int[] arr = {1, 2, 3, 4, 5};
Class componentType = arr.getClass().getComponentType(); // int.class
System.out.println("元素類型: " + componentType.getName()); // int

// 通過 java.lang.Array 操作
int len = Array.getLength(arr); // 5
for (int i = 0; i < len; i++) {
    System.out.print(Array.get(arr, i) + " "); // 1 2 3 4 5
}

// 修改元素
Array.set(arr, 0, 99);
System.out.println("\n修改後首元素: " + Array.get(arr, 0)); // 99

// 動態創建數組
Object newArr = Array.newInstance(String.class, 3); // String[3]
Array.set(newArr, 0, "A");

反射在工廠模式中的應用:動態實例化


public class ReflectionFactory {
    public static  T createInstance(String className, Class type) {
        try {
            Class clazz = Class.forName(className);
            // 驗證類型兼容
            if (!type.isAssignableFrom(clazz)) {
                throw new IllegalArgumentException("類型不匹配");
            }
            // 嘗試無參構造
            return type.cast(clazz.getDeclaredConstructor().newInstance());
        } catch (Exception e) {
            throw new RuntimeException("反射創建失敗: " + className, e);
        }
    }
}

// 使用
UserService service = ReflectionFactory.createInstance("com.example.UserServiceImpl", UserService.class);

性能與安全考量

維度影響優化建議
性能反射調用比直接調用慢 10-100倍 (JIT 優化後縮小)緩存 Class/Method/Field/Constructor 對象;高頻熱點用 MethodHandle/VarHandle
安全破壞封裝,可訪問 privateSecurityManager 限制 setAccessible;模塊化 (JPMS) 導出控制
異常檢查異常多,代碼冗長封裝工具類統一拋運行時異常

現代替代方案

  • MethodHandle / VarHandle (JDK 7+):更接近底層、性能更優、支持調用點緩存。
  • jclasslib / ASM:字節碼級操作,極致性能。
  • GraalVM Native Image:反射需額外配置 reflect-config.json,否則運行時報錯。

總結

反射賦予 Java 「運行時自我認知與操作」的能力。掌握 Class 獲取三式、Constructor/Method/Field 三大核心操作對象、暴力反射突破私有封裝、數組統一操作 API,並理解性能代價與安全邊界,是進階 Java 開發者與框架設計者的必修課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved