描述符与特征签名的区别

描述符与特征签名的区别


概念分析

在学习Java语言和JVM时,可能对字段(方法)的描述符(Descriptor)以及字段(方法)的特征签名(Signatures)这两个概念没有区别清楚。其中描述符是JVM中定义class文件字节码的概念,而特征签名在Java语言层面和java虚拟机层面都有定义,两者的定义并不相同。
1.字段(方法)的描述符
描述符java虚拟机层面的概念,是针对class文件字节码定义的。定义如下:
引用

A field descriptor represents the type of a class, instance, or local variable. It is a series of characters generated by the grammar:

A method descriptor represents the parameters that the method takes and the value that it returns:

注:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2
    http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3

需要注意的两点如下:
描述符概念是针对class字节码的;
方法描述符中包括方法的返回值类型。


2.字段(方法)的特征签名
对于特征签名这个概念,Java语言规范和Java虚拟机规范中存在不同的定义

2.1 Java语言层面
Java方法的特征签名只包括了方法的名称、参数顺序以及参数类型,Java语言规范中的定义参考如下:
引用

Two methods have the same signature if they have the same name and argument types.

注:http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.2
2.2.Java虚拟层面
Java虚拟机中定义的是针对字节码的,方法的特征签名包括方法的返回值以及可能抛出的受检查异常(Checked Exceptions),也就是方法描述时在throws关键字后面列举的异常。Java虚拟机规范中定义如下:
引用

A method signature, defined by the production MethodTypeSignature, encodes the (possibly parameterized) types of the method's formal arguments and of the exceptions it has declared in its throws clause, its (possibly parameterized) return type, and any formal type parameters in the method declaration.

注:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.4
在Java语言层面,描述符与特征签名不同;在Java虚拟机规范中,描述符与特征签名是相同的。

方法重载(Overload)

对于一句话的理解:
引用

在同一class文件中,两个方法可以拥有同样的特征签名,前提是返回值不能相同。

首先说这句话表述是不准确的,特征签名这个概念到底是Java语言层面的还是JVM层面的没有交代清楚。如果是JVM层面的,由于特征签名已经包括了方法的返回值类型,后面又说返回值不同,这不是自相矛盾吗?

有一个如下的例子:
Java代码  收藏代码
public class cls {   
    public static int mytest(List<String> s){return 0;} 
    public static String mytest(List<Integer> ss) {return "";} 


这两个方法,的特征签名(语言层面)是不同的,所以可以编译(JDK6u5)通过,但是由于Java泛型是一种语法糖行为,编译后(Javac)类型将被擦除。字节码如下:
Java代码  收藏代码
public static int mytest(java.util.List); 
  Code: 
   Stack=1, Locals=1, Args_size=1 
   0:   iconst_0 
   1:   ireturn 
  LocalVariableTable: 
   Start  Length  Slot  Name   Signature 
   0      2      0    s       Ljava/util/List; 
 
  LocalVariableTypeTable: length = 0xC 
   00 01 00 00 00 02 00 11 00 14 00 00 
  Signature: length = 0x2 
   00 16 
 
public static java.lang.String mytest(java.util.List); 
  Code: 
   Stack=1, Locals=1, Args_size=1 
   0:   ldc     #2; //String 
   2:   areturn 
  LocalVariableTable: 
   Start  Length  Slot  Name   Signature 
   0      3      0    ss       Ljava/util/List; 
 
  LocalVariableTypeTable: length = 0xC 
   00 01 00 00 00 03 00 18 00 19 00 00 
  Signature: length = 0x2 
   00 1A 


由此,可以知道这两个方法的特征签名(语言)变成相同的了,但是他们的返回类型不同,所以在Class文件中可以共存,开头说的那句话其实就是表述这个意思,只是概念上使用会让人产生疑惑。
在JDK7中,javac的行为和ejc的一致了,这段代码将不能编译通过。

Code属性的LocalVariableTable属性

JDK5引入泛型后,LocalVariableTable属性增加了一个LocalVariableTypeTable属性,两者的不同主要是将描述符替换成了特征签名(语言)。由于泛型在编译后类型将被擦除,描述符将无法准确的描述泛型类型了,而特征签名(语言)记录的是Java语言层面的类型,即未被擦除时的类型。参考代码如下:
Java代码  收藏代码
public static String mytest(List<Integer> ss) {return "";}     
public static String mytest(int ss) {return "";} 

Java代码  收藏代码
public static java.lang.String mytest(java.util.List); 
  Code: 
   Stack=1, Locals=1, Args_size=1 
   0:   ldc     #2; //String 
   2:   areturn 
  LocalVariableTable: 
   Start  Length  Slot  Name   Signature 
   0      3      0    ss       Ljava/util/List; 
 
  LocalVariableTypeTable: length = 0xC 
   00 01 00 00 00 03 00 18 00 19 00 00 
  Signature: length = 0x2 
   00 1A 
 
public static java.lang.String mytest(int); 
  Code: 
   Stack=1, Locals=1, Args_size=1 
   0:   ldc     #2; //String 
   2:   areturn 
  LocalVariableTable: 
   Start  Length  Slot  Name   Signature 
   0      3      0    ss       I 


在第一个方法中,存在泛型参数,在LocalVariableTypeTable属性中,其特征签名(语言)值为00 1A(26)
该索引常量池中值如下:
Java代码  收藏代码
const #26 = Asciz  (Ljava/util/List<Ljava/lang/Integer;>;)Ljava/lang/String;; 

你可能感兴趣的:(区别)