本文主要是介绍【反射知识点详解】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Java中的反射(Reflection)是一个非常强大的机制,它允许程序在运行时检查或修改类的行为。这种能力主要通过java.lang.reflect
包中的类和接口来实现。
通过反射,Java程序可以动态地创建对象、调用方法、访问字段,以及获取类的各种信息(如构造器、方法、字段等)。
反射的用途
反射主要用于以下几种情况:
- 动态创建对象:通过类的
Class
对象动态地创建其实例。 - 访问类的字段和方法:即使字段和方法是私有的,也可以通过反射机制来访问和修改它们。
- 动态调用方法:可以在运行时决定调用哪个方法,而不需要在编写代码时就知道。
- 框架开发:很多Java框架(如Spring)都大量使用反射来简化配置和初始化过程。
反射学什么?
- 学习获取类的信息、操作它们:这是反射学习的核心目标。你需要学会如何获取一个类的信息(如类名、方法、字段等),并能够在运行时操作这些信息。
反射第一步:加载类,获取类的字节码:Class对象
- 加载类:Java的类加载器(ClassLoader)负责将类的.class文件加载到JVM中。
- 获取Class对象:一旦类被加载,就可以通过多种方式来获取其
Class
对象。例如,使用Class.forName(String className)
、obj.getClass()
或Type.class
(其中Type是某个类名)。
以下是获取Class
对象的三种主要方式:
1. 使用.class
语法
对于已知的类,你可以直接使用.class
语法来获取其Class
对象。这种方式在编译时就已经确定了要操作的类,因此不需要处理ClassNotFoundException
。
2. 使用Class.forName(String className)
方法
这是最常用的动态加载类的方式。Class.forName(String className)
方法会加载指定的类,并返回该类的Class
对象。如果指定的类不存在于类路径中,或者因为其他原因无法被加载,那么会抛出ClassNotFoundException
异常。
3. 使用对象的getClass()
方法
如果你已经有了类的实例,那么可以通过调用该实例的getClass()
方法来获取其Class
对象。这个方法返回的是该对象的实际运行时类的Class
对象,这允许你在运行时发现对象的实际类型(包括继承体系中的具体类型)。
代码演示3种方式
测试类
package demo14;public class Test {public static void main(String[] args) throws Exception {//通过.class获取Class c1=Student.class;//getName()方法返回类的全限定名,即demo14.StudentSystem.out.println(c1.getName());//getSimpleName()方法返回类的简单名称,即StudentSystem.out.println(c1.getSimpleName());//通过Class.forName()获取Class c2=Class.forName("demo14.Student");System.out.println(c1==c2);//创建了一个Student类的实例并通过该实例的getClass()方法获取Class对象Student student=new Student();Class c3=student.getClass();System.out.println(c3==c2);//这比较获取的Class对象是否相同。由于Java虚拟机为每个类管理一个唯一的Class对象// 所以这3个对象是相同的,输出结果为true}
}
Student类
package demo14;public class Student {
}
获取类构造器:Constructor对象
Java中Class提供了从类中获取构造器的方法
方法名称 | 描述 | 返回值类型 |
---|---|---|
getConstructor(Class<?>... parameterTypes) | 获取具有指定参数类型的公共构造器 | Constructor<T> |
getDeclaredConstructor(Class<?>... parameterTypes) | 获取具有指定参数类型的构造器(包括私有构造器) | Constructor<T> |
getConstructors() | 获取类的所有公共构造器 | Constructor<?>[] |
getDeclaredConstructors() | 获取类的所有构造器(包括私有构造器) | Constructor<?>[] |
部分方法演示
获取类的成员变量:Field对象
方法声明 | 说明 |
---|---|
void set(Object obj, Object value) | 为对象obj 的某个属性赋值为value |
Object get(Object obj) | 从对象obj 中获取某个属性的值,并返回该值 |
public void setAccessible(boolean flag) | 设置为true 时,表示允许通过反射访问私有成员,绕过Java的访问控制检查 |
代码
假设我们有一个Cat
类,它有两个私有成员变量name
和age
,以及一个无参构造器和一个有参构造器。我们的目标是使用Java反射来获取这些成员变量的信息,并打印它们的名称和类型。
Cat类定义
package com.itheima.d2_reflect; public class Cat { private String name; private int age; public Cat() { System.out.println("无参数构造器执行"); } public Cat(String name, int age) { this.name = name; this.age = age; System.out.println("有参数构造器执行"); } }
反射获取成员变量
接下来是使用反射API获取Cat
类成员变量的代码示例:
package com.itheima.d2_reflect; import java.lang.reflect.Field; public class Test3Field { public static void testGetFields() throws NoSuchFieldException, IllegalAccessException { // 获取Cat类的Class对象 Class<?> c = Cat.class; // 获取类的全部成员变量 Field[] fields = c.getDeclaredFields(); // 遍历成员变量数组 for (Field field : fields) { // 打印成员变量的名称和类型 System.out.println(field.getName() + " ---> " + field.getType()); } // 定位某个特定的成员变量(例如name) Field fName = c.getDeclaredField("name"); // 打印该成员变量的名称和类型 System.out.println(fName.getName() + "--->" + fName.getType()); // 注意:直接访问私有成员变量会抛出IllegalAccessException,因此需要设置可访问性 fName.setAccessible(true); // 创建一个Cat对象 Cat cat = new Cat(); // 通过反射给私有成员变量赋值 fName.set(cat, "卡菲猫"); // 注意:由于我们没有直接获取或修改age字段,这里不展示其操作 // 打印修改后的cat对象(这里假设有合适的toString方法) System.out.println(cat); // 需要Cat类有适当的toString()实现 } // 主方法,用于测试 public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { testGetFields(); }
}
获取类的成员方法:Method对象
- Method对象:表示类的方法。通过
Class
对象的getMethod(String name, Class<?>... parameterTypes)
或getDeclaredMethod(String name, Class<?>... parameterTypes)
方法,可以获取到方法的Method
对象。 - 使用:获取到
Method
对象后,可以调用其invoke(Object obj, Object... args)
方法来执行对象的方法。同样,如果方法是私有的,可能需要先调用Method.setAccessible(true)
。
类别 | 方法名 | 签名 | 说明 |
---|---|---|---|
Class提供的成员方法 | getMethods() | Method[] getMethods() | 获取类的全部public成员方法(按名称排序) |
Class提供的成员方法 | getDeclaredMethods() | Method[] getDeclaredMethods() | 获取类的全部成员方法(不考虑访问修饰符) |
Class提供的成员方法 | getMethod(String name, Class<?>... parameterTypes) | Method getMethod(String name, Class<?>... parameterTypes) | 获取类的某个public成员方法,需指定方法名和参数类型 |
Class提供的成员方法 | getDeclaredMethod(String name, Class<?>... parameterTypes) | Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 获取类的某个成员方法,需指定方法名和参数类型,不考虑访问修饰符 |
Method提供的成员方法 | setAccessible(boolean flag) | void setAccessible(boolean flag) | 设置方法的访问权限。如果flag为true,则忽略Java的访问控制检查(暴力反射) |
Method提供的成员方法 | invoke(Object obj, Object... args) | Object invoke(Object obj, Object... args) throws IllegalAccessException, InvocationTargetException | 触发某个对象的该方法执行,需传入对象实例和参数 |
代码示例
以下是一个完整的Java代码示例,展示了如何使用上述方法:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; public class ReflectionDemo { public static void main(String[] args) { try { // 假设有一个类Person Class<?> personClass = Class.forName("Person"); // 获取所有public方法 Method[] publicMethods = personClass.getMethods(); System.out.println("Public Methods:"); for (Method method : publicMethods) { System.out.println(method.getName()); } // 获取所有方法(包括private) Method[] declaredMethods = personClass.getDeclaredMethods(); System.out.println("\nDeclared Methods:"); for (Method method : declaredMethods) { System.out.println(method.getName()); } // 获取特定方法(假设有一个public方法sayHello) Method sayHelloMethod = personClass.getMethod("sayHello"); // 假设Person类有一个无参构造器 Object personInstance = personClass.newInstance(); // 调用sayHello方法 sayHelloMethod.invoke(personInstance); // 获取特定方法(假设有一个private方法secretMethod) Method secretMethod = personClass.getDeclaredMethod("secretMethod"); // 设置访问权限为true(暴力反射) secretMethod.setAccessible(true); // 调用secretMethod方法 secretMethod.invoke(personInstance); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } // 假设的Person类 static class Person { public void sayHello() { System.out.println("Hello!"); } private void secretMethod() { System.out.println("This is a secret method."); } }
}
这篇关于【反射知识点详解】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!