编程技术是改变世界的力量。
本站
当前位置:网站首页 > 后端语言 > 正文

Java 反射详解 java反射是干嘛的

gowuye 2024-04-04 11:58 4 浏览 0 评论

Java 反射是一个比较重要的知识点,你会在很多地方见到反射。它提供了 Java 语言在运行期间加载、探知和使用编译期间完全未知的类的能力。这种能力在框架的编写中非常常见,例如动态代理中、类扫描解析中。

反射的定义与作用

反射机制:即 Java 语言在运行时有一种自观的能力,能够了解自身的情况并为下一步的动作做准备。反应出来就是,在运行时,对于一个类,我们能够知道该类有哪些方法和属性。对于一个对象,我们能够调用其任意的一个方法。这是一种动态获取类的信息以及动态调用对象方法的能力。

实现反射的基础

Java 提供反射机制,依赖于 Class 类和 java.lang.reflect 类库。其主要的类如下:

  1. Class:表示类或者接口
  2. Field:表示类中的成员变量
  3. Method:表示类中的方法
  4. Constructor:表示类的构造方法
  5. Array:该类提供了动态创建数组和访问数组元素的静态方法

Class

Class 类是 Java 中用来表示运行时类型信息的对应类。实际上在 Java 中每个类都有一个 Class 对象,每当我们编写并且编译一个新创建的类就会将相关信息写到 .class 文件里。当我们 new 一个新对象或者引用静态成员变量时,JVM 中的类加载器子系统会将对应 Class 对象加载到 JVM 中,然后 JVM 再根据这个类型信息相关的 Class 对象创建我们需要实例对象或者提供静态变量的引用值。我们可以将 Class 类,称为类类型,一个 Class 对象,称为类类型对象(参考 《Thinking in Java》)。

Class 类有以下的特点:

  1. Class 类也是类的一种,class 则是关键字。
  2. Class 类只有一个私有的构造函数,只有 JVM 能够创建 Class 类的实例。
  3. 对于同一类的对象,在 JVM 中只有唯一一个对应的 Class 类实例来描述其类型信息。(同一个类:即包名 + 类名相同,且由同一个类加载器加载)

.class 文件存储了一个 Class 的所有信息,比如所有的方法,所有的构造函数,所有的字段(成员属性)等等。JVM 启动的时候通过 .class 文件会将相关的类加载到内存中,过程如下:

获取 Class 实例的方法

上面提到 Class 类只有一个私有的构造函数。所以无法通过 new 的方法获取 Class 实例。

/*
 * Constructor. Only the Java Virtual Machine creates Class
 * objects.
 */
private Class() {}

Class.forName() 方法

可以通过 Class 的 forName 方法获取 Class 实例,其中类的名称要写类的完整路径。该方法只能用于获取引用类型的类类型对象。

// 这种方式会使用当前的类的加载器加载,并且会将 Class 类实例初始化
Class<?> clazz = Class.forName("java.lang.String");
// 上面的调用方式等价于
Class<?> clazz = Class.forName("java.lang.String", true, currentLoader);

使用该方法可能会抛出 ClassNotFoundException 异常,这个异常发生在类的加载阶段,原因如下:

  1. 类加载器在类路径中没有找到该类(检查:查看所在加载的类以及其所依赖的包是否在类路径下)
  2. 该类已经被某个类加载器加载到 JVM 内存中,另外一个类加载器又尝试从同一个包中加载

Object.getClass() 方法

如果我们有一个类的对象,那么我们可以通过 Object.getClass 方法获得该类的 Class 对象。

// String 对象的 getClass 方法
Class clazz1 = "hello".getClass();
// 数组对象的 getClass 方法
Class clazz2 = (new byte[1024]).getClass();
System.out.println(class2) // 会输出 [B, [ 代表是数组, B 代表是 byte。即 byte 数组的类类型

class 语法

若我们知道要获取的类类型的名称时,我们可以使用 class 语法获取该类类型的对象。

// 类
Class clazz = Integer.class;
// 数组
Class clazz2 = int [][].class;

包装类的 TYPE 静态属性

对于基本类型和 void 都有对应的包装类。在包装类中有一个静态属性 TYPE,保存了该来的类类型。以 Integer 类为例,其源码中定义了如下的静态属性:

/**
 * The {@code Class} instance representing the primitive type
 * {@code int}.
 *
 * @since   JDK1.1
 */
@SuppressWarnings("unchecked")
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

生成 Class 类实例的方法:

Class clazz1 = Integer.TYPE;
Class clazz2 = Void.TYPE;

Class 类的方法

Class 中有获取其他 Class 的方法,列举如下:

  1. Class.getSuperclass():获取该类的父类
  2. Class.getClasses() :获取该类所有公共类、接口、枚举组成的Class 数组,包括继承的
  3. Class.getDeclaredClasses():获取该类显式声明的所有类、接口、枚举组成的 Class 数组
  4. (Class/Field/Method/Constructor).getDeclaringClass():获取该类/属性/方法/构造函数所在的类

Member & AccessibleObject

在讲 Field、Method、Constructor 之前,先说说 Member 和 AccessibleObject。Member 是一个接口,表示 Class 的成员,前面的三个类都是其实现类。

AccessibleObject 是 Field、Method、Constructor 三个类共同继承的父类,它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。通过 setAccessible 方法可以忽略访问级别,从而访问对应的内容。并且 AccessibleObject 实现了 AnnotatedElement 接口,提供了与获取注解相关的能力。

Field

Field 提供了有关类或接口的单个属性的信息,以及对它的动态访问的能力。

可以通过 Class 提供的方法,获取 Field 对象,具体如下:

方法返回值

方法名称

方法说明

Field

getDeclaredField(String name)

获取指定name名称的(包含private修饰的)字段,不包括继承的字段

Field[]

getDeclaredField()

获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段

Field

getField(String name)

获取指定name名称、具有public修饰的字段,包含继承字段

Field[]

getField()

获取修饰符为public的字段,包含继承字段

Field 相关的 API 我就不全局列举出来了,可以点击这里查看。

Method

Method 提供了有关类或接口的单个方法的信息,以及对它的动态访问的能力。

可以通过 Class 提供的方法,获取 Field 对象,具体如下:

方法返回值

方法名称

方法说明

Method

getDeclaredMethod(String name, Class<?>... parameterTypes)

返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。

Method[]

getDeclaredMethod()

返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

Method

getMethod(String name, Class<?>... parameterTypes)

返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

Method[]

getMethods()

返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。

Method 相关的 API 可以点击这里查看。

Constructor

Constructor 提供了有关类的构造方法的信息,以及对它的动态访问的能力。

可以通过 Class 提供的方法,获取 Constructor 对象,具体如下:

方法返回值

方法名称

方法说明

Constructor<T>

getConstructor(Class<?>... parameterTypes)

返回指定参数类型、具有public访问权限的构造函数对象

Constructor<?>[]

getConstructors()

返回所有具有public访问权限的构造函数的Constructor对象数组

Constructor<T>

getDeclaredConstructor(Class<?>... parameterTypes)

返回指定参数类型、所有声明的(包括private)构造函数对象

Constructor<?>[]

getDeclaredConstructor()

返回所有声明的(包括private)构造函数对象

Constructor 相关的 API 我就不全局列举出来了,可以点击这里查看。

Array

在 Java 中数组也是一种类,Array 提供了动态创建数组和访问数组元素的静态方法。

通过 getXXX(Object array, int index) 方法,传入数组对象和下标索引,可以获取到该位置的值。

通过 newInstance(Class<?> componentType, int length) 方法,传入数组类型和长度,创建数组。如下:

// 下面创建的两个数组等价
int x[] = new int[10];
int y[] = (int[]) Array.newInstance(int.class, 10);
// 输出 true
System.out.println(x.length == y.length);

通过 newInstance(Class<?> componentType, int... dimensions) 方法,创建多维数组。如下:

// 下面创建的两个数组等价
int x[][] = new int[10][10];
int y[][] = (int[][]) Array.newInstance(int.class, 10, 10);
// 输出 true
System.out.println(x.length == y.length);

反射的应用场景

几乎所有的 Java 框架都会使用到反射,例如动态配置:读取写好的配置文件的值,然后通过反射机制将这些值设置到配置类中。总的来说应用场景有如下几点:

  1. 框架开发中使用,例如动态配置
  2. 插件开发中使用,例如持续集成
  3. 应用扩展

当然反射也有一些缺点:

  1. 性能低
  2. 可读性差
  3. 只能在运行期间报错

相关推荐

PHPMailer远程命令执行漏洞分析

摘要:PHPMailer是一个强大的PHP编写的邮件发送类,但近日被爆出远程命令执行漏洞,该漏洞实际上是什么,有何种影响,本文对该漏洞进行了分析及验证方法,并给出防护方案。0x00漏洞概要PHPMa...

「安全漏洞」DedeCMS-5.8.1 SSTI模板注入导致RCE

漏洞类型SSTIRCE利用条件影响范围应用漏洞概述2021年9月30日,国外安全研究人员StevenSeeley披露了最新的DedeCMS版本中存在的一处SQL注入漏洞以及一处SSTI导致的RCE...

回顾使用PHP原生发送电子邮件(终)文件附件

FileAttachments文件附件Fileattachmentsworkjustlikemixedemail,exceptthatadifferentcontenttyp...

php-fpm.conf &amp; php.ini 安全优化实践

0x01关于php其历史相对已经比较久远了,这里也就不废话了,属弱类型中一种解释型语言除了web开发以及写些简单的exp,暂未发现其它牛逼用途,暂以中小型web站点开发为主另外,低版本的php自身...

linux 安全配置 ossec 开源检测

linux安全配置ossec开源检测一:介绍主要功能有日志分析、完整性检查、rootkit检测、基于时间的警报和主动响应。除了具有入侵检测系统功能外,它还一般被用在SEM/SIM(安全事件管理(...

PHP使用PHPMailer发送验证码邮件的方法与调用逻辑

首先我们需要下载PHPMailer:https://github.com/PHPMailer/PHPMailer一般情况下我们只需要压缩包中的src文件夹中的文件,并保存至根目录即可:设置一个文件,如...

回顾使用PHP原生发送电子邮件(一)

IwishIcouldremembertheveryfirstemailmessageIeversent.Unfortunately,thetruthisthatI...

PHPMAILER实现PHP发邮件功能php实例

这篇文章主要为大家详细介绍了PHPMAILER实现PHP发邮件功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下本文实例为大家分享了PHPMAILER实现PHP发邮件功能的具体代码,供大家参考,具...

500道网络安全面试题集锦(附答案)

本篇文章内容为网络安全各个方向涉及的面试题,星数越多代表问题出现的几率越大,但是无论如何都无法覆盖所有的面试问题,更多的还是希望由点达面,查漏补缺,然后祝各位前程似锦,都能找到自己满意的工作!一、We...

网站放家里,随处看电影「Apache+php+ssl 安装和配置」

  使用5G网络,随处都可以看到放自己家里电脑的视频。这个功能很容易实现,不需要太多的专业知识,也不需要额外花钱。如果确实需要,最多花不到两百块钱买一台旧电脑放家里,做个网站,就能解决全部问题,Fre...

Windows2008中 Magic Winmail Server提权

MagicWinmailServer是安全易用全功能的邮件服务器软件,不仅支持SMTP/POP3/IMAP/Webmail/LDAP(公共地址簿)/多域/发信认证/反垃圾邮件/邮件过滤/邮件组...

利用PHPmailer发送邮件

早上帮朋友做一个收集客户联系方式的页面,要求能实时推送信息给管理员。刚开始想到做后台管理,因为时间紧,做后台是赶不上了。想过通过短信发送,成本太高,否决了。。。灵机一动,客户提交时直接把信息发送到邮箱...

phpmailer发送邮件

phpmailer发送邮件PHP内置的mail函数使用起来不够方便,另外受其他语言的影响,博主更偏好面向对象的包管理模式,因此phpmailer成为了我用PHP发送邮件的首选,这里分享给大家。库导入这...

PHP初级教程:读取输入

PHP读取输入:Form:?formaction="welcome.php"method="post">Name:(inputtype="text...

php filter 验证Email,Url,Ip格式

今天发现一个非常好用的函数东西,filter过滤器,用于验证和过滤来自非安全来源的数据,比如用户的输入。验证Email:$email='1234567@qq.com';if(!filter_v...

取消回复欢迎 发表评论: