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

Java语言层面和JVM层面方法特征签名的区别 及 实例分析

2016-12-27 01:13 901 查看
Java语言层面和JVM层面方法特征签名的区别 及 实例分析

      在文章《Java前端编译:Java源代码编译成Class文件的过程》和《Java
Class文件结构解析 及 实例分析验证》中多次提到Java语言层面方法特征签名和JVM层面方法特征签名的区别。

      下面我们先来回顾一下Java语言层面方法特征签名和JVM层面方法特征签名的区别的是什么,再用测试程序实例来分析验证。

1、两个层面的方法特征签名的区别

      方法特征签名:用于区分两个不同方法的语法符号;

(A)、Java语言层面的方法特征签名:

     特征签名 = 方法名 + 参数类型 + 参数顺序;

      更多请参考:http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.2

(B)、JVM层面的方法特征签名:

     特征签名 = 方法名 + 参数类型 + 参数顺序 + 返回值类型;

      如果存在类型变量或参数化类型,还包括类型变量或参数化类型编译未擦除类型前的信息(FormalTypeParametersopt),和抛出的异常信息(ThrowsSignature),即方法名+签名;

     
Java语言重载(Overload)一个方法,需要ava语言层面的方法特征签名不同,即不包括方法返回值;而Class文件中有两个同名同参数(类型、顺序都相同),但返回值类型不一样,也是允许的,可以正常运行,因为JVM层面的方法特征签名包括返回值类型。


     
同样的,对字段来说,Java语言规定字段无法重载,名称必须不一样;但对Class文件来说,只要两个字段描述(类型)不一样,名称一样也是可以的。


2、实例分析验证

      下面我们先用javac编译测试程序JavacTestOverload.java,测试程序如下:

public class JavacTestOverload {

public String method1(String str) {
String mtdName = Thread.currentThread().getStackTrace()[1].getMethodName();//获取当前方法名称,具体使用数组的那个元素和JVM的实现有关,具体说明可以查看Thread.getStackTrace方法的javadoc
System.out.println("invoke " + mtdName + " return String");
return "";
}

public int method2(String str) {
String mtdName = Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("invoke " + mtdName + " return int");
return 1;
}

public static void main(String[] args) {
JavacTestOverload javacTestOverload = new JavacTestOverload();
String str = javacTestOverload.method1("Test");
int i = javacTestOverload.method2("Test");
}

}


     注意,public String method1(String str)方法和public String method2(String str)方法,方法名称和返回值类型不同,而参数一样,因为方法名不同,是同时满足Java语言层面的方法特征签名和JVM层面的方法特征签名的要求;

     如果方法名相同(都为"method1"),就不满足ava语言层面的方法特征签名的要求,javac编译就会出现错误,如下:



     我们用上面的程序先通过编译,运行调用这两个方法,可以看到两个方法分别输出了自己的名称,如下:



     接着,我们用javap反编译JavacTestOverload..class文件,并保存到JavacTestOverload..txt文件,方便对照分析:

     javap -verbose JavacTestOverload> JavacTestOverload..txt



     然后,通过分析JavacTestOverload..txt反编译信息,可以知道"method2"方法名称对应的常量为第27项,描述符为第28项("method1"方法的分别为第25、26项),如下:



     而后,在JavacTestOverload..class找到"method2"方法名称对应的字节码,修改为"method1",修改前后如下("32"--》"31"):





     这样两个方法的名称就相同了,可以再通过反编译修改后的JavacTestOverload..class来对比,可以看到修改后生成的反编译信息中没有了"method2"的字符信息,都变为了"method1",如下:





     而运行修改后的JavacTestOverload..class,可以看到两个方法运行时输出的名称都为"method1",也证明了两个方法的名称相同,如下:



 

     到这里,这样就证明了:Class文件中有两个同名同参数(类型、顺序都相同),但返回值类型不一样的方法也是允许的,可以正常运行,因为JVM层面的方法特征签名包括返回值类型 。

 

【参考资料】

1、《The Java Virtual Machine Specification》Java SE 8 Edition:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
2、《The Java Language Specification》Java SE 8 Edition:https://docs.oracle.com/javase/specs/jls/se8/html/index.html
3、Java前端编译:Java源代码编译成Class文件的过程
4、Java Class文件结构解析 及 实例分析验证
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: