java 反射api_Java的反射API

java 反射api

如果您曾经问​​过自己以下问题:
–“如何在字符串中仅包含其名称的方法调用?”
–“如何动态列出类中的所有属性?”
–“如何编写将任何给定对象的状态重置为默认值的方法?”

然后,您可能已经听说过Java的Reflection API,如果您还没有听说过,这是一个很好的机会,了解它的全部含义和用途。 此功能确实功能强大,但一如既往,必须使用一些良好的判断。

它给表带来的好处是能够分析有关类的信息,包括类的属性,方法,注释,甚至实例的状态,所有这些都可以在运行时进行。 这样获得的动力听起来真的很有用,不是吗?

在本教程中,我打算展示Reflection API的一些基本用法。 可以做什么,不应该做什么,优缺点。

所以……可以吗?

起点,

使用Reflection时,最重要的事情之一就是知道从哪里开始,知道什么类别可以让我们访问所有这些信息。 答案是: java.lang.Class <T>

假设我们有以下课程:

package com.pkg;public class MyClass{private int number;private String text;}

我们可以通过3种不同的方式获得Class的引用。 直接来自类,名称或实例:

Class<?> clazz1 = MyClass.class;
Class<?> clazz2 = Class.forName("com.pkg.MyClass");MyClass instance = new MyClass();
Class<?> clazz3 = instance.getClass();

提示这里我们看到一个重要的细节。 通常,将Class实例的标识符命名为clazz或clz之类,这看起来很奇怪,但这仅是因为class已经是Java语言中的保留字。

从类的参考中,我们可以浏览所有内容,找出它的成员,注释,甚至是包或ClassLoader,但是稍后我们将更详细地介绍所有这些,因此现在让我们集中讨论方法给我们有关班级本身的信息:

int getModifiers() 返回int内的类或接口的修饰符,以准确找出要应用的修饰符,我们应使用Modifier类提供的静态方法
布尔 isArray() 确定该类是否表示一个数组
boolean isEnum() 确定该类是否在源代码中声明为枚举
boolean isInstance(Object obj) 如果可以将通知对象分配给此Class表示的类型的对象,则返回true
boolean isInterface() 确定该类是否表示一个接口
boolean isPrimitive() 确定该类是否表示原始类型
T newInstance() 创建此类的新实例
类<? 超级 T> getSuperclass() 返回超类的引用,以防在Object类中被调用,它返回null

您可以直接在类文档中看到这些方法以及许多其他方法的完整定义。

提示要成功使用Modifier类中的方法,必须具有一些按位运算的基本知识,在这种情况下,最常见的是AND运算。

属性,

这些字段表示类的属性,就这么简单。 通过此类,我们可以获得有关它们的信息,但是在此之前,我们如何获得对Field的引用?

Class类内部,我们有几种不同的方法可以返回类的字段,作为破坏者,我已经说过, 方法构造函数都有等效的方法,但让我们首先关注属性

关于如何找出类的成员,我们有一些“重要的要记住”的事情,我将首先介绍方法,然后详细说明这些细节。

字段getField(字符串名称) 返回反映给定名称的类的public属性的Field
Field [] getFields() 返回反映该类的公共属性的字段数组。
字段getDeclaredField(字符串名称) 返回反映给定名称的类的声明属性的Field
Field [] getDeclaredFields() 返回反映该类的声明属性的字段数组。

好吧,好像我们有两组非常相似的方法,而且确实如此。 它们之间的差异是微妙的,并且很少有人知道它们。

getFieldgetFields方法仅返回类的公共属性。 某种程度上讲它更清洁,因为我们并不总是(很多时候不应该)与内部属性混在一起。 这样做的好处是,它仍然在超类中搜索属性, 沿着层次结构向上移动 ,这对我们来说很方便,但是同样,超类中的属性必须是公开的才能被找到。

现在, getDeclaredFieldgetDeclaredFields方法不是很干净,因为它们的任何声明属性都是有效的,可以是私有的,受保护的或其他任何属性。 因此,与普遍看法相反,通过反射访问私有成员并不那么复杂。 另一个不同之处在于,它们不搜索超类的属性,因此它们完全忽略了继承层次结构

让我们举例说明如何尝试访问类中的属性,以说明上述方法的使用以及Field类提供的其他方法。 以我们之前定义的MyClass类为例,假设我们想知道某个实例的text属性中包含的值。

MyClass instance = new MyClass();
instance.setText("Reflective Extraction");
Class<?> clazz = instance.getClass();
Field field = clazz.getDeclaredField("text"); // Accessing private attribute
Object value = field.get(instance);
System.out.println(value);

提示首先使用get方法可能会造成混淆,但这很简单。 手中有一个字段,您将发送要从中提取值作为参数的目标实例。 必须记住,该字段与类相关,与实例无关,因此,如果属性不是静态的,则需要一个具体的实例,以便可以提取值。 如果我们在谈论静态属性,则可以传递任何类型的任何实例,甚至可以传递null作为参数。

好的,我们使用getDeclaredField方法访问属性,传递字段的名称,并使用get方法检索其当前值。 完成了吧?

错误。 当我们运行代码时,我们看到抛出了一个异常,更具体地说是一个IllegalAccessException ,这不仅仅是因为我们拥有该字段的引用,而且我们可以按照自己的意愿来操纵它,但是还有一种解决方法。

Field的超类是AccessibleObject类,它也是MethodConstructor的超类,因此此说明对3中的任何一个都有效。

AccessibleObject类定义2个主要方法: setAccessible( booleanisAccessible() ,它们基本上定义了属性是否可访问。 在我们的例子中,私有属性是永远无法访问的,除非您在同一个类中,在这种情况下,您可以毫无问题地访问它们。 因此,我们要做的就是将文本配置为可访问。

MyClass instance = new MyClass();
instance.setText("Reflective Extraction");
Class<?> clazz = instance.getClass();
Field field = clazz.getDeclaredField("text"); // Accessing private attribute
field.setAccessible(true); // Setting as accessible
Object value = field.get(instance);
System.out.println(value);

然后我们就可以轻松打印出我们的价值

提示AccessibleObject类具有便捷的静态方法,也称为setAccessible,但它接收AccessibleObject数组和boolean标志。 此方法可用于一次性设置多个字段,方法或构造函数。 如果要将一个类中的所有属性设置为可访问,则可以执行以下操作:

MyClass instance = new MyClass();
Class<?> clazz = instance.getClass();
Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);

同样,我们可以从属性中提取值,也可以分配一个值。 我们使用set方法做​​到这一点。 调用它时,我们需要通知我们要修改的实例以及要设置为相应属性的值。

MyClass instance = new MyClass();
Class<?> clazz = instance.getClass();
Field field = clazz.getDeclaredField("text");
field.setAccessible(true);
field.set(instance, "Reflective attribution");
System.out.println(instance.getText());

动作,

在开始实际方法之前,我想强调一下,正如我之前所说的,它与FieldConstructor类非常相似,因此这3种可以通过引用以相同的方式获得,这意味着我们有一个getDeclaredField方法,我们还有一个getDeclaredMethodgetDeclaredConstructor (其他方法也是如此)。 因此,它们的工作方式完全相同,因此,再次解释它们将毫无意义且浪费时间。

那么,让我们开始吧,如何使用反射调用方法?

一个方法不像一个属性,仅靠它的名字是不够的,因为我们可以有许多不同的方法使用相同的名字,这被称为重载。 因此,要获取一种特定的方法,我们需要告知其名称以及所接收到的参数列表。 假设我们的类具有以下描述的方法:

public void print(String text){System.out.println(text);
}public void printHelloWorld(){System.out.println("Hello World");
}public int sum(int[] values){int sum = 0;for(int n : values){sum += n;}return sum;
}

为了获得对这些方法的引用,按照在示例中声明它们的顺序,我们可以这样做:

MyClass instance = new MyClass();
Class<?> clazz = instance.getClass();
Method print = clazz.getMethod("print", String.class);
Method printHelloWorld = clazz.getMethod("printHelloWorld");
Method sum = clazz.getMethod("sum", int[].class);

为了调用它们,我们使用invoke方法(巧合?

该方法的签名是这样的:

Object invoke(Object obj, Object... args)

这意味着,我们需要告知将在其上调用该方法的实例(目标)以及需要传递给它的参数(如果有)。 除此之外,它还返回一个Object ,这将是方法的返回值,无论它是什么。 如果该方法不返回任何内容( void ),则调用invoke将返回null

对于上面的3个方法引用,调用将如下所示:

print.invoke(instance, "I'm Mr. Reflection by now");
printHelloWorld.invoke(instance);
int sumValue = (int) sum.invoke(instance, new int[]{1, 4, 10});
System.out.println(sumValue);

提示: 同样,如果我们使用静态方法,则不必传递有效的实例,我们可以这样做:

staticMethod.invoke(null);

通用参数呢?

好吧,在这种情况下,我们需要对语言有所了解,然后才能调用它们。 有必要知道通用类型仅在编译时存在,并且当Reflection在运行时运行时,通用类型不再存在,所有这些都归因于一个名为Type Erasure的功能,该功能是为了与以前的Java版本保持向后兼容性而创建的。

接收通用参数的方法很可能会接收Object ,但是还有另一种可能性。 如果您声明这样的通用类型: <T extended CharSequence> ,则运行时方法可能会收到CharSequence ,因为它是最具体的类型,仍然保持通用,因此对于此方法:

public  print(T sequence){System.out.println(sequence)
}

反射调用可能如下所示:

Method print = clazz.getMethod(print, CharSequence.class);
print.invoke(instance);

创建实例

众所周知,即使构造函数的用法非常相似,它也不是方法。 就像我们正在调用方法一样,但是后面带有new关键字,并且仅当我们要创建类的新实例时才调用它。 在用法上的相似之处导致在反射方面的操作相似。 碰巧的是,我们将使用newInstance方法,而不是使用invoke,但有一些区别。

我们正在创建一个实例,因此没有实例与构造函数关联,因此我们不传递任何实例参数。 但是,正如我们对“ 方法 ”所做的那样,passamos会列出一个论据列表。

Class <T>也具有newInstance方法,但是仅当我们的类具有可访问的构造函数而没有参数时,它才有用。如果没有任何参数,我们需要直接引用我们的Constructor <T> 。 让我们在示例类中定义一些构造函数:

public class MyClass{private String text;private int number;public MyClass(){}public MyClass(String text){this.text = text;}public MyClass(String text, int number){this.text = text;this.number = number;}}

现在,让我们使用反射来获取它们:

Class clazz = MyClass.class;
Constructor c1 = clazz.getConstructor();
Constructor c2 = clazz.getConstructor(String.class);
Constructor c3 = clazz.getConstructor(String.class, int.class);

现在让我们创建3个实例,每个构造函数引用一个:

MyClass instance1 = c1.newInstance();
MyClass instance2 = c2.newInstance("text");
MyClass instance3 = c3.newInstance("other text", 1);

到此为止,我们可以看到我们几乎可以以任何想要的方式操作一个类,列出它的属性,获取对方法的引用,更改其状态,读取信息,但是我们仍然缺少一件事,我认为这也是很酷,更不用说

元数据,

当需要检查与注释有关的信息时,可以使用AnnotatedElement接口提供的方法。 仅供参考 :实现这些方法的层次结构中的第一个类是AccessibleObject ,因此所有子类都可以访问它们。

使用注释,我们可以定义有关类,属性和/或方法的信息,而无需实际编写任何执行代码。 批注将在另一时间处理,从而使开发更容易。 因此,让我们开始写一个小例子:

我们创建了1个名为@NotNull的注释,并且每当对属性进行注释时,它就无法保存值null。 让我们看一下代码:

public class MyClass{@NotNullprivate String text;}

好的,因此我们在文本属性中定义了此特征,但是如何有效验证此规则? 使用我们的Validator类,如下所示:

public class Validator{public static void validate(Object target) throws IllegalArgumentException, IllegalAccessException{Class<?> clazz = target.getClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields){validateField(field, target);}}private static void validateField(Field field, Object target) throws IllegalArgumentException, IllegalAccessException{field.setAccessible(true);Object value = field.get(target);if (field.isAnnotationPresent(NotNull.class)){if (value == null)throw new IllegalArgumentException("This attribute cannot be null");}}
}

因此,当我们验证对象时,我们可以使用此批注为我们工作,验证器将对其进行检查,将代码放在单个位置,从而使其更易于维护。 在代码的某些点上,我们将有一个这样的调用:

Validator.validate(myClassInstance);

我们完成了。 在开发框架时,该技术被广泛使用,通常您只需查看文档以查看每个注释会做什么,并相应地使用它们。

缺点,为什么我不应该使用反射?

我相信很清楚,在某些情况下使用反射会给我们带来很多好处和便利,但是众所周知,当我们拥有的只是一把锤子时,任何问题都像钉子一样,所以不要全神贯注地思考如何使用反射解决所有问题,因为它有缺点:

  • 性能开销 :使用反射时,JVM需要做大量工作来获取我们需要的所有信息,执行动态调用以及所有其他操作,因此这在处理时间上要付出一定的成本。
  • 运行时安全性 :为了运行反射代码,必须在虚拟机内部拥有一定的清除级别,而您可能并非一直如此,因此在考虑使用它时请记住这一点。
  • 模型安全性 :出于某种原因,我们的属性具有不同的可见性,对吗? 反射可以完全忽略它们,无论做什么,都可能在封装中引起一些警报。

基本规则是:仅在没有其他替代方法时才使用反射。 如果您可以做一些事而无需反思,您可能应该这样做。 您应该根据具体情况进行分析。

可以在Oracle教程中获得更多信息。

我知道谈论反射时还有其他功能,例如代理 ,但是它们更加高级和复杂。 我可能会在以后的文章中写到它们,但这超出了本文的范围,因为它仅用作本主题的介绍,或者是对已经知道这一点的人的复习,因此提出了高级主题弊大于利。

希望大家喜欢,下次再见!

参考:来自Rodrigo Sasaki博客博客的JCG合作伙伴 Rodrigo Sasaki的Java反射API 。

翻译自: https://www.javacodegeeks.com/2013/07/javas-reflection-api.html

java 反射api

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/347489.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL多表查询,SQL,笛卡尔积等值连接自连接外连接,SQL99新特性,完整详细可收藏

文章目录1、笛卡尔积&#xff08;交叉连接&#xff09;2、等值连接 vs 非等值连接3、自连接 vs 非自连接4、内连接 vs 外连接5、SQL99语法新特性多表查询&#xff0c;也称为关联查询&#xff0c;指两个或更多个表一起完成查询操作。 前提条件&#xff1a;这些一起查询的表之间…

针对开发人员的Microsoft SQL Server元数据

Microsoft SQL Server 2016最近在关系数据库管理系统&#xff08;RDBMS&#xff09;中处于领先地位。 高性能&#xff0c;安全性&#xff0c;分析和云兼容性的结合使其成为领先的RDBMS 。 SQL Server 2017甚至支持R和Python编程语言&#xff0c;这进一步提高了它在学术机构中的…

计算机怎么录制视频教程,怎么录制视频教程?查看电脑具体录屏方法

怎么录制视频教程&#xff1f;查看电脑具体录屏方法2020年04月07日 15:25作者&#xff1a;黄页编辑&#xff1a;黄页分享怎么录制视频教程?有时候在生活中遇到了喜欢的视频、直播等&#xff0c;想在电脑上通过录屏的方式记录下来。高质量的录屏视频怎么实现?其实具体录屏的方…

加密解密,MySQL单行函数,数学函数字符串日期时间,流程控制,完整详细可收藏查询SQL

前些天发现了十分不错的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;没有广告&#xff0c;分享给大家&#xff0c;大家可以自行看看。&#xff08;点击跳转人工智能学习资料&#xff09; 文章目录1、函数的理解2、数值函数2.1 基本函数2.2 角度与弧度…

无锡太湖学院计算机科学与技术宿舍,无锡太湖学院宿舍条件,宿舍环境图片(10篇)...

无锡太湖学院宿舍条件,宿舍环境图片(10篇)大学宿舍是一个神奇的地方&#xff0c;来自于天南地北的同学聚集在一个屋檐下&#xff0c;并将在一起度过宝贵的大学时光。人们常说&#xff0c;没有住过宿舍的大学是不完整的。当然不同的大学宿舍环境条件都不一样。高考升学网小编本文…

SELECT执行过程,MySQL聚合函数,多行分组函数,GROUP BY HAVING,详细完整可收藏

文章目录1、聚合函数介绍2、五个常用聚合函数3、GROUP BY4、HAVING5、SELECT的执行过程1、聚合函数介绍 聚合函数作用于一组数据&#xff0c;并对一组数据返回一个值。聚合函数不能嵌套调用。比如不能出现类似“AVG(SUM(字段名称))”形式的调用。 2、五个常用聚合函数 2.1 A…

嵌套套娃,MySQL子查询,单行与多行子查询,相关和不相关(关联)子查询,完整详细可收藏

文章目录1、需求分析与问题解决2、单行子查询3、多行子查询4、相关子查询5、抛一个思考题子查询指一个查询语句嵌套在另一个查询语句内部的查询&#xff0c;这个特性从MySQL 4.1开始引入。SQL 中子查询的使用大大增强了 SELECT 查询的能力&#xff0c;因为很多时候查询需要从结…

键盘连接在计算机的,技巧:如何在计算机键盘上输入连字符和破折号?

文字/连字符在哪里&#xff0c;我该如何输入&#xff1f;例如&#xff0c;某些英语单词中有连字符&#xff0c;例如高层。当然&#xff0c;有时是否有连字符都没有关系&#xff0c;并且不影响其含义。但是&#xff0c;有时有必要。例如&#xff0c;单词在行尾的位置是不够的。您…

删库跑路?不可回滚?MySQL创建和管理表,修改清空表,MySQL8新特性DDL原子化,完整详细可收藏

文章目录1、基础知识2、创建和管理数据库3、创建表4、修改表5、重命名表6、 删除表7、清空表8、MySQL8新特性—DDL的原子化1、基础知识 1.1 一条数据存储的过程 存储数据是处理数据的第一步 。只有正确地把数据存储起来&#xff0c;我们才能进行有效的处理和分析。否则&#x…

不全?MySQL数据类型精讲,定点日期枚举文本字符串,json二进制,空间,选择建议,完整详细可收藏

文章目录1. MySQL中的数据类型2. 整数类型3. 浮点数类型4. 定点数类型5. 位类型&#xff1a;BIT6. 日期与时间类型7. 文本字符串类型8. ENUM类型9. SET类型10. 二进制字符串类型11. JSON 类型12. 空间类型13. 选择建议1. MySQL中的数据类型 常见数据类型的属性&#xff0c;如下…

约束,MySQL约束,非空默认值,主键外键唯一自增,完整详细可收藏

文章目录1. 约束(constraint)概述2. 非空约束3. 唯一性约束4. PRIMARY KEY 约束5. 自增列&#xff1a;AUTO_INCREMENT6. FOREIGN KEY 约束7. CHECK 约束8. DEFAULT约束9. 面试1. 约束(constraint)概述 1.1 为什么需要约束 数据完整性&#xff08;Data Integrity&#xff09;是…

如何测试Java类的线程安全性

我在最近的一次网络研讨会中谈到了这个问题&#xff0c;现在是时候以书面形式进行解释了。 线程安全是Java等语言/平台中类的重要品质&#xff0c;我们经常在线程之间共享对象。 缺乏线程安全性导致的问题很难调试&#xff0c;因为它们是零星的并且几乎不可能有意复制。 您如何…

争议?MySQL存储过程与函数,封装,体,完整详细可收藏

文章目录1. 存储过程概述2. 创建存储过程3. 调用存储过程4. 存储函数的使用5. 存储过程和函数的查看、修改、删除6. 关于存储过程使用的争议MySQL从5.0版本开始支持存储过程和函数。存储过程和函数能够将复杂的SQL逻辑封装在一起&#xff0c;应用程序无须关注存储过程和函数内部…

梦幻,MySQL视图,虚实表,完整详细可收藏

文章目录1. 数据库对象2. 视图概述3. 创建视图4. 查看视图5. 更新视图的数据6. 修改、删除视图7. 总结1. 数据库对象 2. 视图概述 2.1 为什么使用视图&#xff1f; 视图一方面可以帮我们使用表的一部分而不是所有的表&#xff0c;另一方面也可以针对不同的用户制定不同的查询视…

一条一条来,MySQL变量、流程控制与游标,完整详细可收藏

前些天发现了十分不错的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;没有广告&#xff0c;分享给大家&#xff0c;大家可以自行看看。&#xff08;点击跳转人工智能学习资料&#xff09; 文章目录1. 变量2. 定义条件与处理程序3. 流程控制4. 游标MyS…

无服务器:SLAppForge Sigma入门

&#xff01; Cmere。 寻找某事&#xff1f; 无服务器&#xff0c;是吗&#xff1f; 在上面。 进入海峡&#xff0c;在“ Sigma”标志处右转。 &#xff08;嗯&#xff0c;还不要怪我们&#xff1b;至少我们认为这很容易&#xff01;&#xff09; 我们的梦想目标之一是与S…

自动,MySQL触发器,完整详细可收藏

前些天发现了十分不错的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;没有广告&#xff0c;分享给大家&#xff0c;大家可以自行看看。&#xff08;点击跳转人工智能学习资料&#xff09; 文章目录1. 触发器概述2. 触发器的创建3. 查看、删除触发器4.…

统计信号处理基础 - 估计与检测理论 估计部分习题3.7公式推导

统计信号处理基础 - 估计与检测理论 估计部分习题3.7公式推导题目证明结论得证题目 相信学习信号检测与估计的童鞋们肯定看到过Steven M.Kay大牛的书&#xff0c;非常厚的一本&#xff0c;不得不说&#xff0c;人家的书就是写得好&#xff0c;浅显易懂&#xff08;当然是要从头…

机器学习大作业《Kaggle赛题之Kannada MNIST研究》论文和Python代码

一、赛题概述及分析 1、背景介绍 邮政系统每天都会处理大量的信件&#xff0c;最为要紧的一环是要根据信件上的收信人邮编进行识别和分类&#xff0c;以便确定信件的投送地。原本这项任务是依靠大量的人工来进行&#xff0c;后来人们尝试让计算机来替代人工。然而&#xff0c…

neo4j图形界面_图形处理:betweeness中心性– neo4j的密码与graphstream

neo4j图形界面上周&#xff0c; 我写了关于中间性中心算法以及我尝试使用graphstream 理解它的尝试 &#xff0c;在阅读源代码的同时&#xff0c;我意识到我可以使用neo4j的所有最短路径算法将某些东西放在一起。 概括地说&#xff0c;中间性中心度算法用于确定图中节点的负载…