反射一、反射的基本概念

news/2024/5/19 6:39:40 标签: Java, 反射

1、引言

反射的重点在于这个“反”字,理解反射,重点要在于这个“反”的含义。既然是“反”,那么一定是与通常不一致的情况。那我们首先来看下正常情况下,Java程序从编写到运行的一个过程。

Java中,万物皆是类。开发人员在编写Java代码时,就是一个创建并编写大量Java类的过程。Java程序运行时,ClassLoader就会将所有用到的Java类加载到JVM虚拟机中。如下是一个简单的Java示例代码,代码中定义了一个Person类,以及一个包含main函数作为程序入口的Main类 。在main函数里面,代码引用了Person类。

class Person {
    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

class Main {
    public static void main(String[] args) {
        Person person = new Person("Joy");
        System.out.println(person.getName());
    }
}

这个小程序,从开发者层面来看,在开发这段小程序时,开发人员一定是知道main程序中依赖的Person类的详细信息,包括Person类的属性、方法等,所以开发人员可以在main函数中明确的使用Person这个类。

从代码的编译层面来看,在这个小程序的编译阶段,编译Main这个类时,编译器发现了对Person类的引用,那么编译器就一定会去找Person类的相关信息,然后将Person类进行编译,以便在Main类中可以正常使用。编译器如果没有找到Person类,那么就一定会编译失败。换言之,这个小程序编译成功了,那么就说明在编译阶段,编译器已经获取到了所有Person类的详细信息。

从代码的运行程序面来看,程序运行时,ClassLoader将需要用到的类都加载到了JVM中,所有JVM中也一定是清楚的知道相关类的所有信息的。

如下图所示,对于一个类来说,从代码编写阶段到程序运行阶段,主要有以下几个状态:

 首先一个类对应的是*.java文件中的一个class,java文件中定义了类的各种属性、方法等。经过编译之后,每个类对应一个class文件,class文件中也描述了类的所有相关信息。程序运行时,类加载器对class文件进行解析,加载对应的类,同时会在JVM中生成一个Class对象,每个Class对象和每个class文件是一一对应的,也就是每个类在JVM中都有且只有一个对应的Class对象存在。这个Class对象描述了其对应的java类的详细信息。当要实例化这个类的一个对象时,就是根据这个类对应的Class对象获取类的相关信息,并根据此信息生成该类的一个新的对象。

以上就是java程序中,在通常情况下运行时,类从代码阶段到执行阶段所有的几个简单状态。

2、什么是反射

说完通常情况了,就该说一下特殊情况了。假设有这么一个需求,要求编写一段代码,这个代码的作用是,对于给定的任意一个类,要求调用这个类的任意某个方法。

这个要怎么做呢?

和上面的不同点在于,在开发人员层面,编写上面示例代码时,开发者是明确知道Person类的,知道Person类的所有信息,也知道要调用Person类的某个具体的方法。但是现在这个需求,开发人员什么都不知道了,不知道到底要调用类的哪个方法了,甚至不知道到底要调用哪个类的方法,对于这个类的信息是一无所知,只知道在程序运行期间会给你一个类。

从编译层面来看,在编译阶段,是没有这个类的信息的,编译器也是对这个类一无所知的。这个类的信息是在运行阶段才会有的。

那么就没有办法完成了吗?办法当然是有的,就算是开发人员在开发时不知道这个类是啥,就算是编译器在编译阶段不知道这个类的信息,但是在程序的运行阶段,是可以想办法获取这个类的信息的。只要在程序的运行阶段,我们获取了这个类的所有信息,那么我们想干啥就干啥,想执行它的啥方法就可以执行它的啥方法了。这就是反射。换言之,反射就是在程序的运行期动态获取类的信息。

如下图所示,虽然在代码开发和编译阶段可能不知道某个类的详细信息,但是在运行阶段,当这个类被加载到JVM中了就会生成一个Class对象,我们通过这个Class对象,在运行期一样可以获得这个类对应的信息,这就是反射

 这里可能就有疑问了,JVM中的Class对象是由class文件加载后生成的,为什么一定要等到加载之后才能获取类的信息呢?加载之前,从class文件中不也能够获取到这个类的信息吗?

的确,如果在程序运行之前,就拥有这个类的class文件,比如是开发人员自己写的代码编译出的class文件,或者是引用别人的jar包里面的class文件,都是可以提前获取到类的信息的,也就不用反射了。但问题是,在程序运行之前,并不是都有对应的class文件的,可能这个class文件是程序在运行时才从网络中获取到的字节流呢?甚至这个class文件就是在程序运行期才动态生成的字节流呢?这些情况下提前是不可能获知到类的相关信息的,就只能通过反射在运行期动态获取类的信息了。

 下面就是一个反射的小例子,实现了上面所说的功能:

对于给定的任意一个类,要求调用这个类的任意某个方法。

import java.lang.reflect.Method;

class Person {
    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        System.out.println("==========get name executed!==========");
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

class Main {
    public static void main(String[] args) throws Exception {
        String className = args[0];
        String methodName = args[1];
        Class cls = Class.forName(className);
        Method m = cls.getMethod(methodName);
        Object o = cls.newInstance();
        m.invoke(o);
    }
}

程序的执行结果:程序运行时,指定执行Person类的getName方法。

3、反射的用途

通过前面的叙述,就算是开发人员在开发代码时可以不知道某些类的相关信息,编译器在编译阶段也可以不知道类的相关信息,但是通过在运行期动态获取类的相关信息,依然可以完成许多事情。许多的Java框架比如Spring、mybatis等等,都是基于反射实现的。

总之,最重要的就是要理解一点,对于java类,通常情况下是开发人员自己编写的java文件通过编译得到对应的class文件,或者直接引用别人的jar包得到对应的class文件,在这些方式里面,程序在运行之前就已经获取到类对应的信息了,就无需用到反射

反射的作用场景就在于,在程序运行之前,是获取不到对应的class文件的。这些class文件有可能是程序运行中从其他地方获取到的,有可能是动态生成的。在这种情况下,就需要通过反射在运行期来动态获取对应类的相关信息。

4、再次理解Class类

Java语言当中,一切都是类与对象。

比如说有三辆具体的车,每一辆车都有各自的属性(比如颜色)和相同方法(比如前进),我们可以抽象出来车这样一个类,三辆车中的每一辆都是车这个类的一个对象(实例)。

在生活中,既可以有车这个类,还可以有人这个类,也可以有树这个类,总之,我们可以有无穷多个类。那么其实我们可以站在更高的一个角度来看待问题,把各个类都抽象出来形成一个新的类别,这个类别名字就叫作“类”(Class),就像我们可以把几辆车抽象出来形成的一个类叫“车”一样。车这个类是抽象出来的新的“类”的一个实例,人这个类也是抽象出来的新的这个“类”的一个实例。

为了形象化一点,我们可以这样假想以便于理解:对于每一个类,比如车这个类Car,会有对应的Car.class,我们具有无数个类也就具有无数个class文件。想象一下其实每个class文件都是一个对象,一个叫做“类”的这个类的对象。

总而言之:车对应的类型是Car,可以有无数辆具体的车,但只会有一个Car类别;类对应的类型是Class类,可以有无数个具体的类(对应无数个class文件),但只会有一个Class类别。


http://www.niftyadmin.cn/n/969273.html

相关文章

酷狗音乐快速转换MP3格式的方法

喜欢听音乐的朋友们,散步跑步的时候都是随身听,音乐可以给人带来力量,让人心情愉悦,有时候甚至还可以让我们忘记烦恼和忧愁,是一种不错的解压方式,所以热爱运动的宝宝们是离不来音乐的陪伴的,这…

linux信号signal处理机制

信号是Linux编程中非常重要的部分,本文将详细介绍信号机制的基本概念、Linux对信号机制的大致实现方法、如何使用信号,以及有关信号的几个系统调用。 信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作…

python crawler - Session模拟表单登陆并下载登录后用户头像demo

要登录的网站:https://www.1point3acres.com/bbs/ 找到form中的action查看提交表单的目的地址: https://www.1point3acres.com/bbs/member.php?modlogging&actionlogin&loginsubmityes&infloatyes&lssubmityes&inajax1 登录后,查…

通过手机物理返回键实现弹出层的隐藏

通过手机物理返回键实现弹出层的隐藏 <template><div class"popup"><h1 click"popup">返回键隐藏弹出层</h1><div class"pop" :class"{up: flag}">弹出层文本<--点击关闭实现弹出层隐藏--><sp…

详细介绍优化SQL Server 2000的设置

优化SQL Server 2000的设置 SQL Server已经为了优化自己的性能而进行了良好的配置&#xff0c;比今天市场其他的关系型数据库都要好得多。然而&#xff0c;你仍然有几项设置需要进行修改&#xff0c;以便你的数据库每分钟可以处理更多的事务(TPM)。本篇文章的目的就是讨论这些设…

ajax post数据发送类型Content-Type详解

application/x-www-form-urlencoded 首先&#xff0c;Content-Type 被指定为 application/x-www-form-urlencoded&#xff1b;其次&#xff0c;提交的数据按照 key1val1&key2val2 的方式进行编码&#xff0c;key 和 val 都进行了 URL 转码。大部分服务端语言都对这种方式有…

Pandas - Series、DataFrame、plot(demo

import pandas import numpy as npfrom pandas import Series , DataFrames Series([1,2,3,np.nan,5,1]) print(s)0 1.0 1 2.0 2 3.0 3 NaN 4 5.0 5 1.0 dtype: float64numpy.random.randn() :以给定的形状创建一个数组&#xff0c;数组元素来符合标准正态…

行动总是远远少于想法

要说没时间&#xff0c;那是借口&#xff1b;要说时间多&#xff0c;也是自己骗自己。每天总是围着一些大大小小的、来来去去基本相同的事忙着&#xff0c;到了晚上&#xff0c;可能才有机会想想、做做自己真正想做的事。所以说工作久了&#xff0c;工作也仅仅只是一个工作而已…