您的位置:首页 > 编程语言 > Java开发

深入理解 Java 反射:Method (成员方法)

2017-12-21 22:48 459 查看
Method 介绍

继承的方法(包括重载、重写和隐藏的)会被编译器强制执行,这些方法都无法反射。

因此,反射一个类的方法时不考虑父类的方法,只考虑当前类的方法。

每个方法都由 修饰符、返回值、参数、注解和抛出的异常组成。

java.lang.reflect.Method 方法为我们提供了获取上述部分的 API。

获取方法的信息

下面的代码演示了如何获得一个方法的 修饰符、返回值、参数、注解和抛出的异常 等信息:

public class MethodTypeSpy extends BaseTestClass {

private static final String fmt = “%24s: %s\n”;

private static final String HELLO_WORLD = “I’m cute shixin”;

@Deprecated
public static void main(String[] args) throws ClassNotFoundException {
MethodTypeSpy methodTypeSpy = new MethodTypeSpy();
Class<? extends MethodTypeSpy> cls = methodTypeSpy.getClass();
printFormat("Class:%s \n", cls.getCanonicalName());
Method[] declaredMethods = cls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
printFormat(fmt, "Method name", declaredMethod.getName());  //获得单独的方法名
//获得完整的方法信息(包括修饰符、返回值、路径、名称、参数、抛出值)
printFormat(fmt, "toGenericString", declaredMethod.toGenericString());

int modifiers = declaredMethod.getModifiers();      //获得修饰符
printFormat(fmt, "Modifiers", Modifier.toString(modifiers));

System.out.format(fmt, "ReturnType", declaredMethod.getReturnType());   //获得返回值
System.out.format(fmt, "getGenericReturnType", declaredMethod.getGenericReturnType());//获得完整信息的返回值

Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); //获得参数类型
Type[] genericParameterTypes = declaredMethod.getGenericParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
System.out.format(fmt, "ParameterType", parameterTypes[i]);
System.out.format(fmt, "GenericParameterType", genericParameterTypes[i]);
}

Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();     //获得异常名称
Type[] genericExceptionTypes = declaredMethod.getGenericExceptionTypes();
for (int i = 0; i < exceptionTypes.length; i++) {
System.out.format(fmt, "ExceptionTypes", exceptionTypes[i]);
System.out.format(fmt, "GenericExceptionTypes", genericExceptionTypes[i]);
}

Annotation[] annotations = declaredMethod.getAnnotations(); //获得注解
for (Annotation annotation : annotations) {
System.out.format(fmt, "Annotation", annotation);
System.out.format(fmt, "AnnotationType", annotation.annotationType());
}
}
}


}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43


查看当前类 MethodTypeSpy的方法 main() 的信息,运行结果:

Class:net.sxkeji.shixinandroiddemo2.test.reflection.MethodTypeSpy

Method name: main

toGenericString: public static void net.sxkeji.shixinandroiddemo2.test.reflection.MethodTypeSpy.main(java.lang.String[]) throws java.lang.ClassNotFoundException

Modifiers: public static

ReturnType: void

getGenericReturnType: void

ParameterType: class [Ljava.lang.String;

GenericParameterType: class [Ljava.lang.String;

ExceptionTypes: class java.lang.ClassNotFoundException

GenericExceptionTypes: class java.lang.ClassNotFoundException

Annotation: @java.lang.Deprecated()

AnnotationType: interface java.lang.Deprecated

Process finished with exit code 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14


获取方法的参数名称

从 JDK 1.8 开始,java.lang.reflect.Executable.getParameters 为我们提供了获取普通方法或者构造方法的名称的能力。

在 JDK 中 java.lang.reflect.Method 和 java.lang.reflect.Constructor 都继承自 Executable,因此它俩也有同样的能力。

然而在 Android SDK 中 Method, Constructor 继承自 AbstractMethod,无法获得方法的参数名:

public final class Method extends AbstractMethod implements GenericDeclaration, Member

1


你可以在 J2EE 环境下练习官方的 获取参数名称代码:

/*

* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.

*

* Redistribution and use in source and binary forms, with or without

* modification, are permitted provided that the following conditions

* are met:

*

* - Redistributions of source code must retain the above copyright

* notice, this list of conditions and the following disclaimer.

*

* - Redistributions in binary form must reproduce the above copyright

* notice, this list of conditions and the following disclaimer in the

* documentation and/or other materials provided with the distribution.

*

* - Neither the name of Oracle or the names of its

* contributors may be used to endorse or promote products derived

* from this software without specific prior written permission.

*

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS

* IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,

* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR

* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR

* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,

* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,

* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR

* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF

* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING

* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

import java.lang.reflect.*;

import java.util.function.*;

import static java.lang.System.out;

public class MethodParameterSpy {

private static final String  fmt = "%24s: %s%n";

// for the morbidly curious
<E extends RuntimeException> void genericThrow() throws E {}

public static void printClassConstructors(Class c) {
Constructor[] allConstructors = c.getConstructors();
out.format(fmt, "Number of constructors", allConstructors.length);
for (Constructor currentConstructor : allConstructors) {
printConstructor(currentConstructor);
}
Constructor[] allDeclConst = c.getDeclaredConstructors();
out.format(fmt, "Number of declared constructors",
allDeclConst.length);
for (Constructor currentDeclConst : allDeclConst) {
printConstructor(currentDeclConst);
}
}

public static void printClassMethods(Class c) {
Method[] allMethods = c.getDeclaredMethods();
out.format(fmt, "Number of methods", allMethods.length);
for (Method m : allMethods) {
printMethod(m);
}
}

public static void printConstructor(Constructor c) {
out.format("%s%n", c.toGenericString());
Parameter[] params = c.getParameters();
out.format(fmt, "Number of parameters", params.length);
for (int i = 0; i < params.length; i++) {
printParameter(params[i]);
}
}

public static void printMethod(Method m) {
out.format("%s%n", m.toGenericString());
out.format(fmt, "Return type", m.getReturnType());
out.format(fmt, "Generic return type", m.getGenericReturnType());

Parameter[] params = m.getParameters();
for (int i = 0; i < params.length; i++) {
printParameter(params[i]);
}
}

public static void printParameter(Parameter p) {
out.format(fmt, "Parameter class", p.getType());
out.format(fmt, "Parameter name", p.getName());
out.format(fmt, "Modifiers", p.getModifiers());
out.format(fmt, "Is implicit?", p.isImplicit());
out.format(fmt, "Is name present?", p.isNamePresent());
out.format(fmt, "Is synthetic?", p.isSynthetic());
}

public static void main(String... args) {

try {
printClassConstructors(Class.forName(args[0]));
printClassMethods(Class.forName(args[0]));
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}


}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
4344
45
46
47
48
49
50
5152
53
54
55
56
57
58
59
60
6162
63
64
65
66
67
68
69
70
7172
73
74
75
76
77
78
79
80
8182
83
84
85
86
87
88
89
90
9192
93
94
95
96
97
98
99
100
101102
103


获取方法的修饰符

方法可以被以下修饰符修饰:

访问权限控制符:public, protected, private
限制只能有一个实例的:static
不允许修改的:final
抽象,要求子类重写:abstract
预防重入的同步锁:synchronized
用其他语言实现的方法:native
严格的浮点型强度:strictfp
注解


类似获取 Class 的修饰符,我们可以使用 “Method.getModifiers()方法获取当前成员变量的修饰符。

返回java.lang.reflect.Modifier“` 中定义的整形值。

举个例子:

public class MethodModifierSpy extends BaseTestClass {

private final static String CLASS_NAME = "java.lang.String";

public static void main(String[] args) {
MethodModifierSpy methodModifierSpy = new MethodModifierSpy();
Class<? extends MethodModifierSpy> cls = methodModifierSpy.getClass();
printFormat("Class: %s \n\n", cls.getCanonicalName());

Method[] declaredMethods = cls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
printFormat("\n\nMethod name: %s \n", declaredMethod.getName());
printFormat("Method toGenericString: %s \n", declaredMethod.toGenericString());

int modifiers = declaredMethod.getModifiers();
printFormat("Method Modifiers: %s\n", Modifier.toString(modifiers));

System.out.format("synthetic= %-5b,  var_args= %-5b,  bridge= %-5b \n"
, declaredMethod.isSynthetic(), declaredMethod.isVarArgs(), declaredMethod.isBridge());
}

}

public final void varArgsMethod(String... strings) {

}


}

1
2
3
4
5
6
7
8
9
10
11
12
13
1415
16
17
18
19
20
2122
23
24
25
26
27
28


运行结果:

Class: net.sxkeji.shixinandroiddemo2.test.reflection.MethodModifierSpy

Method name: main

Method toGenericString: public static void net.sxkeji.shixinandroiddemo2.test.reflection.MethodModifierSpy.main(java.lang.String[])

Method Modifiers: public static

synthetic= false, var_args= false, bridge= false

Method name: varArgsMethod

Method toGenericString: public final void net.sxkeji.shixinandroiddemo2.test.reflection.MethodModifierSpy.varArgsMethod(java.lang.String…)

Method Modifiers: public final transient

synthetic= false, var_args= true , bridge= false

Process finished with exit code 0

1
2
3
4
5
6
7
8
9
10
11
12
13
1415
16


注意:上面的最后一行可以看到,方法有三种类型:synthetic, varagrs, bridge。

下面介绍这三种方法类型:

synthetic method:合成方法

这个知识点主要学习自:http://www.oschina.net/code/snippet_2438265_54869


什么是合成方法呢?

首先需要理解一个概念:

对于 Java 编译器而言,内部类也会被单独编译成一个class文件。
那么原有代码中的相关属性可见性就难以维持,synthetic method也就是为了这个目的而生成的。生成的synthetic方法是包访问性的static方法.


还是有些抽象,举个例子:

public class Foo {

private Object baz = “Hello”;

private int get(){

return 1;

}

private class Bar {

private Bar() {

System.out.println(get());

}

}

}

12
3
4
5
6
7
8
9
10
11


上面的代码中,Bar 访问了 Foo 的 private 方法 get()。

使用 javap -private Foo看一下:

public class Foo {

private java.lang.Object baz;

public Foo();

private int get();

static int access$000(Foo); //多出来的 synthetic 方法,为了在 Bar 中的这段代码 System.out.println(get());

}

12
3
4
5
6


因此可以这么理解:

Synthetic (合成)方法是由编译器产生的、源代码中没有的方法。
当内部类与外部类之前有互相访问 private 属性、方法时,编译器会在运行时为调用方创建一个 synthetic 方法。


合成方法主要创建于嵌套内部类中。

我们可以使用 Method.isSynthetic() 方法判断某个方法是否为 synthetic 。

varargs ( variable arguments) method:Java 可变参数方法

public void testVarargs(String… strings){

//…

}

12
3

创建时必须放在方法尾部,即一个方法只能有一个可变数组参数
调用时可以传入一个数组:
testVarargs(new String[]{"shixin","zhang"});
也可以分别传入多个参数:
testVarargs("shixin","zhang");


推荐使用后者。

我们可以使用 Method.isVarArgs() 方法判断某个方法包含可变参数 。

bridge method:桥接方法

这个知识点主要学习自:http://blog.csdn.net/mhmyqn/article/details/47342577


桥接方法是为了泛型的向前兼容提出的,不太熟悉泛型的同学可以查看《

Java 进阶巩固:深入理解 泛型》。

我们知道,为了兼容 JDK 1.5 以前的代码,泛型会在编译时被去除(泛型擦除),这时需要创建桥接方法。

举个例子:

/**

* @author Mikan

* @date 2015-08-05 16:22

*/

public interface SuperClass {

T method(T param);


}

package com.mikan;

/**

* @author Mikan

* @date 2015-08-05 17:05

*/

public class SubClass implements SuperClass {

public String method(String param) {

return param;

}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
1415
16
17
18
19
20
21


上面的代码创建了一个泛型接口和实现类。

实现类在运行时的字节码如下:

localhost:mikan mikan$ javap -c SubClass.class

Compiled from “SubClass.java”

public class com.mikan.SubClass implements com.mikan.SuperClass
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java