如何有效地使用反射

本文是我们名为“ 高级Java ”的学院课程的一部分。

本课程旨在帮助您最有效地使用Java。 它讨论了高级主题,包括对象创建,并发,序列化,反射等。 它将指导您完成Java掌握的过程! 在这里查看 !

目录

1.简介 2.反射API 3.访问泛型类型参数 4.反射API和可见性 5.反射API陷阱 6.方法句柄 7.方法参数名称 8.接下来 9.下载源代码

1.简介

在本教程的这一部分中,我们将简要介绍一个非常有趣的主题,即反射反射是程序在运行时检查或自检的能力。 反射是一项极其有用且功能强大的功能,它可以极大地扩展程序的功能,以在执行过程中执行其自身的检查,修改或转换,而无需一行代码更改。 并非所有的编程语言实现都支持此功能,但是幸运的是Java自一开始就采用了此功能。

2.反射API

反射API是Java标准库的一部分,它提供了一种在运行时探索内在类详细信息,动态创建新类实例(无需显式使用new运算符),动态调用方法,自省注释(已添加注释)的方法。在教程的第5部分“ 如何以及何时使用Enums和Annotations”中进行了介绍 ,以及更多其他内容。 它使Java开发人员可以自由编写代码,这些代码可以在运行时自行调整,验证,执行甚至修改自己。

Reflection API以非常直观的方式设计,并托管在java.lang.reflect包下。 它的结构严格遵循Java语言概念,并具有表示类(包括通用版本),方法,字段(成员),构造函数,接口,参数和注释的所有元素。 Reflection API的入口点是Class< ? > Class< ? >类。 例如,列出String类的所有公共方法的最简单方法是使用getMethods()方法调用:

final Method[] methods = String.class.getMethods();
for( final Method method: methods ) {System.out.println( method.getName() );
}

按照相同的原则,我们可以使用getFields()方法调用列出String类的所有公共字段,例如:

final Field[] fields = String.class.getFields();
for( final Field field: fields ) {System.out.println( field.getName() );
}

继续使用反射对String类进行实验,让我们尝试创建一个新实例,并在其上调用length()方法,所有这些仅使用反射API

final Constructor< String > constructor = String.class.getConstructor( String.class );
final String str = constructor.newInstance( "sample string" );
final Method method = String.class.getMethod( "length" );
final int length = ( int )method.invoke( str );
// The length of the string is 13 characters

反射最需要的用例可能围绕注释处理。 注释本身(不包括Java标准库中的注释)对代码没有任何影响。 但是,Java应用程序可以在运行时使用反射来检查它们感兴趣的不同Java元素上存在的注释,并根据注释及其属性应用某些逻辑。 例如,让我们看一下自省的方式是否在类定义中存在特定的批注:

@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.TYPE )
public @interface ExampleAnnotation {// Some attributes here
}@ExampleAnnotation
public class ExampleClass {// Some getter and setters here
}

使用反射API ,可以使用getAnnotation()方法调用轻松完成。 返回的非null值表示存在注释,例如:

final ExampleAnnotation annotation =ExampleClass.class.getAnnotation( ExampleAnnotation.class );if( annotation != null ) {// Some implementation here
}

如今,大多数Java API都包含注释,以方便开发人员使用和集成它们。 非常流行的Java规范的最新版本,例如RESTful Web服务的Java API ( https://jcp.org/en/jsr/detail?id=339 ), Bean验证 ( https://jcp.org/en/jsr / detail?id = 349 ), Java临时缓存API ( https://jcp.org/en/jsr/detail?id=107 ), Java消息服务 ( https://jcp.org/en/jsr/detail? id = 343 ), Java Persistence ( https://jcp.org/en/jsr/detail?id=338 )以及许多其他构建在注释之上,它们的实现通常大量使用Reflection API来收集有关正在运行的应用程序。

3.访问泛型类型参数

自从引入泛型(本教程的第4部分“ 如何和何时使用泛型”介绍了泛型 )以来, Reflection API已得到扩展,以支持对泛型类型的自省。 在许多不同的应用程序中经常弹出的用例是弄清楚用其声明了特定类,方法或其他元素的通用参数的类型。 让我们看一下示例类声明:

public class ParameterizedTypeExample {private List< String > strings;public List< String > getStrings() {return strings;}
}

现在,在使用反射检查类时,非常容易知道将strings属性声明为具有String类型参数的泛型类型List 。 下面的代码段说明了如何实现:

final Type type = ParameterizedTypeExample.class.getDeclaredField( "strings" ).getGenericType();if( type instanceof ParameterizedType ) {final ParameterizedType parameterizedType = ( ParameterizedType )type;for( final Type typeArgument: parameterizedType.getActualTypeArguments() ) {System.out.println( typeArgument );}
}

以下通用类型参数将被打印在控制台上:

class java.lang.String

4.反射API和可见性

在本教程的第1部分“ 如何创建和销毁对象”中 ,我们第一次遇到了Java语言支持的可访问性和可见性规则。 可能会令人惊讶,但是反射API能够以某种方式修改给定类成员的可见性规则。

让我们看一下带有单个私有字段名称的类的以下示例。 提供了此字段的getter,但没有提供setter,这是有意为之。

public static class PrivateFields {private String name;public String getName() {return name;}
}

显然,对于任何Java开发人员而言,显然都无法使用Java语言语法构造来设置name字段,因为该类无法提供实现此目的的方法。 关于救援的反射API ,让我们看看如何通过更改字段的可见性和可访问范围来完成。

final PrivateFields instance = new PrivateFields();
final Field field = PrivateFields.class.getDeclaredField( "name" );
field.setAccessible( true );
field.set( instance, "sample name" );
System.out.println( instance.getName() );

以下输出将打印在控制台上:

sample name

请注意,如果没有field.setAccessible( true )调用,则会在运行时引发异常,说明无法访问带有修饰符private的类的成员。

反射API的此功能通常由测试支架或依赖项注入框架使用,以便访问内部(或不可暴露的)实现细节。 除非您别无选择,否则请尝试避免在应用程序中使用它。

5.反射API陷阱

另外,请注意,即使反射API非常强大,也有一些陷阱。 首先,它是安全权限的主题,可能并非在您的代码正在其上运行的所有环境中都可用。 其次,它可能会对您的应用程序产生性能影响。 从执行前景来看,对反射API的调用非常昂贵。

最后, 反射API不能提供足够的类型安全保证,迫使开发人员在大多数地方使用Object实例,并且在转换构造函数/方法参数或方法返回值方面受到很大限制。

自Java 7发行以来,有一些有用的功能可以提供更快,另一种方式来访问某些功能,这些功能以前只能通过反射调用来使用。 下一节将向您介绍它们。

6.方法句柄

Java 7版本向JVM和Java标准库(方法句柄)引入了一个非常重要的新功能。 方法句柄是对基础方法,构造函数或字段(或类似的低级操作)的类型化,直接可执行的引用,具有自变量或返回值的可选转换。 从许多方面来看,它们是使用Reflection API执行的方法调用的更好替代方法。 让我们看一下使用方法句柄动态调用String类上的方法length()的代码片段。

final MethodHandles.Lookup lookup = MethodHandles.lookup();
final MethodType methodType = MethodType.methodType( int.class );
final MethodHandle methodHandle =lookup.findVirtual( String.class, "length", methodType );
final int length = ( int )methodHandle.invokeExact( "sample string" );
// The length of the string is 13 characters

上面的示例不是很复杂,只是概述了方法处理能力的基本概念。 请将其与使用“ 反射API ”部分中的“ 反射API”的相同实现进行比较。 但是,它看起来确实有些冗长,但是从性能和类型安全性角度来看,预期的方法句柄是更好的选择。

方法句柄是非常强​​大的工具,它们为在JVM平台上有效实现动态(和脚本)语言提供了必要的基础。 在本教程的第12部分“ 动态语言支持”中 ,我们将介绍其中的几种语言。

7.方法参数名称

Java开发人员多年来面临的一个众所周知的问题是,方法参数名称在运行时没有保留,而是被彻底清除了。 几个社区项目,例如Paranamer ( https://github.com/paul-hammant/paranamer ),试图通过向生成的字节码中注入一些其他元数据来解决此问题。 幸运的是,Java 8通过引入新的编译器参数–parameters改变了这一点,该–parameters将确切的方法参数名称注入字节码中。 让我们看一下以下方法:

public static void performAction( final String action, final Runnable callback ) {// Some implementation here
}

在下一步中,让我们使用Reflection API检查该方法的方法参数名称,并确保保留它们:

final Method method = MethodParameterNamesExample.class.getDeclaredMethod( "performAction", String.class, Runnable.class );
Arrays.stream( method.getParameters() ).forEach( p -> System.out.println( p.getName() ) );

指定了-parameters编译器选项后,以下参数名称将被打印在控制台上:

action
callback

对于许多Java库和框架的开发人员来说,这一期待已久的功能确实让他们大为放松。 从现在开始,仅使用纯Java 反射API即可提取更多有用的元数据,而无需引入任何其他变通方法(或黑客手段)。

8.接下来

在本教程的这一部分中,我们介绍了反射API ,它是检查代码,从代码中提取有用的元数据甚至进行修改的方法。 尽管存在所有缺点,但反射API如今已在大多数(如果不是全部)Java应用程序中得到了广泛使用。 在本教程的下一部分中,我们将讨论Java中的脚本和动态语言支持。

9.下载源代码

这是“ 反射”课程,是高级Java课程的第11部分。 您可以在此处下载源代码: advanced-java-part-11

翻译自: https://www.javacodegeeks.com/2015/09/how-to-use-reflection-effectively.html

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

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

相关文章

从字符串 const str = ‘qwbewrbbeqqbbbweebbbbqee‘;中能得到结果 [“b“, “bb“, “bbb“, “bbbb“] 以下错误语句是?

从字符串 const str qwbewrbbeqqbbbweebbbbqee;中能得到结果 ["b", "bb", "bbb", "bbbb"] 以下错误语句是&#xff1f;(B) A.str.match(/b/g) B.str.match(/b*/g) C.str.match(/b{1,4}/g) D.str.match(/b{1,5}/g) 解析&#xff1…

Burp Collaborator 使用总结

0x00&#xff1a;使用原因 我们在做渗透测试的时候&#xff0c;经常会遇到这种情况&#xff0c;测试跨站可能有些功能插入恶意脚本后无法立即触发&#xff0c;例如提交反馈表单&#xff0c;需要等管理员打开查看提交信息时才会触发&#xff0c;或者是盲注跨站&#xff0c;盲打 …

安装phpssdb扩展:

安装 igbinary 扩展(安装phpssdb扩展时候要用到--enable-ssdb-igbinary): clone https://github.com/igbinary/igbinary.git 安装phpssdb 扩展 &#xff1a;git clone https://github.com/jonnywang/phpssdb.git ; phpssdb 安装文档&#xff1a;https://github.com/jon…

在HTML中嵌入PHP代码,有以下几种方法,其中错误的是( )

在HTML中嵌入PHP代码&#xff0c;有以下几种方法&#xff0c;其中错误的是&#xff08; D&#xff09; A.以”<?php开头&#xff0c;以“?>”结束&#xff0c;中间为PHP代码。 B.以<script language“php”>开头&#xff0c;</script> 结束&#xff0c;中…

各大src地址

一、目录&#xff08;以下排名不分先后&#xff09; 1.360安全应急响应中心&#xff08;360SRC&#xff09;&#xff0d;http://security.360.cn/ 2.联想安全应急响应中心&#xff08;LSRC&#xff09;&#xff0d;http://lsrc.lenovo.com/ 3.腾讯安全应急响应中心&#xff…

文本”Hello, world.”显示的颜色是?

文本”Hello, world.”显示的颜色是? <style> #content .text {text-color:red;} #content>.title {color:green;} #content div.title {font-color:blue;} strong {font-color:yellow;} * {color:black;} </style> <div id"content"> <…

jrockit_1.6下载_Oracle JRockit Mission Control 4.1发布

jrockit_1.6下载Oracle发布了以前的仅JRockit专用工具Mission Control Suite&#xff08;JRMC&#xff09;的新版本。 4.1版本是次要版本升级&#xff0c;直接遵循4.0.1&#xff08;该版本发布于2010年中期&#xff09;。 但是&#xff0c;即使版本号表明是次要的升级&#xff…

dnslog盲注原理

Dnslog盲注原理 布尔盲注和时间盲注相当于猜单词的游戏&#xff0c;我们需要对每一位逐步的猜测&#xff0c;效率很低&#xff0c;需要发送很多的请求进行判断&#xff0c;很可能会触发安全设备的防护 我们需要一种方式能够减少请求&#xff0c;直接回显数据——Dnslog注入 Dn…

Sharepoint Ribbon Loaction

https://msdn.microsoft.com/zh-cn/library/ee537543%28voffice.14%29?f255&MSPPError-2147217396 列表视图相关loaction: Ribbon.List.CustomViews转载于:https://www.cnblogs.com/qiumc/p/4795020.html

JavaScript实现继承的方式,不正确的是:

JavaScript实现继承的方式&#xff0c;不正确的是&#xff1a;DA.原型链继承 B.构造函数继承 C.组合继承 D.关联继承 解析 javaScript实现继承共6种方式&#xff1a; 原型链继承、借用构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合式继承。

如何创建和销毁对象

本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的过程&#xff01; 在这里查看 &#xff01; 目录 1.简…

msf各种弱口令爆破

msf各种弱口令爆破 Msf: 记录下msf各个爆破弱口令的模块 run post/windows/gather/arp_scanner RHOSTS10.10.10.0/24 使用arp_scanner模块 检测在线主机 metasploit 增加路由 route add 10.10.1.3 255.255.255.0 1使用扫描模块 use scanner/portscan/tcp爆破ssh Msf>us…

[学习笔记]批次需求计划系统-简要

一、该系统的目的二、系统特色(1)来源根据 如上图(2)仅仅补充需求来源的最大值&#xff0c;避免料件多买而造成浪费(3)可透过[发放LRP工单]将生产计划发放成正式工单(4)可透过[发放LRP採购单]将採购计划发放成正式的请购单或者採购单(5)系统会记录计划的来源单据&#xff0c;方…

在浏览器控制台执行以下代码,输入的结果是()

在浏览器控制台执行以下代码,输入的结果是(A) A.4400 4401 4399 4400 B.4400 4401 4401 4402 C.4400 4400 4399 4400 D.4400 4401 4399 4402 E.4400 4401 4401 4400 解析 js在执行之前

iOS-心跳

转载于:https://www.cnblogs.com/zhuyaguang/p/4800703.html

google国内镜像网址收集

搞IT的遇到问题&#xff0c;光靠baidu有时真的解决不了问题&#xff0c;所以时不时的就需要求助google&#x1f602;&#xff0c; 里面有好多国外网友的博客、stackoverflow、github issues、官方文档等等的大量一手英文资料&#xff0c; 但是因为种种原因国家一直不放开Google…

根据 HTML 规范,以下代码中,外层容器 .outer 的宽高分别是:

根据 HTML 规范&#xff0c;以下代码中&#xff0c;外层容器 .outer 的宽高分别是&#xff1a;B <style> .outer { height: 50px; } .inner { width: 120px; height: 100px; } </style><div class"outer"> <span class"inner">co…

分而治之_播放框架模块:分而治之

分而治之通常情况是您开始开发应用程序并继续满足需求。 当您的应用程序变得更大时&#xff0c;您开始意识到将其分为不同组件的便利。 而且&#xff0c;当您开发第二个或第三个应用程序时&#xff0c;您开始认识到可以在不同应用程序之间重用的某些功能。 这是模块化应用程序的…

Mysql身份认证漏洞及利用(CVE-2012-2122)

当连接MariaDB/MySQL时&#xff0c;输入的密码会与期望的正确密码比较&#xff0c;由于不正确的处理&#xff0c;会导致即便是memcmp()返回一个非零值&#xff0c;也会使MySQL认为两个密码是相同的。 也就是说只要知道用户名&#xff0c;不断尝试就能够直接登入SQL数据库。按照…

【剑指offer】十九,数组中出现次数超过一半的数字

题目描述 数组中有一个数字出现的次数超过数组长度的一半&#xff0c;请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次&#xff0c;超过数组长度的一半&#xff0c;因此输出2。分析&#xff1a;找数组中出现次数超过一半的数字&…