2023.12.12 关于 Java 反射详解

发布时间:2023年12月17日

目录

基本概念

定义

用途

反射相关的类

反射基本原理

Class 类中的相关方法

常用获得类相关的方法

常用获得类中属性相关的方法

常用获得类中构造器相关的方法

常用获得类中方法相关的方法

实例理解

反射优缺点


基本概念

定义

  • Java 的反射(reflection)机制是一种强大功能,它可以让我们在运行时动态地获取和操作 类 或 对象 的信息

实例理解

  • 我们可以通过反射机制来创建一个类的对象,而不需要使用 new 关键字
  • 我们也可以通过反射机制来访问或修改一个对象的私有属性或方法,而不需要遵循封装原则
  • 我们还可以通过反射机制来调用一个对象的任意方法,而不需要知道它的参数类型或返回值类型

用途

典型用途一:

  • 在开发第三方应用时,我们可能会遇到一些类的成员变量、方法或属性是私有的,或者只对系统应用开放,这就意味着我们不能直接访问这些成员或方法
  • 这时我们便可以在运行时 通过 Java 的反射机制 来动态地访问和操作类的内部成员,包括私有成员和方法

典型用途二:

  • 反射在开发通用框架 Spring 中起着重要的作用
  • 在Spring 框架中,所有类(Bean)都由 Spring 容器进行管理,这些 Bean 可以通过 XML 配置或注解来配置
  • 当我们从容器中获取Bean 以进行依赖注入时,容器会读取配置信息,这些配置信息包含了类的信息,比如类的名称、属性、方法等
  • Spring根据这些信息 动态地创建这些类的实例,这个过程就是所谓的 依赖注入,该过程中,反射起到了关键作用
  • Spring 使用反射来动态地创建类的实例,调用方法,以及设置属性值

反射相关的类

反射基本原理

  • Java 的反射机制是基于 java.lang.Class 类实现的
  • 当我们编译一个 Java 文件时,会生成一个 .class 文件
  • 当 JVM 加载这个 .class 文件时,会将其解析为一个 java.lang.Class 类的对象
  • 在程序运行时,每个 Java 文件都会被 JVM 解析为一个 Class 类的实例
  • 这个 Class 类的实例包含了该 Java 文件中所定义类的所有信息,包括类的名称、属性、方法等
  • 我们可以通过 Java 的反射机制来操作这个 Class 类的实例
  • 具体来说,我们可以使用反射来获取类的属性和方法,甚至可以添加或修改类的属性和方法
  • 这使得我们可以在运行时动态地操作类,使其成为一个 动态的类
类名用途
Class 类代表类的实体,在运行的 Java 应用程序中表示类和接口
Field 类代表类的成员变量、类的属性
Method 类代表类的方法
Constructor 类代表类的构造方法

Class 类中的相关方法

常用获得类相关的方法

方法用途
getClassLoader()获得类的加载器
getDeclaredClasses()返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有的)
forName(String className)根据类名返回类的对象
newInstance()创建类的实例
getName()获得类的完整路径名字

常用获得类中属性相关的方法

方法用途
getField(String name)获得某个公有的属性对象
getFields()获得所有公有的属性对象
getDeclaredField(String name)获得某个属性对象
getDeclaredFields()获得所有属性对象

常用获得类中构造器相关的方法

方法用途
getConstructor(Class <?> parameterTypes)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有构造方法
getDeclaredConstructor(Class <?> parameterTypes)获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()获得该类所有构造方法

常用获得类中方法相关的方法

方法用途
getMethod(String name, Class <?> parameterTypes) 获得该类某个公有的方法
getMethods()获得该类所有公有的方法
getDeclaredMethod(String name, Class <?> parameterTypes) 获得该类某个方法
getDeclaredMethods()获得该类所有方法

实例理解

  • 此处我们先创建一个 Student 类
class Student{
    //私有属性name
    private String name = "master";
    //公有属性age
    public int age = 18;
    //不带参数的构造方法
    public Student(){
        System.out.println("Student()");
    }
    private Student(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }
    private void eat(){
        System.out.println("make hamburger!");
    }
    public void sleep(){
        System.out.println("go to bed!");
    }
    private void function(String str) {
        System.out.println(str);
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 此处介绍三种方式来获取 Student 的 Class 对象

public class Demo1 {
    public static void main(String[] args) {
//        有三种方式可以获取 Class 对象
        Student student1 = new Student();
//        1、通过对象的 getClass() 方法
        Class<?> c1 = student1.getClass();
//        2、通过类名 .class 获取
        Class<?> c2 = Student.class;
//        3、通过调用 Class.forName() 方法获取了 Student 类的 Class 对象
//        Class.forName() 方法需要一个类的全限定名(包括 包名和类名) 作为参数
//        此处的 ? 是一个通配符,用于表示未知类型
//        当我们声明一个泛型变量时,如果我们不确定或不关心实际的类型参数,我们可以使用 ? 来表示
        Class<?> c3 = null;
        try {
            c3 = Class.forName("Student");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
//        此处证明通过上述三种方式所获取的 Class 对象 都是同一个
        System.out.println((c1.equals(c2) && c1.equals(c3) && c2.equals(c3)) ? "true" : "false");
    }
}
  • 此处我们通过反射机制创建一个对象

import java.lang.reflect.InvocationTargetException;

public class ReflectClassDemo {

//  通过反射创建一个对象
    public static void reflectNewInstance() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Class<?> c3 = Class.forName("Student");
//        通过调用 Class 类的 newInstance() 方法来创建一个 c3 对应类的新实例
//        newInstance() 方法调用的是这个类的无参构造函数
//        如果这个类没有无参构造函数,或者无参构造函数是私有的,那么 newInstance 会抛出一个异常
//        因为 newInstance() 方法返回的类型为 Object 类 所以需要类型转换,此处转换为 Student 类
        Student student = (Student) c3.newInstance();
        System.out.println(student);
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
        reflectNewInstance();
    }
}

运行结果:

  • 此处我们通过反射机制获取私有的构造方法
import java.lang.reflect.Constructor;

public class ReflectClassDemo {

//  通过反射获取私有的构造方法
    public static void  reflectPrivateConstructor() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<?> c3 = Class.forName("Student");
        Constructor<?> constructor = c3.getDeclaredConstructor(String.class,int.class);
//        注意只要是涉及到 private 都要使用 setAccessible(true) 来打开权限,此处的构造方法为私有的
        constructor.setAccessible(true);
//        此处利用构造方法 修改年龄和性别
        Student student = (Student)constructor.newInstance("xiaolin",20);
        System.out.println(student);
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
        reflectPrivateConstructor();
    }
}

运行结果:

  • 此处我们通过反射机制获取私有属性
import java.lang.reflect.Field;

public class ReflectClassDemo {

//  通过反射获取私有属性
    public static void reflectPrivateField() throws ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException {
        Class<?> c3 = Class.forName("Student");
        Field field = c3.getDeclaredField("name");
//        注意只要是涉及到 private 都要使用 setAccessible(true) 来打开权限,此处的 name 属性是私有的
        field.setAccessible(true);
        Student student = (Student) c3.newInstance();
//        此处修改私有属性
        field.set(student,"haoran");
        System.out.println(student);
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
        reflectPrivateField();
    }
}

运行结果:

  • 此处我们通过反射机制获取私有方法
import java.lang.reflect.Method;

public class ReflectClassDemo {

//  通过反射获取私有方法
    public static void reflectPrivateMethod() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class<?> c3 = Class.forName("Student");
        Method method1 = c3.getDeclaredMethod("function", String.class);
        Method method2 = c3.getDeclaredMethod("sleep");
//        注意只要是涉及到 private 都要使用 setAccessible(true) 来打开权限,此处的 function 方法是私有的
        method1.setAccessible(true);
        Student student = (Student) c3.newInstance();
//        此处给 function 方法传参
        method1.invoke(student,"此处利用反射机制给 function 方法传个字符串参数");
//        此处调用 sleep 方法,该方法为 public 无需额外打开权限,直接调用即可
        method2.invoke(student);
    }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
        reflectPrivateMethod();
    }
}

运行结果:

反射优缺点

优点

  • 对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个类都能调用它的任意一个方法
  • 增加程序的灵活性和扩展性,降低耦合性,提高自适应性
  • 反射已经运用在很多流行框架,典型代表为 Spring

缺点

  • 使用反射会有效率问题,会导致程序效率降低
  • 反射技术绕过了源代码的技术,因而会带来维护问题
  • 反射代码比相应的直接代码更复杂

总结:

  • 虽然反射非常强大,但也需要谨慎使用
  • 我们需要在反射带来的灵活性和可扩展性与其带来的性能开销、维护问题和代码复杂性之间找到一个平衡
  • 某些情况下,比如开发通用的框架,使用反射式非常有价值的
  • 但在其他情况下,我们可能更倾向于使用更简单、更直接的代码
文章来源:https://blog.csdn.net/weixin_63888301/article/details/134977562
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。