热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何有效地使用反射

本文是我们名为“高级Java”的学院课程的一部分。本课程旨在帮助您最有效地使用Java。它讨论了高级主题,包括对象创建,并发,序列化&#x

本文是我们名为“ 高级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 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 strings;public List 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



推荐阅读
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • PHP反射API的功能和用途详解
    本文详细介绍了PHP反射API的功能和用途,包括动态获取信息和调用对象方法的功能,以及自动加载插件、生成文档、扩充PHP语言等用途。通过反射API,可以获取类的元数据,创建类的实例,调用方法,传递参数,动态调用类的静态方法等。PHP反射API是一种内建的OOP技术扩展,通过使用Reflection、ReflectionClass和ReflectionMethod等类,可以帮助我们分析其他类、接口、方法、属性和扩展。 ... [详细]
  • 本文比较了eBPF和WebAssembly作为云原生VM的特点和应用领域。eBPF作为运行在Linux内核中的轻量级代码执行沙箱,适用于网络或安全相关的任务;而WebAssembly作为图灵完备的语言,在商业应用中具有优势。同时,介绍了WebAssembly在Linux内核中运行的尝试以及基于LLVM的云原生WebAssembly编译器WasmEdge Runtime的案例,展示了WebAssembly作为原生应用程序的潜力。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
author-avatar
kafka
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有