(5) Method类的基本使用

上面一小节讲解了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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容

  • 一、Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计...
    子非鱼_t_阅读 4,160评论 1 44
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,917评论 6 13
  • 我走在路上 突然听见后面一声响声 看到一群人在往我这里跑 我忙抓住一个在跑的人那是什么声音 他说这是枪声还推开了我...
    少一阅读 231评论 0 0
  • “翅膀硬了,是吧?不是想离婚吗?行,只有一个条件,净身出户!”面对着丈夫、婆婆、公公这苛刻而冰冷的话语,她只觉得浑...
    吴老师教语文阅读 1,052评论 0 3
  • #幸福是需要修出来的~每天进步1% 20170905 【幸福三朵玫瑰】 昨日玫瑰采摘1朵 今日2朵玫瑰 约会 瑜伽...
    幸福实修王迪阅读 255评论 0 0