反射--哪有岁月静好

文章详细阐述了Java反射的强大功能,包括获取类信息、动态操作对象、与JVM和.class文件的关系。作者提到反射在Spring框架中的应用,并强调了反射的使用注意事项,尤其是在封装性和性能上的权衡。
摘要由CSDN通过智能技术生成

反射

官方解释:

反射(Reflection)是一种强大的机制,它允许程序在运行时检查和操作类、接口、字段和方法。通过反射,Java代码可以获取任意类的内部信息,如成员变量、构造方法、方法和注解等,并可以动态地创建对象、调用方法以及修改属性值。
反射的核心在于获取类的Class对象,这是Java反射的起点。一旦有了Class对象,就可以使用反射API来获取该类的所有方法、构造方法、字段等信息,也可以调用这些方法和构造方法来创建对象或执行特定的操作。
需要注意的是,反射虽然强大,但也应该谨慎使用。因为反射会破坏封装性,并可能导致性能下降。在不需要动态改变程序行为的情况下,应该优先使用普通的方法调用和对象操作。
总结来说,Java中的反射是一种在运行时检查和操作程序结构的技术,它提供了一种动态、灵活的方式来操作Java类、接口和它们的成员。

自己的理解;

反射这个机制,让我们操作一些逻辑变得简单,让我们的代码变得简洁,在我们后面学到的Spring框架中,控制反转这个概念,底层采用了大量的反射机制,因为反射这个机制可以让我们
允许程序在运行时检查类、接口、字段和方法的信息,并可以动态地创建对象、调用方法和修改属性。依赖注入框架通过反射获取类的元数据信息,然后根据配置或注解等信息,动态地创建对象并注入依赖。

其实在很多小伙伴看到这个反射这个机制的时候还是一脸懵逼,因为本来创建对象一句话,new一个对象就好了,现在我们却要编写繁琐的代码,还牵扯到.class文件,这个加载先后,然后就搞糊涂了,我下面也将介绍一下这些相关的东西

反射机制 、 JVM(java虚拟机) 和.class文件 他们之间的联系

  • .class文件是Java程序的重要组成部分。它包含了Java类的信息,这些信息是JVM执行程序时所需要的。当Java编译器将源代码(.java文件)编译成字节码时,就会生成.class文件。这些文件是平台无关的,使得Java程序可以在任何支持JVM的平台上运行。

  • 接下来,JVM是运行Java程序的虚拟计算机。它负责加载.class文件,解释和执行其中的字节码。当Java程序运行时,JVM会读取.class文件,并将其加载到内存中,然后按照字节码的指令执行程序。

  • 而反射是Java的一种特性,它允许程序在运行时检查类、接口、字段和方法的信息,并可以动态地创建对象、调用方法和修改属性。通过反射,我们可以获取到类的Class对象,进而可以获取到类的所有方法、构造方法、字段等元数据信息,甚至可以动态地创建对象并调用其方法。

这三者之间的联系主要体现在以下几个方面:

  • .class文件与JVM:JVM通过加载.class文件来执行Java程序。每个.class文件对应一个Java类,JVM通过读取这些文件来获取类的元数据信息,并据此执行相应的字节码指令。
    反射与JVM:反射机制的实现依赖于JVM的支持。当我们在程序中使用反射API来获取类的信息或动态地创建对象时,实际上是JVM在运行时动态地加载和解析.class文件,并提供相应的反射服务。
  • .class文件与反射:.class文件存储了类的元数据信息,而反射机制正是通过读取这些元数据信息来实现其功能的。当我们使用反射API来获取类的信息时,实际上是读取了.class文件中的元数据。

Class类对象的获取

在jvm加载的时候,会创建一个class对象

获取class类对象的三种方式

  • 根据类名:类名.class
  • 根据对象:对象.getClass()
  • 根据全限定类名:Class.forName(全限定类名)
		// 根据类名获取class类对象
        Class<User> cls = User.class;

        // 根据对象获取class类对象
        User user = new User();
        Class<? extends User> userCls = user.getClass();
        
        //根据全限定类名获取class对象
        Class<?> userCls2 = Class.forName("reflect.User");

常用API方法

		Class<?> cls = Class.forName("reflect.Person");
        /**
         * String getName() :获取class类对象的全限定类名(包括包名)
         */
        System.out.println(cls.getName());

        /**
         * String getSimpleName() :获取class类对象的类名(不包括包名)
         */
        System.out.println(cls.getSimpleName());

        /**
         * boolean isInterface() :判断class类对象是不是表示一个接口
         */
        System.out.println(cls.isInterface());

        /**
         * Class<?>[] getInterfaces() :返回class对象数组,表示Class对象所引用的类所实现的所有接口
         */
        Class<?>[] interfaces = cls.getInterfaces();
        System.out.println(Arrays.toString(interfaces));

        /**
         * Object newInstance():返回一个Object对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。
         */
        Object obj = cls.newInstance();

        /**
         * Field[] getFields():获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。
         */
        Field[] fields = cls.getFields();
        System.out.println(Arrays.toString(fields));

        /**
         * 获得某个类的自己声明的字段,即包括public、private和protected,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。
         */
        Field[] declaredFields = cls.getDeclaredFields();

        /**
         * Constructor<?>[] getConstructors(): 获取有参构造器,返回公开的所有构造器
         */
        Constructor<?>[] constructors = cls.getConstructors();

        /**
         * 获取指定的构造器,可以通过数据类型
         */
        Constructor<?> constructor = cls.getConstructor(String.class, int.class,char.class);

        /**
         * 获取所有的公开的方法,包括构造方法
         */
        Method[] methods = cls.getMethods();

        /**
         * 获取指定的方法,可以获得有参数的方法
         * 后面是演示了方法的执行
         */
        Method method = cls.getMethod("say", String.class);
        method.invoke(cls.newInstance(), "小五");

        /**
         * 获取class对象的私有方法,并且执行
         */
        Method methodPrivate = cls.getDeclaredMethod("methodPrivate");
        // 改为允许访问私有方法
        methodPrivate.setAccessible(true);
        methodPrivate.invoke(cls.newInstance());
        // 养成好习惯,用了之后我们还要给他改回去
        methodPrivate.setAccessible(false);
		
		/**
         * 扫描注解
         */
        boolean res = cls.isAnnotationPresent(AutoRunClass.class);
        System.out.println(res?"被标记了":"没有被标记");

下面给几个小Demo

Person类的结构

	private String name = "李四";
    private int age = 18;
    private char sex = '男';

    public Person() {
    }

    public Person(String name, int age, char sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }


    private void methodPrivate(){
        System.out.println("私有方法");
    }

    @AutoRunMethod()
    public void sayHello() {
        System.out.println("你好" + name);
    }

    public void sayHi() {
        System.out.println(name + "HI!");
    }

    public void say(String info) {
        System.out.println(name + ":" + info);
    }

    public void say(String info, int count) {
        for (int i = 0; i < count; i++) {
            System.out.println(name + ":" + info);
        }
    }

    public int sum(int a, int b){
        return a+b;
    }

关于方法method对象的常用

public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入类名:");
        String className = sc.nextLine();
        Class<?> cls = Class.forName(className);
        Method[] methods = cls.getDeclaredMethods();

        for (Method method : methods) {
            System.out.println("方法名:" + method.getName());
            System.out.println("参数个数:" + method.getParameterCount());
            System.out.println("参数类型:" + Arrays.toString(method.getParameterTypes()));
            System.out.println(Arrays.toString(method.getTypeParameters()));
            int result = method.getModifiers();// 获取方法的访问修饰符
            switch (result){
                case Modifier.PUBLIC:
                    System.out.println("public");
                    break;
                case Modifier.PRIVATE:
                    System.out.println("private");
                    break;
                case Modifier.PROTECTED:
                    System.out.println("protected");
                    break;
            }
            System.out.println("-------------------------");
        }
    }

反射关于注解常用

public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入类名:");
        String className = sc.nextLine();

        Class<?> cls = Class.forName(className);

        boolean clsRes = cls.isAnnotationPresent(AutoRunClass.class);

        if (clsRes) {
            Object obj = cls.newInstance();
            // 获取符合条件类中的方法
            Method[] methods = cls.getDeclaredMethods();
            for (Method method : methods) {
                // 判断方法是否符合条件
                boolean methodRes = method.isAnnotationPresent(AutoRunMethod.class);

                if (methodRes) {
                    method.invoke(obj);
                } else {
                    System.out.println("该方法没有被注解标记:" + method.getName());
                }
            }

        } else {
            System.out.println("该类没有被标记");
        }
    }
  • 14
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值