JDK 8中一个很少被提及的特性就是它可以方法参数的元信息存储到编译完的class文件中(JEP 118)。这个特性使得Java程序可以在运行时通过反射来获取参数的元信息。
在Java教程——
反射API中有节课叫
获取方法参数的名字,里面讲到了如何使用Java 8的这个新特性。这个教程提供了一个MethodParameterSpy类,你可以用它来检测某个Java类的方法参数的属性。文中同时提及,将额外的参数信息存储在.class文件里面是一个可选的特性,因为这会增加class文件的大小。它同时还指出,有些情况下参数名内包含一些敏感信息,因此开发人员并不希望将它保存到编译后的.class文件中。
在Java 8中,使用javac编译器的时候加上-parameters参数的话,会在生成的.class文件中额外存储参数的元信息。当你输入javac -help的时候,你会看到-parameters这个选项,就像下面的截图中那样。
Oracle在javac的官方文档中,讲到了如何在运行时获取这个额外的方法参数元信息:"将方法及构造方法的参数存储在生成的.class文件中,这样可以通过反射API java.lang.reflect.Executable.getParameters来获取到这些信息"。下面的代码片段(ParameterDisplayer类)将会展示这个功能。
package dustin.examples.jdk8;
import static java.lang.System.out;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
/**
* Uses JDK 8 Parameter class to demonstrate metadata related to the parameters
* of the methods and constructors of the provided class (includes private,
* protected, and public methods, but does not include methods inherited from
* parent classes; those classes should be individually submitted).
*
* @author Dustin
*/
public class ParameterDisplayer
{
private static void displayParametersMetadata(final String[] classesNames)
{
for (final String className : classesNames)
{
try
{
final Class clazz = Class.forName(className);
// Get all class's declared methods (does not get inherited methods)
final Method[] declaredMethods = clazz.getDeclaredMethods();
for (final Method method : declaredMethods)
{
writeHeader(
"Method " + method.toGenericString()
+ " has " + method.getParameterCount() + " Parameters:");
int parameterCount = 0;
final Parameter[] parameters = method.getParameters();
for (final Parameter parameter : parameters)
{
out.println(
"\targ" + parameterCount++ + ": "
+ (parameter.isNamePresent() ? parameter.getName() : "Parameter Name not provided,")
+ (isParameterFinal(parameter) ? " IS " : " is NOT ")
+ "final, type " + parameter.getType().getCanonicalName()
+ ", and parameterized type of " + parameter.getParameterizedType()
+ " and " + (parameter.isVarArgs() ? "IS " : "is NOT ")
+ "variable." );
}
}
}
catch (ClassNotFoundException cnfEx)
{
out.println("Unable to find class " + className);
}
}
}
private static void writeHeader(final String headerText)
{
out.println("\n==========================================================");
out.println("= " + headerText);
out.println("==========================================================");
}
/**
* Indicate whether provided Parameter is final.
*
* @param parameter Parameter to be tested for 'final' modifier.
* @return {@code true} if provided Parameter is 'final'.
*/
private static boolean isParameterFinal(final Parameter parameter)
{
return Modifier.isFinal(parameter.getModifiers());
}
public static void main(final String[] arguments)
{
if (arguments.length < 1)
{
out.println("You must provide the fully qualified name of at least one class.");
System.exit(-1);
}
displayParametersMetadata(arguments);
}
}
一开始我想用这个程序来测试下JDK里的某个大家比较熟悉的类,不过后来想了下可能没用,因为JDK的类编译的时候应该是没加-parameters选项。因此为了演示的需要,我自己又简单地写了一个类。
package dustin.examples.jdk8;
import java.util.List;
/**
* Class with numerous methods intended to be used in demonstrating JDK 8's new
* Parameter class.
*
* @author Dustin
*/
public class ManyMethods
{
public ManyMethods() {}
private void addArrayOfStrings(String[] strings) {}
private void addManyStrings(final String ... strings) {}
private void addListOfStrings(final List<String> strings) {}
@Override
public String toString()
{
return "ManyMethods";
}
}
下面两个截图演示的是没加-parameters选项和加了这个选项的时候,用ParameterDisplayer来运行ManyMethods类的结果。最明显的区别就是,没加-parameters选项的时候,参数名是无法获取到的。还有就是如果没加这个选项,参数是否是final类型的是不确定的。如果没加-parameters选项的话,不管参数是不是final类型的,Parameter.getModifiers()返回的结果都是不包含final的。
ParameterDisplayer类使用了Parameter.isNamePresent()来自动检测参数名是否存在(是否用-parameters选项编译了)。不这么做的话,如果参数名不存在,Parameter.getName()返回的是"arg"加上参数的序号(第一个参数的话是arg0,第二个arg1,依次类推)。
ManyMethods类里有两个方法都包含一个final类型的参数。只有当编译的时候加上-parameters选项,才能通过Parameter.getModifiers()方法来正确的识别出是否是final类型。
一点题外话:Sun/Oracle的工具文档中通常包含一个"windows"页及"solaris"页,后者通常用来说明如何在Linux/Unix的各个平台上运行某个工具。我注意到在Java 8的文档中这个已经变了。文档仍然有一个windows版本,但Unix/Linux版本的URL现在标识的是"unix"了。为了说明这点,下面列举了Java SE7和Java SE8里javac工具的文档页的URL:
http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javac.html
http://docs.oracle.com/javase/8/docs/technotes/tools/unix/javac.html
http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html
http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javac.html
再回到Parameter类的话题上,值得注意的是存储额外的参数元信息的.class文件的大小会增加。就上面这个ManyMethods类而言,.class文件从909字节增加到了961字节。
Constructor,和Method一样,都继承了Executable接口,因此Constructor类和Methods类一样,也有一个getParamaters方法。在Java 8里,如果代码编译的时候增加了这个额外的信息的话,你能获取到更详细的关于方法参数的信息。
原创文章转载请注明出处:
http://it.deepinmind.com
英文原文链接
分享到:
相关推荐
这些代码可能包括创建枚举类,然后通过反射获取枚举值、实例名以及演示如何使用`getDeclaredConstructor()`和`newInstance()`方法来访问私有构造函数(虽然这不是常规用法)。 通过深入理解这些概念,你可以更好地...
在本篇文章中,我们将深入探讨如何通过反射获取构造方法并实例化对象,这对于理解和使用Java的动态性至关重要。 首先,我们需要了解`java.lang.Class`类,它是Java中所有类的通用表示。当我们知道一个类的名字...
以上就是Java反射获取参数的基本步骤和Java 8的相关特性。通过反射,开发者可以在运行时动态地操控类,实现代码的灵活性,但需要注意的是,反射操作可能会带来安全风险,如破坏封装性,增加程序的复杂性,因此在使用...
Java反射机制允许我们在程序运行期间动态地获取类、接口、字段和方法的信息,并能对这些信息进行操作。例如,我们可以获取一个对象的类类型,实例化未知类型的对象,访问和修改私有属性,以及调用任意方法。反射机制...
本教程将深入探讨如何通过反射获取类、方法上的注解以及注解中的值和方法参数。 1. **注解的定义与使用** 注解以`@`符号开头,后面跟着注解的类型。例如,`@Override`表示方法重写,`@Deprecated`表示某个功能已...
尽管Java反射不直接支持泛型信息,但可以通过方法签名或字段类型来间接获取。例如,`List<?>`的`Class`类型是`java.util.List`,这意味着你不能直接获取泛型的实际类型参数。然而,你可以通过类型检查和转换来操作...
Java反射是Java编程语言中的一个重要特性,它允许程序在运行时动态地获取类的信息并操作类的对象。在Java中,反射机制提供了强大的能力,包括在运行时检查类的结构、创建对象实例、调用方法以及访问和修改字段值。...
在标题和描述中提到的知识点主要集中在如何通过反射获取类的所有属性和get方法,包括来自子类和父类的。下面将详细介绍这些内容。 1. **获取所有属性**: 在Java中,`java.lang.Class` 类提供了获取类属性的方法。...
Java反射机制是Java编程语言中一个强大的特性,它允许运行时动态地获取类的信息并进行操作,如创建对象、调用方法、访问属性以及处理数组等。在本篇讲解中,我们将深入理解如何通过反射来执行方法、操作属性以及处理...
以下是如何使用Java反射来调用私有方法并获取私有属性值的详细步骤: 1. **获取Class对象**:首先,我们需要获取到目标类的Class对象,这可以通过`Class.forName()`方法或者对象的`getClass()`方法来实现。例如,...
在本文中,我们将深入探讨如何使用Java反射来获取并执行某个特定的方法。 首先,我们需要了解Java反射的基本概念。`java.lang.Class`类是反射的核心,它代表了Java中的每一个类。我们可以通过以下方式获取到一个...
Java8通过Function获取字段名的步骤 Java8通过Function获取字段名是指在Java8中使用Function函数式编程来获取Java对象的字段名。这个步骤主要解决了硬编码的问题,效果类似于MyBatis-Plus的LambdaQueryWrapper。 ...
以下将详细介绍Java反射的8个关键实例。 1. 获取Class对象 要使用反射,首先需要获取到目标类的Class对象。这可以通过以下几种方式实现: - 使用`Class.forName()`方法,如`Class<?> clazz = Class.forName("全...
以下是对Java反射机制的详细解释: 1. 获取类信息: 要使用反射,首先需要获取类的`java.lang.Class`对象。这可以通过以下几种方式实现: - `Class<?> c = Class.forName("全限定类名");`:根据类的全限定名(包...
这篇博客文章“JAVA反射参数传递概略”将深入探讨如何使用反射来处理方法参数,尤其是涉及到参数传递的细节。 首先,让我们理解什么是参数传递。在Java中,有两种基本的参数传递方式:值传递和引用传递。值传递是将...
在Java中,我们可以通过`Class`对象的`getMethod`方法获取到指定的公共方法,然后通过`invoke`方法调用它。例如: ```java Method method = MyClass.class.getMethod("myMethodWithoutArgs"); method.invoke...
Java反射是Java编程语言中的一个重要特性,它允许运行时的Java程序访问并操作类、接口、字段和方法等对象的内部信息,即使这些信息在编译时并不知道。这一机制使得Java具有了高度的动态性,能够实现元编程,即在程序...
以下是一个简单的示例,展示了如何使用反射获取类的方法: ```java import java.lang.reflect.*; public class ReflectionExample { public static void main(String[] args) { try { Class<?> clazz = Class....
虽然Java反射不能直接获取到泛型的实际类型,但是可以通过方法签名或者字段类型获取到擦除后的边界类型。 6. **接口与实现类**: 反射也可以用来检查一个类是否实现了特定的接口,通过`Class.getInterfaces()`...
### Java反射机制总结 ...总之,Java反射机制是Java语言的一个重要特性,它提供了在运行时动态获取类信息和操作类结构的能力。合理使用反射可以提高程序的灵活性和扩展性,但在使用时也需要考虑到其潜在的问题和限制。