【Java 基础】14 反射


反射是 Java 中一种强大的编程技术,允许在 运行时 检查和操作类、方法、字段等程序 元信息

通过反射,你可以在不知道类名的情况下,动态地加载类、创建对象,调用方法,访问字段等

接下来就开始介绍一下 Java 反射 的基本概念、用途和示例。

1.什么是反射

反射是 Java 在运行时检查和操作类、方法、字段等信息的能力。通过反射,你可以动态地获取和使用类的信息,而不需要在编译时就知道类的全部细节。

Java 反射的核心是 java.lang.reflect 包,它提供了许多类和接口来实现反射功能。

我们有一个 Student

class Student {
    private String name;
    
    public String get(){
        return "abc";
    }
}

根据 Student 创建一个它的实例,然后我们想根据实例来获取类的信息(比如 name 或者 get 方法等),这个反向拿的过程就叫做反射。
在这里插入图片描述

2.反射的主要用途

  • 动态加载类: 在运行时根据类的名称动态加载类。这使得你可以根据配置文件或用户输入加载不同的类。
  • 创建对象: 通过类的信息创建对象实例,而不是使用 new 关键字。使得可以在运行时创建任意类的对象。
  • 调用方法: 在运行时通过方法名动态调用类的方法。使得可以在不知道方法名称的情况下调用特定的方法。
  • 访问字段: 动态访问和修改类的字段,无需事先知道字段的名称。
  • 获取注解信息: 通过反射可以获取类、方法、字段等元素上的注解信息,对一些框架的实现是非常重要的。

3.反射潜在的问题

尽管 Java 反射提供了这些强大的功能,但它也带来了一些潜在的问题和性能开销:

  • 性能开销: 反射操作通常比直接的静态类型操作慢,因为它绕过了编译时的类型检查,可能导致运行时的性能损失。

  • 类型安全性: 反射绕过了编译时的类型检查,可能引入运行时的错误。因此,在使用反射时,必须格外小心,确保类型安全性。

  • 封装性破坏: 反射使得可以访问和修改私有成员,这可能破坏了类的封装性。过度使用反射可能导致代码难以理解和维护。

4.反射的基本用法

例如 现在有这么一个 Student

package com.example.demo.test;
class Student {
    // 私有的名字
    private String name = "张三";

    public String getName() {
        return name;
    }

    // 私有的方法 说 hello
    private void sayHello(String str){
        System.out.println("Hello " + str);
    }
}

下面开始展示反射的常用操作

  • 通过类名加载类
Class<?> clazz = Class.forName("com.example.demo.test.Student");
  • 创建对象
Object instance = clazz.getDeclaredConstructor().newInstance();
  • 获取方法并调用
Method method = clazz.getDeclaredMethod("sayHello", String.class);
// 如果方法是私有的,需要设置为可访问
method.setAccessible(true);
method.invoke(instance, "Reflection!");

输出结果:

Hello Reflection!

  • 获取字段并修改值
// 打印 旧的 name
Method method2 = clazz.getDeclaredMethod("getName");
String oldName = (String) method2.invoke(instance);
System.out.println("Old name is " + oldName);

// 获得私有的 name 字段
Field field = clazz.getDeclaredField("name");
// 如果字段是私有的,需要设置为可访问
field.setAccessible(true);
// 不仅可以访问私有属性 name,还可以修改 name 的值
field.set(instance, "李四");

// 打印 新的 name
String newName = (String) method2.invoke(instance);
System.out.println("New name is " + newName);

输出结果:

Old name is 张三
New name is 李四

完整的代码:

public static void main(String[] args) throws Exception{
        // ============================================================================
        // 通过类名加载类
        Class<?> clazz = Class.forName("com.example.demo.test.Student");

        // ============================================================================
        Object instance = clazz.getDeclaredConstructor().newInstance();

        // 创建对象
        // ============================================================================
        Method method = clazz.getDeclaredMethod("sayHello", String.class);
        // 如果方法是私有的,需要设置为可访问
        method.setAccessible(true);
        method.invoke(instance, "Reflection!");

        // ============================================================================
        // 打印 旧的 name
        Method method2 = clazz.getDeclaredMethod("getName");
        String oldName = (String) method2.invoke(instance);
        System.out.println("Old name is " + oldName);

        // 获得私有的 name 字段
        Field field = clazz.getDeclaredField("name");
        // 如果字段是私有的,需要设置为可访问
        field.setAccessible(true);
        // 不仅可以访问私有属性 name,还可以修改 name 的值
        field.set(instance, "李四");

        // 打印 新的 name
        String newName = (String) method2.invoke(instance);
        System.out.println("New name is " + newName);
        // ============================================================================
    }

上述代码演示了通过反射加载类创建对象调用方法以及访问字段的过程。

总结

反射让我们可以在 Java 运行时检查和操作类、方法、字段等信息,但是,需要注意的是反射通常会导致性能损失,并且由于绕过了编译时的类型检查,可能会引入运行时的错误。因此,在使用反射时,务必谨慎操作,确保类型安全性。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值