第一章 反射(2023版本IDEA)


  在本章中,将学习Java中反射的用法,如通过反射查看类的信息,创建实例、调用方法等。通过本章学习,我们能够了解反射的概念和作用,能够在开发中简单应用反射,能够借助API独立解决问题。

1.1 Java反向概述

  反射(Reflection)机制是Java语言特性之一,是Java被视为动态(或准动态)语言的一个关键特性。

1.1.1 什么是反射

  在计算机领域,反射指一种能力,能够自描述和自控制,即在运行状态中,动态获取类信息及动态调用实例方法的能力。

Java反射有以下3个动态特性。

  • 运行时创建实例。
  • 运行期间调用方法。
  • 运行时更改属性。

  Java反射(Reflection)提供了在运行时检查和修改代码的能力,这些能力通常被称为动态特性。以下是Java反射的三个主要动态特性及其示例代码:

  1. 动态地获取类的信息: 使用反射可以动态地获取类的名称、包、修饰符、父类、实现的接口、字段、方法等信息。
    示例代码:
Class<?> clazz = String.class;  
System.out.println("Class Name: " + clazz.getName());  
System.out.println("Package Name: " + clazz.getPackage().getName());  
System.out.println("Modifiers: " + Modifier.toString(clazz.getModifiers()));  
System.out.println("Superclass: " + clazz.getSuperclass().getName());  
for (Class<?> iface : clazz.getInterfaces()) {
     
    System.out.println("Interface: " + iface.getName());  
}
  1. 动态地调用对象的方法: 使用反射可以动态地调用对象的方法,即使这些方法在编译时是不可知的。
    示例代码:
try {
     
    Class<?> clazz = String.class;  
    Method method = clazz.getMethod("substring", int.class, int.class);  
    String str = "Hello, World!";  
    Object result = method.invoke(str, 0, 5); // 调用 str.substring(0, 5)  
    System.out.println("Substring: " + result); // 输出 "Hello"  
} catch (Exception e) {
     
    e.printStackTrace();  
}
  1. 动态地创建和操作对象: 使用反射可以动态地创建类的实例,并设置和获取字段的值。
    示例代码:
try {
     
    Class<?> clazz = MyClass.class; // 假设 MyClass 有一个无参构造函数和一个名为 "field" 的字段  
    Object obj = clazz.getDeclaredConstructor().newInstance(); // 创建 MyClass 的实例  
    Field field = clazz.getDeclaredField("field"); // 获取 "field" 字段  
    field.setAccessible(true); // 如果字段是私有的,需要设置为可访问  
    field.set(obj, "New Value"); // 设置字段的值  
    System.out.println(field.get(obj)); // 获取字段的值并输出  
} catch (Exception e) {
     
    e.printStackTrace();  
}

注意
  MyClass 是上述示例中的一个假设类,你需要用实际的类名替换它。同时,请注意使用反射时的安全性问题,例如当处理私有字段或方法时。另外,反射操作通常比直接方法调用慢,因此应该避免在性能关键的代码中使用反射

通过Java反射可以实现以下功能。

  • 在运行时探知任意一个实例所属的类。
  • 在运行时构造任意一个类的实例。
  • 在运行时探知任意一个类所真有的方法和属性。
  • 在运行时调用任意一个实例的方法。

通过Java反射,我们可以实现多种功能,包括但不限于:

1.动态加载类

try {
     
    // 加载类  
    Class<?> clazz = Class.forName("com.example.MyClass");  
    // 实例化对象(假设有一个无参构造函数)  
    Object obj = clazz.getDeclaredConstructor().newInstance();  
    // ...  
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
     
    e.printStackTrace();  
}

2.动态调用方法

try {
     
    Class<?> clazz = String.class;  
    // 获取指定名称和参数类型的方法  
    Method method = clazz.getMethod("charAt", int.class);  
    String str = "Hello";  
    // 调用方法  
    Object result = method.invoke(str, 0);  
    System.out.println("First char: " + result); // 输出 "H"  
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
     
    e.printStackTrace();  
}

3.动态访问和修改字段

try {
     
    Class<?> clazz = MyClass.class; // 假设 MyClass 有一个公共字段 "publicField"  
    Object obj = clazz.getDeclaredConstructor().newInstance();  
      
    // 获取字段  
    Field field = clazz.getDeclaredField("publicField");  
      
    // 设置字段值(如果需要访问私有字段,使用 field.setAccessible(true))  
    field.set(obj, "NewValue");  
      
    // 获取字段值  
    Object value = field.get(obj);  
    System.out.println("Field value: " + value); // 输出 "NewValue"  
} catch (NoSuchFieldException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
     
    e.printStackTrace();  
}

4.获取类的所有方法、字段、构造函数

Class<?> clazz = MyClass.class;  
  
// 获取所有公共方法  
Method[] methods = clazz.getMethods();  
for (Method method : methods) {
     
    System.out.println(method.getName());  
}  
  
// 获取所有字段(包括私有)  
Field[] fields = clazz.getDeclaredFields();  
for (Field field : fields) {
     
    System.out.println(field.getName());  
}  
  
// 获取所有构造函数  
Constructor<?>[] constructors = clazz.getDeclaredConstructors();  
for (Constructor<?> constructor : constructors) {
     
    System.out.println(constructor.toString());  
}

5.动态修改数组大小(尽管这不是反射的直接用途,但可以通过反射操作Array类来实现)

Object array = Array.newInstance(String.class, 5); // 创建一个大小为5的String数组  
Array.set(array, 0, "First"); // 设置第一个元素为"First"  
  
// 注意:Java数组的大小在创建后是固定的,不能通过反射改变。  
// 但你可以通过创建一个新的数组并复制元素来“模拟”改变大小。  
  
// 假设我们要“扩大”数组到10个元素  
Object newArray = Array.newInstance(String.class, 10);  
System.arraycopy(array, 0, newArray, 0, Array.getLength(array));  
  
// 现在newArray是一个新的大小为10的数组,并且包含了原来数组的所有元素

6.动态代理(虽然不完全是反射的直接应用,但它是基于反射实现的)
使用Proxy类和InvocationHandler接口可以动态地创建代理对象,这些对象在方法调用时可以执行额外的逻辑。

请注意,使用反射时需要谨慎处理异常和安全性问题,并且由于性能原因,应避免在频繁执行的代码路径上使用反射。

1.1.2 Java反射常用的API

使用Java反射技术,常用的类如下。

  • java.lang.Class<T>类:反射的核心类,反射所有的操作都围绕该类来生成的。通过Class类可以获得类的属性、方法等内容信息。
  • java.lang.reflect.Constructor<T>类:表示类的构造方法。
  • java.lang.reflect.Field类:表示类的属性,可以获取和设置中属性的值。
  • java.lang.reflect.Method类:表示类的方法,可以用来获取类中方法的信息或执行方法。

  在Java中,反射是一种强大的工具,允许我们在运行时检查类、接口、字段和方法的信息,并且可以动态地创建和调用对象。以下是一些Java反射常用的API代码示例:
1. 获取Class对象

Class<?> clazz = String.class; // 使用.class语法  
Class<?> clazz2 = Class.forName(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值