• JAVA获取方法参数名的分析(一)


    关于题目

    首先解释一下题目. 我们知道, Java通过反射,可以从一个类得知它有哪些方法,有哪些变量,也可以知道每个方法中有哪几个什么类型的传入参数。但有一个东西反射取不到,那就是我们对方法传入参数的命名。

    取得传入参数的名字有什么意义?

    对这个问题的探究,源于在写一个测试类时候的需求。假设我们有一个类需要测试,这个类中有数十个方法。为每个方法编写测试类,将耗费大量的时间和精力。因此我有一种想法,就是通过java的反射,获得这个类所有的方法,再通过传入参数的名字和参数类型,来生成一些符合要求的数据进行传入。(能这样生成数据的前提是:这个类的编码需要遵循严格的规范,对参数的命名有统一的标准,同时,这个类应该和某种业务紧密相关,这样,才能通过业务和参数名字,判断应生成什么合适的数据)。如果能做到上面说的,那么对具有数十或数百个方法的类,要测试的话只需要传入这个类就可以了。

    存在的问题

    根据上面的设想,问题就出现了。获得类的方法,获得类的参数类型,反射都可以做到。但参数名称呢?上网求证,多数人给了直接否定的答案。因为API中根本没有提供相关的方法。但有一些人的观点启发了我。他们提到,IDE(如eclipse,myeclipse)中在编码过程中,调用一个类的方法,在代码提示的时候,ide是可以显示出方法中的参数名字的,如下图:


    IDE是怎样做到的呢,如果IDE可以做到,我们是否可以尝试去分析它们的做法,来获得参数名称。

    可能的做法

    网上找到了一个很直观的方法——通过直接读取.java文件,把类作为一个普通文本,用正则表达式匹配方法,来直接获取参数的名字。

    Java代码  收藏代码
    1. /**    
    2.  *   @author   zhangc    
    3.  *    
    4.  *一个测试程序,用来扫描文件(java文件),找出所有方法的参数列表    
    5.  */  
    6. import java.io.*;  
    7. import java.util.regex.*;  
    8.   
    9. public class ScanSource {  
    10.     static void findArgsList(Object targetSrc) {  
    11.         /* 
    12.          * 正则匹配串基本上是这样子分组情况(A(B(c(d)))) 
    13.          * 串是:(\w+\s+\w+\s*\(((\s*\w+\s*(\[ 
    14.          * \])*\s*\s+(\[\])*\s*\w+\s*(\[\])*,?)+)\)\s*\{) 比如public 
    15.          * static void findArgsList(Object targetSrc,int []a){ 
    16.          * A是匹配整个方法定义行:这里是:static void findArgsList(Object targetSrc,int []a){ 
    17.          * B是匹配匹配参数列表:这里是Object targetSrc,int []a 
    18.          * C是匹配一个参数,包括类型和类型名称和逗号:这里是Object targetSrc, D是匹配数组标识符:这里是[] 
    19.          * 这个串有点bt,水平有限,只能这样 
    20.          */  
    21.         Pattern p = Pattern  
    22.                 .compile("(\w+\s+\w+\s*\(((\s*\w+\s*(\[\])*\s*\s+(\[\])*\s*\w+\s*(\[\])*,?)+)\)\s*\{)");  
    23.         Matcher m = p.matcher((CharSequence) targetSrc);  
    24.   
    25.         // locate the all methord defination  
    26.         while (m.find()) {  
    27.             String methodName = m.group(0);  
    28.             String methodArgName = m.group(1);  
    29.             String strArgs = m.group(2);  
    30.             String fourArgs = m.group(3);  
    31.             System.out.println(methodName + " " + methodArgName + " " + strArgs + " " + fourArgs + " ");  
    32.         }  
    33.   
    34.     }  
    35.   
    36.     public static String LoadTargetFile(String targetFileName) {  
    37.         String result = null;  
    38.         try {  
    39.             FileInputStream fis = new FileInputStream(targetFileName);  
    40.   
    41.             // 临时分配10000size给byte数组。  
    42.             byte[] bufReceived = new byte[10000];  
    43.   
    44.             int counts = fis.read(bufReceived);  
    45.             byte[] bufActual = new byte[counts];  
    46.             System.arraycopy(bufReceived, 0, bufActual, 0, counts);  
    47.             result = new String(bufActual);  
    48.         } catch (FileNotFoundException e) {  
    49.             e.printStackTrace();  
    50.         } catch (IOException e) {  
    51.             e.printStackTrace();  
    52.         }  
    53.         return result;  
    54.     }  
    55.   
    56.     public static void main(String[] args) {  
    57.         String target = LoadTargetFile("src/com/spring/aop/TestAspect.java");  
    58.         System.out.println(target);  
    59.         findArgsList(target);  
    60.     }  
    61. }  

     

     这个通过正则表达式的类,在我写的一个简单的测试类中,是可以取得参数的值的,但当把它用在我们那个有几十个方法的类的时候,表达式的匹配就失效了,没有得到任何的结果(具体原因可能是正则表达式的错误,没能匹配到一些方法)。同时,这种方法需要有.java这个源文件,而在IDE中引入的常常是.class组成的Jar包。为了进一步了解IDE对方法传入参数名的处理,下面我做了一个测试。

    测试IDE对方法传入参数的处理

    建立一个工程。在工程中新建如下的一个类:

    Java代码  收藏代码
    1. package testplugin;  
    2.   
    3. public class TestJar {  
    4.     public void testJar(String jarName, String yourName){  
    5.         System.out.println("jarName:" + jarName + "|| yourName:" + yourName);  
    6.     }  
    7. }  

     

    接着我们用2种方式对这个类打jar包:

    1. 用javac编译类文件然后打到jar包中,命名为testPlugin_javac.jar.

    2. 用MyEclipse直接对工程进行导出,导出为testPlugin_myeclipse.jar.

    (打开2个jar中的TestJar.class文件,会发现2个class文件有差异)。

    再建立一个工程,先后将2个jar包引入做实验,可以看到:

    1. 引入testPlugin_javac.jar, 调用testJar方法,如下图



     可以看到,2个传入参数失去了原有的名称。

    2. 移除上面的包,引入testPlugin_myEclipse.jar, 调用testJar方法,如下图



     可以看到,参数名称被识别出来了。

    关键在于,2个jar包中的class文件不同。我们打开2个class文件(我们只是直观的看一下class文件中的变量,所以没有用专用的工具查看):

    javac生成的.class:

    myelipse直接打出来的.class(实际上就是调用了debug模式编译出来的.class):

    2个class文件里下面的部分都有SourceFile块。应该是用来表示这个class文件是从哪个java文件编译来的。我们重点看上面的部分。

    可以看到,用普通的javac编译出来的类,方法的传入参数名会被编译器修改,于是上面第一个图里SourceFile以上的部分就找不到jarName和yourName 2个名字。而只有通过-debug模式编译出来的类,它的参数名才能被保存下来。而也就是在.class文件中有保留下来参数名的jar包,在IDE中代码提示才能正确显示出参数名字。

    那么说明IDE是否能识别类中的方法名取决于编译过后产生的不同的class文件。那么下一节我们会使用工具来解析这2个class文件来看其中到底有什么不同。

  • 相关阅读:
    【大数据】中文词频统计
    【大数据】复合数据类型,英文词频统计
    Hadoop综合大作业
    分布式文件系统HDFS练习
    安装关系型数据库MySQL 安装大数据处理框架Hadoop
    爬取全部的校园新闻
    作业六|获取一篇新闻的全部信息
    理解爬虫原理
    作业——04 中文词频统计
    复合数据类型,英文词频统计
  • 原文地址:https://www.cnblogs.com/smilesmile/p/3842572.html
Copyright © 2020-2023  润新知