Java中反射详解

1、类对象的概念

所有的类,都存在一个类对象,这个类对象用于提供类本身的信息,比如有几种构造方法,有多少属性,有哪些普通方法。

2、获取类对象

获取类对象有3种方式(比如有个对象叫Hero)
(1)Class.forName
(2)Hero.class
(3)new Hero().getClass()

package reflection;
 
import charactor.Hero;
 
public class TestReflection {
 
    public static void main(String[] args) {
            String className = "charactor.Hero";
            try {
                Class pClass1=Class.forName(className);
                Class pClass2=Hero.class;
                Class pClass3=new Hero().getClass();
                System.out.println(pClass1==pClass2);
                System.out.println(pClass1==pClass3);
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
}

以上代码会打印两个true, 因为一个类只有一个类对象, 所以上面三种方法获取的都是同一个类对象

3、获取类对象时会导致属性初始化

例如Hero对象添加如下属性:

public class Hero {
    public String name;
    public float hp;
    public int damage;
    public int id;
 
    static String copyright;
 
    static {
        System.out.println("初始化 copyright");
        copyright = "版权由Riot Games公司所有";
    }
}

public class TestReflection {
    public static void main(String[] args) {
            String className = "charactor.Hero";
            try {
                Class pClass1=Class.forName(className);
                Class pClass2=Hero.class;
                Class pClass3=new Hero().getClass();
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
}

查看打印结果,会发现:无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)

4、通过反射创建对象

public class TestReflection {
    public static void main(String[] args) {
        //传统的使用new的方式创建对象
        Hero h1 =new Hero();
        h1.name = "teemo";
        System.out.println(h1);
        try {
            //使用反射的方式创建对象
            String className = "charactor.Hero";
            //类对象
            Class pClass=Class.forName(className);
            //构造器
            Constructor c= pClass.getConstructor();
            //通过构造器实例化
            Hero h2= (Hero) c.newInstance();
            h2.name="gareen";
            System.out.println(h2);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

5、通过反射访问属性

public class Hero {
    public String name;
    public float hp;
    public int damage;
    public int id;
     
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Hero(){
         
    }
    public Hero(String string) {
        name =string;
    }
 
    @Override
    public String toString() {
        return "Hero [name=" + name + "]";
    }
    public boolean isDead() {
        // TODO Auto-generated method stub
        return false;
    }
    public void attackHero(Hero h2) {
        System.out.println(this.name+ " 正在攻击 " + h2.getName());
    }
 
}
public class TestReflection {
  
    public static void main(String[] args) {
            Hero h =new Hero();
            //使用传统方式修改name的值为garen
            h.name = "garen";
            try {
                //获取类Hero的名字叫做name的字段
                Field f1= h.getClass().getDeclaredField("name");
                //修改这个字段的值
                f1.set(h, "teemo");
                //打印被修改后的值
                System.out.println(h.name);
                 
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
}

getField和getDeclaredField的区别
这两个方法都是用于获取属性字段,getField 只能获取public的,包括从父类继承来的字段。getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上field.setAccessible(true))。

6、通过反射调用方法

public class Hero {
    public String name;
    public float hp;
    public int damage;
    public int id;
     
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Hero(){
         
    }
    public Hero(String string) {
        name =string;
    }
 
    @Override
    public String toString() {
        return "Hero [name=" + name + "]";
    }
    public boolean isDead() {
        // TODO Auto-generated method stub
        return false;
    }
    public void attackHero(Hero h2) {
        // TODO Auto-generated method stub
         
    }
 
}
public class TestReflection {
 
    public static void main(String[] args) {
        Hero h = new Hero();
 
        try {
            // 获取这个名字叫做setName,参数类型是String的方法
            Method m = h.getClass().getMethod("setName", String.class);
            // Method m = h.getClass().getDeclaredMethod("setName", String.class);
            // 对h对象,调用这个方法
            m.invoke(h, "盖伦");
            // 使用传统的方式,调用getName方法
            System.out.println(h.getName());
 
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }
}

getMethod()和getDeclaredMethod的区别
这两个方法都是用于获取对象的方法,getMethod()只能获取public的,包括从父类继承来的方法。getDeclaredMethod() 可以获取本类所有的方法,包括private的,但是不能获取继承来的方法,如果要获取private方法,必须加上field.setAccessible(true)。

<think>好的,我现在需要详细解析Java反射机制,并提供使用示例。首先,我需要回顾反射的基本概念和原理。根据引用[1]和[2],反射允许程序在运行时检查和操作类的结构和行为,动态获取类的信息,访问属性和方法。这涉及到Class类、Method、Field、Constructor等核心API,如引用[4]提到的。 接下来,要解释反射的用途,比如框架开发、动态代理,特别是Spring中通过XML配置装载Bean的例子(引用[3])。然后需要详细说明如何使用反射,包括获取Class实例、创建对象、访问字段、调用方法等步骤,每个部分都需要代码示例。 还需要注意性能影响,反射可能比直接调用慢,但现代JVM优化了这点,不过频繁使用仍需谨慎。应用场景部分要强调动态加载和灵活性,如插件系统或配置驱动的架构。 用户可能的问题包括反射的优缺点、如何提高反射性能、在Spring中的具体应用,以及安全性问题。要确保回答覆盖这些方面,并结构清晰,分步骤讲解。 最后,检查示例代码的正确性,确保使用中文,数学公式正确应用$...$格式,独立公式用$$...$$。引用标识要自然添加,如引用[1]、[2]等。确保回答符合系统级指令的所有要求,包括生成相关问题,并正确格式化代码块。</think>### Java 反射机制详解 #### 一、反射的基本概念 Java 反射机制允许程序在**运行时**动态获取类的信息(如方法、属性、构造器等),并操作对象或调用方法,无需在编译时确定具体类[^1][^2]。这种能力使得框架和工具能够实现高度灵活的设计,例如 Spring 框架通过 XML 配置动态加载 Bean[^3]。 #### 二、反射的核心 API 1. **`java.lang.Class`**:表示类的元数据,反射操作的入口。 2. **`java.lang.reflect.Method`**:描述类的方法。 3. **`java.lang.reflect.Field`**:描述类的成员变量。 4. **`java.lang.reflect.Constructor`**:描述类的构造方法[^4]。 #### 三、反射的使用步骤 ##### 1. 获取 `Class` 对象 三种方式: - **`类名.class`**:`Class<User> clazz = User.class;` - **`对象.getClass()`**:`User user = new User(); Class<?> clazz = user.getClass();` - **`Class.forName("全限定类名")`**:`Class<?> clazz = Class.forName("com.example.User");` ##### 2. 创建对象实例 通过构造器实例化: ```java Constructor<?> constructor = clazz.getDeclaredConstructor(); Object obj = constructor.newInstance(); // 调用无参构造 ``` ##### 3. 操作字段 ```java Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true); // 允许访问私有字段 nameField.set(obj, "Alice"); // 设置字段值 String name = (String) nameField.get(obj); // 获取字段值 ``` ##### 4. 调用方法 ```java Method method = clazz.getDeclaredMethod("setName", String.class); method.invoke(obj, "Bob"); // 调用 setName("Bob") ``` #### 四、示例代码 ```java public class User { private String name; public User() {} public void setName(String name) { this.name = name; } } public class ReflectionDemo { public static void main(String[] args) throws Exception { // 获取 Class 对象 Class<?> clazz = Class.forName("User"); // 创建实例 Object user = clazz.getDeclaredConstructor().newInstance(); // 设置私有字段 Field field = clazz.getDeclaredField("name"); field.setAccessible(true); field.set(user, "Charlie"); // 调用方法 Method method = clazz.getMethod("setName", String.class); method.invoke(user, "David"); } } ``` #### 五、性能与注意事项 - **性能影响**:反射操作比直接调用慢,因涉及动态解析和安全检查,可通过缓存 `Method` 或 `Field` 对象优化。 - **安全性**:反射可绕过访问限制(如私有方法),需谨慎使用。 - **典型场景**:动态代理、IDE 调试工具、Spring 框架的依赖注入。 #### 六、应用场景 1. **框架开发**:如 Spring 通过反射加载和管理 Bean。 2. **动态代理**:AOP 实现中动态生成代理类。 3. **序列化工具**:JSON 库通过反射解析对象字段。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值