Java 中文官方教程 2022 版(四十三)(3)https://developer.aliyun.com/article/1488268
获取方法参数的名称
原文:
docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
您可以使用方法java.lang.reflect.Executable.getParameters
获取任何方法或构造函数的形式参数的名称。(类Method
和Constructor
扩展了类Executable
,因此继承了方法Executable.getParameters
。)但是,默认情况下,.class
文件不会存储形式参数名称。这是因为许多生成和消费类文件的工具可能不希望.class
文件包含参数名称的更大的静态和动态占用空间。特别是,这些工具将不得不处理更大的.class
文件,并且 Java 虚拟机(JVM)将使用更多内存。此外,一些参数名称,如secret
或password
,可能会暴露有关安全敏感方法的信息。
要在特定的.class
文件中存储形式参数名称,并使反射 API 能够检索形式参数名称,请使用javac
编译器的-parameters
选项编译源文件。
MethodParameterSpy
示例演示了如何检索给定类的所有构造函数和方法的形式参数的名称。该示例还打印有关每个参数的其他信息。
以下命令打印类ExampleMethods
的构造函数和方法的形式参数名称。注意:记得使用-parameters
编译器选项编译示例ExampleMethods
:
*java MethodParameterSpy ExampleMethods*
此命令打印以下内容:
Number of constructors: 1 Constructor #1 public ExampleMethods() Number of declared constructors: 1 Declared constructor #1 public ExampleMethods() Number of methods: 4 Method #1 public boolean ExampleMethods.simpleMethod(java.lang.String,int) Return type: boolean Generic return type: boolean Parameter class: class java.lang.String Parameter name: stringParam Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Parameter class: int Parameter name: intParam Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method #2 public int ExampleMethods.varArgsMethod(java.lang.String...) Return type: int Generic return type: int Parameter class: class [Ljava.lang.String; Parameter name: manyStrings Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method #3 public boolean ExampleMethods.methodWithList(java.util.List<java.lang.String>) Return type: boolean Generic return type: boolean Parameter class: interface java.util.List Parameter name: listParam Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method #4 public <T> void ExampleMethods.genericMethod(T[],java.util.Collection<T>) Return type: void Generic return type: void Parameter class: class [Ljava.lang.Object; Parameter name: a Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Parameter class: interface java.util.Collection Parameter name: c Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false
MethodParameterSpy
示例使用Parameter
类中的以下方法:
getType
: 返回标识参数声明类型的Class
对象。getName
: 返回参数的名称。如果参数的名称存在,则此方法返回.class
文件提供的名称。否则,此方法将合成一个形式为arg*N*
的名称,其中*N*
是声明参数的方法描述符中的参数索引。
例如,假设您编译了类ExampleMethods
而没有指定-parameters
编译器选项。示例MethodParameterSpy
将为方法ExampleMethods.simpleMethod
打印以下内容:
public boolean ExampleMethods.simpleMethod(java.lang.String,int) Return type: boolean Generic return type: boolean Parameter class: class java.lang.String Parameter name: arg0 Modifiers: 0 Is implicit?: false Is name present?: false Is synthetic?: false Parameter class: int Parameter name: arg1 Modifiers: 0 Is implicit?: false Is name present?: false Is synthetic?: false
getModifiers
:返回一个整数,表示形式参数具有的各种特征。如果适用于形式参数,则此值是以下值的总和:
值(十进制) | 值(十六进制) | 描述 |
16 | 0x0010 | 形式参数声明为final |
4096 | 0x1000 | 形式参数是合成的。或者,您可以调用方法isSynthetic 。 |
32768 | 0x8000 | 参数在源代码中是隐式声明的。或者,您可以调用方法isImplicit |
isImplicit
:如果此参数在源代码中是隐式声明的,则返回true
。有关更多信息,请参阅隐式和合成参数部分。isNamePresent
:如果参数在.class
文件中具有名称,则返回true
。isSynthetic
:如果此参数在源代码中既不是隐式声明也不是显式声明,则返回true
。有关更多信息,请参阅隐式和合成参数部分。
隐式和合成参数
如果源代码中未明确编写某些构造,则某些构造将被隐式声明。例如,ExampleMethods
示例不包含构造函数。它将隐式声明一个默认构造函数。MethodParameterSpy
示例打印有关ExampleMethods
隐式声明构造函数的信息:
Number of declared constructors: 1 public ExampleMethods()
请考虑来自MethodParameterExamples
的以下摘录:
public class MethodParameterExamples { public class InnerClass { } }
类InnerClass
是一个非静态嵌套类或内部类。内部类也会隐式声明一个构造函数。但是,此构造函数将包含一个参数。当 Java 编译器编译InnerClass
时,它会创建一个代表以下代码的.class
文件:
public class MethodParameterExamples { public class InnerClass { final MethodParameterExamples parent; InnerClass(final MethodParameterExamples this$0) { parent = this$0; } } }
InnerClass
构造函数包含一个参数,其类型是包含InnerClass
的类,即MethodParameterExamples
。因此,示例MethodParameterExamples
打印如下内容:
public MethodParameterExamples$InnerClass(MethodParameterExamples) Parameter class: class MethodParameterExamples Parameter name: this$0 Modifiers: 32784 Is implicit?: true Is name present?: true Is synthetic?: false
因为类InnerClass
的构造函数是隐式声明的,所以它的参数也是隐式的。
注意:
- Java 编译器为内部类的构造函数创建一个形式参数,以便编译器能够从创建表达式传递一个引用(表示立即封闭实例)到成员类的构造函数。
- 值 32784 表示
InnerClass
构造函数的参数既是 final(16)又是隐式的(32768)。 - Java 编程语言允许在变量名中使用美元符号(
$
);然而,按照惯例,在变量名中不使用美元符号。
Java 编译器生成的构造如果不对应于源代码中显式或隐式声明的构造,则标记为合成的,除非它们是类初始化方法。合成的构造是编译器生成的在不同实现之间变化的工件。考虑以下摘录来自MethodParameterExamples
:
public class MethodParameterExamples { enum Colors { RED, WHITE; } }
当 Java 编译器遇到enum
构造时,它会创建几个与.class
文件结构兼容且提供enum
构造所期望功能的方法。例如,Java 编译器会为代表以下代码的enum
构造Colors
创建一个.class
文件:
final class Colors extends java.lang.Enum<Colors> { public final static Colors RED = new Colors("RED", 0); public final static Colors BLUE = new Colors("WHITE", 1); private final static values = new Colors[]{ RED, BLUE }; private Colors(String name, int ordinal) { super(name, ordinal); } public static Colors[] values(){ return values; } public static Colors valueOf(String name){ return (Colors)java.lang.Enum.valueOf(Colors.class, name); } }
Java 编译器为这个enum
构造创建了三个构造函数和方法:Colors(String name, int ordinal)
、Colors[] values()
和Colors valueOf(String name)
。方法values
和valueOf
是隐式声明的。因此,它们的形式参数名称也是隐式声明的。
enum
构造函数Colors(String name, int ordinal)
是一个默认构造函数,它是隐式声明的。然而,这个构造函数的形式参数(name
和ordinal
)并没有隐式声明。因为这些形式参数既没有显式声明也没有隐式声明,它们是合成的。(enum
构造函数的默认构造函数的形式参数不是隐式声明的,因为不同的编译器可能对这个构造函数的形式参数形式有不同的规定;另一个 Java 编译器可能为其指定不同的形式参数。当编译器编译使用enum
常量的表达式时,它们仅依赖于enum
构造的公共静态字段,这些字段是隐式声明的,而不依赖于它们的构造函数或这些常量是如何初始化的。)
因此,示例MethodParameterExample
关于enum
构造Colors
打印如下内容:
enum Colors: Number of constructors: 0 Number of declared constructors: 1 Declared constructor #1 private MethodParameterExamples$Colors() Parameter class: class java.lang.String Parameter name: $enum$name Modifiers: 4096 Is implicit?: false Is name present?: true Is synthetic?: true Parameter class: int Parameter name: $enum$ordinal Modifiers: 4096 Is implicit?: false Is name present?: true Is synthetic?: true Number of methods: 2 Method #1 public static MethodParameterExamples$Colors[] MethodParameterExamples$Colors.values() Return type: class [LMethodParameterExamples$Colors; Generic return type: class [LMethodParameterExamples$Colors; Method #2 public static MethodParameterExamples$Colors MethodParameterExamples$Colors.valueOf(java.lang.String) Return type: class MethodParameterExamples$Colors Generic return type: class MethodParameterExamples$Colors Parameter class: class java.lang.String Parameter name: name Modifiers: 32768 Is implicit?: true Is name present?: true Is synthetic?: false
有关隐式声明的构造的更多信息,请参考Java 语言规范,包括在反射 API 中出现为隐式的参数。
检索和解析方法修饰符
原文:
docs.oracle.com/javase/tutorial/reflect/member/methodModifiers.html
方法声明中可能包含的几个修饰符:
- 访问修饰符:
public
、protected
和private
- 限制为一个实例的修饰符:
static
- 禁止值修改的修饰符:
final
- 要求覆盖的修饰符:
abstract
- 防止重入的修饰符:
synchronized
- 表示在另一种编程语言中实现的修饰符:
native
- 强制严格浮点行为的修饰符:
strictfp
- 注解
MethodModifierSpy
示例列出了具有给定名称的方法的修饰符。它还显示方法是否是合成的(编译器生成的)、可变参数的,或者是桥接方法(编译器生成的以支持泛型接口)。
import java.lang.reflect.Method; import java.lang.reflect.Modifier; import static java.lang.System.out; public class MethodModifierSpy { private static int count; private static synchronized void inc() { count++; } private static synchronized int cnt() { return count; } public static void main(String... args) { try { Class<?> c = Class.forName(args[0]); Method[] allMethods = c.getDeclaredMethods(); for (Method m : allMethods) { if (!m.getName().equals(args[1])) { continue; } out.format("%s%n", m.toGenericString()); out.format(" Modifiers: %s%n", Modifier.toString(m.getModifiers())); out.format(" [ synthetic=%-5b var_args=%-5b bridge=%-5b ]%n", m.isSynthetic(), m.isVarArgs(), m.isBridge()); inc(); } out.format("%d matching overload%s found%n", cnt(), (cnt() == 1 ? "" : "s")); // production code should handle this exception more gracefully } catch (ClassNotFoundException x) { x.printStackTrace(); } } }
MethodModifierSpy
产生的输出示例如下。
$ *java MethodModifierSpy java.lang.Object wait* public final void java.lang.Object.wait() throws java.lang.InterruptedException Modifiers: public final [ synthetic=false var_args=false bridge=false ] public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException Modifiers: public final [ synthetic=false var_args=false bridge=false ] public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException Modifiers: public final native [ synthetic=false var_args=false bridge=false ] 3 matching overloads found
$ *java MethodModifierSpy java.lang.StrictMath toRadians* public static double java.lang.StrictMath.toRadians(double) Modifiers: public static strictfp [ synthetic=false var_args=false bridge=false ] 1 matching overload found
$ *java MethodModifierSpy MethodModifierSpy inc* private synchronized void MethodModifierSpy.inc() Modifiers: private synchronized [ synthetic=false var_args=false bridge=false ] 1 matching overload found
$ *java MethodModifierSpy java.lang.Class getConstructor* public java.lang.reflect.Constructor<T> java.lang.Class.getConstructor (java.lang.Class<T>[]) throws java.lang.NoSuchMethodException, java.lang.SecurityException Modifiers: public transient [ synthetic=false var_args=true bridge=false ] 1 matching overload found
$ *java MethodModifierSpy java.lang.String compareTo* public int java.lang.String.compareTo(java.lang.String) Modifiers: public [ synthetic=false var_args=false bridge=false ] public int java.lang.String.compareTo(java.lang.Object) Modifiers: public volatile [ synthetic=true var_args=false bridge=true ] 2 matching overloads found
请注意,Method.isVarArgs()
对于Class.getConstructor()
返回true
。这表明方法声明如下:
public Constructor<T> getConstructor(Class<?>... parameterTypes)
不要这样:
public Constructor<T> getConstructor(Class<?> [] parameterTypes)
请注意,String.compareTo()
的输出包含两种方法。在String.java
中声明的方法:
public int compareTo(String anotherString);
和第二个合成或编译器生成的桥接方法。这是因为String
实现了参数化接口Comparable
。在类型擦除期间,继承方法Comparable.compareTo()
的参数类型从java.lang.Object
更改为java.lang.String
。由于Comparable
和String
中的compareTo
方法的参数类型在擦除后不再匹配,因此无法进行覆盖。在所有其他情况下,这将产生编译时错误,因为接口未实现。桥接方法的添加避免了这个问题。
Method
实现了java.lang.reflect.AnnotatedElement
。因此,任何具有java.lang.annotation.RetentionPolicy.RUNTIME
的运行时注解都可以被检索。有关获取注解的示例,请参见检查类修饰符和类型部分。