上面一小节讲解了Field字段的用法,包括如何获取一个字段的信息,字段的修饰符,字段的类型,以及如何访问一个字段和给字段进行设值。
这一小节将讲解method类的使用,将从下面几个方法来介绍使用:
获取方法的类型信息
(返回方法的名称,返回值类型,参数类型,异常类型,注解信息)获取方法参数的信息
解析和检索方法修饰符
方法的调用
方法的常用的错误
1.获取方法的类型信息
接下来的MethodSpy
例子阐述了在一个给定的类中获取方法的名称,返回类型,参数类型以及异常类型
/**
* 扫描类中方法的信息 (获取方法的名称,返回值,参数,异常信息)
*/
public class MethodSpy {
private static final String fmt = "%24s: %s%n";
public static void main(String... args) {
args = new String[]{Class.class.getName(), "getConstructor"};
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.getName());
//对比方法toGenericString 和toString 的信息,发现toGenericString有泛型信息输出
out.format("%s%n", m.toGenericString());
out.format("%s%n", m.toString());
//得到方法的返回类型的信息
out.format(fmt, "ReturnType", m.getReturnType());
out.format(fmt, "GenericReturnType", m.getGenericReturnType());
//得到参数类型的信息
Class<?>[] pType = m.getParameterTypes();
Type[] gpType = m.getGenericParameterTypes();
for (int i = 0; i < pType.length; i++) {
out.format(fmt, "ParameterType", pType[i]);
out.format(fmt, "GenericParameterType", gpType[i]);
}
//得到异常的信息
Class<?>[] xType = m.getExceptionTypes();
Type[] gxType = m.getGenericExceptionTypes();
for (int i = 0; i < xType.length; i++) {
out.format(fmt, "ExceptionType", xType[i]);
out.format(fmt, "GenericExceptionType", gxType[i]);
}
}
// production code should handle these exceptions more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
输出结果如下:
getConstructor
public java.lang.reflect.Constructor<T> java.lang.Class.getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException,java.lang.SecurityException
public java.lang.reflect.Constructor java.lang.Class.getConstructor(java.lang.Class[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException
ReturnType: class java.lang.reflect.Constructor
GenericReturnType: java.lang.reflect.Constructor<T>
ParameterType: class [Ljava.lang.Class;
GenericParameterType: java.lang.Class<?>[]
ExceptionType: class java.lang.NoSuchMethodException
GenericExceptionType: class java.lang.NoSuchMethodException
ExceptionType: class java.lang.SecurityException
GenericExceptionType: class java.lang.SecurityException
2.获取方法参数的信息
下面的例子说明了如何获取参数信息:
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.List;
import static java.lang.System.out;
/**
* @Project: jdk
* @description: 输出方法的参数信息
* @author: sunkang
* @create: 2018-10-05 21:32
* @ModificationHistory who when What
**/
public class MethodParameterTest {
public boolean simpleMethod( final String stringParam, int intParam) {
System.out.println("String: " + stringParam + ", integer: " + intParam);
return true;
}
public int varArgsMethod(String... manyStrings) {
return manyStrings.length;
}
public boolean methodWithList(List<String> listParam) {
return listParam.isEmpty();
}
public <T> void genericMethod(T[] a, Collection<T> c) {
System.out.println("Length of array: " + a.length);
System.out.println("Size of collection: " + c.size());
}
private static final String fmt = "%24s: %s%n";
public static void printParameter(Parameter p) {
//p.getType(); 返回参数类型
out.format(fmt, "Parameter class", p.getType());
// p.getName(); 返回参数名称,都是编译后的参数名称,名称的格式为argN,N为参数的序号
out.format(fmt, "Parameter name", p.getName());
//p.getModifiers() 得到参数的修饰符,
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());
//参数是否是可变参数 varArgsMethod该方法中参数是可变参数
out.format(fmt, "Is varArgs?", p.isVarArgs());
}
public static void main(String[] args) {
//得到MethodParameterTest的所有的方法
Method[] methods = MethodParameterTest.class.getDeclaredMethods();
for(Method method :methods){
out.println("--------------------------");
out.format("%s%n", method.toGenericString());
out.format(fmt, "Return type", method.getReturnType());
out.format(fmt, "Generic return type", method.getGenericReturnType());
Parameter[] parameters = method.getParameters();
for(Parameter parameter : parameters){
printParameter(parameter);
}
}
}
}
输出结果为: 发现参数的名称为arg0,arg1 这种格式,参数的修饰符的modify的值一般来说为0,这里测试的跟反射的官方文档的说明有点不一致
。具体的说明见https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html#implcit_and_synthetic
--------------------------
public static void com.java.reflect.merbers.methods.MethodParameterTest.main(java.lang.String[])
Return type: void
Generic return type: void
Parameter class: class [Ljava.lang.String;
Parameter name: arg0
Modifiers: 0
Is implicit?: false
Is name present?: false
Is synthetic?: false
Is varArgs?: false
--------------------------
public boolean com.java.reflect.merbers.methods.MethodParameterTest.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
Is varArgs?: false
Parameter class: int
Parameter name: arg1
Modifiers: 0
Is implicit?: false
Is name present?: false
Is synthetic?: false
Is varArgs?: false
--------------------------
public <T> void com.java.reflect.merbers.methods.MethodParameterTest.genericMethod(T[],java.util.Collection<T>)
Return type: void
Generic return type: void
Parameter class: class [Ljava.lang.Object;
Parameter name: arg0
Modifiers: 0
Is implicit?: false
Is name present?: false
Is synthetic?: false
Is varArgs?: false
Parameter class: interface java.util.Collection
Parameter name: arg1
Modifiers: 0
Is implicit?: false
Is name present?: false
Is synthetic?: false
Is varArgs?: false
--------------------------
public static void com.java.reflect.merbers.methods.MethodParameterTest.printParameter(java.lang.reflect.Parameter)
Return type: void
Generic return type: void
Parameter class: class java.lang.reflect.Parameter
Parameter name: arg0
Modifiers: 0
Is implicit?: false
Is name present?: false
Is synthetic?: false
Is varArgs?: false
--------------------------
public int com.java.reflect.merbers.methods.MethodParameterTest.varArgsMethod(java.lang.String...)
Return type: int
Generic return type: int
Parameter class: class [Ljava.lang.String;
Parameter name: arg0
Modifiers: 0
Is implicit?: false
Is name present?: false
Is synthetic?: false
Is varArgs?: true
--------------------------
public boolean com.java.reflect.merbers.methods.MethodParameterTest.methodWithList(java.util.List<java.lang.String>)
Return type: boolean
Generic return type: boolean
Parameter class: interface java.util.List
Parameter name: arg0
Modifiers: 0
Is implicit?: false
Is name present?: false
Is synthetic?: false
Is varArgs?: false
3.解析和检索方法修饰符
下面的这个例子,展示了如何获取方法的修饰符,以及判断方法是否是合成的,判断方法是否是一种桥接的方法,判断方法是否包含可变参数
。
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) {
// args= new String[]{Object.class.getName(),"wait"};
// args= new String[]{MethodModifierSpy.class.getName(),"main"};
args= new String[]{String.class.getName(),"compareTo"};
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()));
//m.isVarArgs() 返回方法是否是包含可变参数
//m.isBridge() 如果为true ,说明是一种桥接的方法,避免泛型擦除后,泛型信息不再,避免发生编译错误,所以在增加了一个桥接的方法
/**
* 原有的接口信息为: public interface Comparable<T> 中的方法public int compareTo(T o);,泛型为T,泛型擦除之后,
* 就会变成public int compareTo(java.lang.Ojbect o)
*
* 比如String 实现了Comparable<String>该接口,必须要实现泛型擦除后的public int compareTo(java.lang.Ojbect o)该接口,编译才不会报错
* public int java.lang.String.compareTo(java.lang.Object)
* Modifiers: public volatile
* [ synthetic=true var_args=false bridge=true ]
*/
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();
}
}
}
输出结果为: 可以发现多了个public int java.lang.String.compareTo(java.lang.Object)的方法
,该方法是一个桥接的方法,为了避免泛型擦除之后编译出错而自动增加的一个方法
。
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
3.方法的调用
下面的例子,展示了普通方法,参数为数组或者可变参数的方法还有静态方法的调用案例
/**
* @Project: jdk
* @description: 方法的调用测试
* @author: sunkang
* @create: 2018-09-28 23:39
* @ModificationHistory who when What
**/
public class MethodInvokeTest {
private void test(String name,int age){
System.out.println("name:"+name+"age:"+age);
}
private static void staticTest(String name,int age){
System.out.println("name:"+name+",age:"+age);
}
private void test(String ... args){
System.out.println("args:"+args);
}
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
MethodInvokeTest test =new MethodInvokeTest();
//1.普通方法的调用
Method method = MethodInvokeTest.class.getDeclaredMethod("test",String.class,int.class);
method.invoke(test,"sunkang",22);
//2.参数为可变参数的或者数组的情况的调用
Method vargMethod= MethodInvokeTest.class.getDeclaredMethod("test",String[].class);
//这里必须强转为一个Object对象才可以
vargMethod.invoke(test, (Object)new String[]{"sun","kang","Mr"});
//3.静态方法的反射调用,此时调用的对象一般传入null就行,传入test对象也是可以的,但是没有意义
Method staticMethod= MethodInvokeTest.class.getDeclaredMethod("staticTest",String.class,int.class);
staticMethod.invoke(null, "sunkang",22);
}
}
输出结果为:
name:sunkangage:22
args:[Ljava.lang.String;@4554617c
name:sunkang,age:22
4.方法的常用的错误
1.由于泛型参数导致方法找不到
/**
* 由于泛型会被擦除,编译器会用其上限类型替换泛型类型,<T> 的T上限就是 Object, <T extends Integer>那么T就是上限就是Integer
* @param <T>
*/
public class MethodTrouble<T> {
//编译之后就是 public void com.java.reflect.merbers.methods.MethodTrouble.lookup(java.lang.Object)
// T extends Integer 那么就是上限就是Integer
public void lookup(T t) {}
public void find(Integer i) {}
public static void main(String... args) {
args = new String[]{"lookup","java.lang.Integer"};
//解决方案可以
// args = new String[]{"lookup","java.lang.Object"};
try {
String mName = args[0];
Class cArg = Class.forName(args[1]);
Class<?> c = (new MethodTrouble<Integer>()).getClass();
Method m = c.getMethod(mName, cArg);
System.out.format("Found:%n %s%n", m.toGenericString());
} catch (NoSuchMethodException x) {
x.printStackTrace();
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
结果为: 解决方案已经在代码中写了
java.lang.NoSuchMethodException: com.java.reflect.merbers.methods.MethodTrouble.lookup(java.lang.Integer)
at java.lang.Class.getMethod(Class.java:1786)
at com.java.reflect.merbers.methods.MethodTrouble.main(MethodTrouble.java:24)
2.方法的访问权限不够导致错误
/**
* 方法访问权限不够导致调用错误 需要设置m.setAccessible(true);可以绕过访问的检查
*/
public class MethodTroubleAgain {
public static void main(String... args) {
AnotherClass ac = new AnotherClass();
try {
Class<?> c = ac.getClass();
Method m = c.getDeclaredMethod("m");
//解决方案如下,设置 m.setAccessible(true)为true
//m.setAccessible(true); // solution
Object o = m.invoke(ac); // IllegalAccessException
} catch (NoSuchMethodException x) {
x.printStackTrace();
} catch (InvocationTargetException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
}
}
}
class AnotherClass {
private void m() {}
}
3.Method.invoke()调用过程中参数不正确引发的异常
/**
* 反射的无参的调用的测试
*
* 对于反射调用无参的情况 ,符合调用的方式有三种:
* 1. m.invoke(obj) 传入一个对象,参数不传
* 2. m.invoke(obj,null) 参数传null,编译会有警告,但是可以正常调用
* 3. m.invoke(obj,new ObJect[0]) 参数传一个空的数组,对于可变参数来说相当于没有参数
*/
public class MethodTroubleToo {
public void ping() {
System.out.format("PONG!%n");
}
/**
* foo(Object... o) 在编译的时候会 参数类型转变为数组类型,也就是foo(Object[] o)
* @param args
*/
public void foo(Object ... args){
}
public static void main(String... args) {
args = new String[]{ "3"};
try {
MethodTroubleToo mtt = new MethodTroubleToo();
Method m = MethodTroubleToo.class.getMethod("ping");
switch (Integer.parseInt(args[0])) {
case 0:
m.invoke(mtt); // works
break;
case 1:
m.invoke(mtt, null); // works (expect compiler warning) 编译警告,但是可以成功运行
break;
case 2:
Object arg2 = null; //arg2 的类型为Object,实际上是没有参数类型进行匹配的
m.invoke(mtt, arg2); // IllegalArgumentException
break;
case 3: //new Object[0]创建了一个空的数组, 对应可变参数来说,相当于没有传入参数
m.invoke(mtt, new Object[0]); // works
break;
case 4:
Object arg4 = new Object[0]; //new Object[0]存储在一个Object中,将Object对待,而原有的方法是没有参数的,将会不匹配
m.invoke(mtt, arg4); // IllegalArgumentException
break;
default:
System.out.format("Test not found%n");
}
} catch (Exception x) {
x.printStackTrace();
}
}
}
4.当反射调用失败的时候会抛出InvocationTargetException的异常
/**
* InvocationTargetException 包装了所有的非检测和检测的异常,当方法被调用的时候,可以用InvocationTargetException来抓取异常
*/
public class MethodTroubleReturns {
private void drinkMe(int liters) {
if (liters < 0)
throw new IllegalArgumentException("I can't drink a negative amount of liquid");
}
public static void main(String... args) {
try {
MethodTroubleReturns mtr = new MethodTroubleReturns();
Class<?> c = mtr.getClass();
Method m = c.getDeclaredMethod("drinkMe", int.class);
m.invoke(mtr, -1);
} catch (InvocationTargetException x) {
Throwable cause = x.getCause();
System.err.format("drinkMe() failed: %s%n", cause.getMessage());
} catch (Exception x) {
x.printStackTrace();
}
}
}
测试结果如下:
drinkMe() failed: I can't drink a negative amount of liquid