字段解析(CONSTANT_Fieldref_info)的一个疑问

simpleman7210 2013-09-29
在参考虚拟机的书籍或规范时,对于字段解析的说明,我有个问题。下面一段话引用java虚拟机规范(JavaSE7):

当解析一个字段引用时,字段解析会在C和它的父类中先尝试查找这个字段:
1. 如果C中声明的字段与字段引用有相同的名称及描述符,那么此次查找成功。字段查找的结果是C中那个声明的字段。
2. 不然的话,字段查找就会被递归应用到类或接口C的直接父接口上。
3. 否则,如果C有一个父类S,字段查找也会递归应用到S上。
4. 如果还不行,那么字段查找失败。


看规范的其它版本,说法也是类似。

我的问题在第2点上,字段查找要在C的父接口上进行吗?因为我以为,一个接口不可能包含任何字段。即使在接口中声明字段也只能是final,也就是被当作常量。如果我没有理解错,既然接口不会包含字段,那么还有必要在C的任何父接口上查找该字段吗?

subchen 2013-09-30
字段声明成final,和常量是有一定区别的,如下:
1. final String s1 = "aaaaa";
2. final String s2 = getFromOthers();

第一个声明会作为常量,引用的地方一般会被替换成常量,直接进常量池,编译完成后没有引用关系存在。

第二个还是和一般的变量没啥区别,只是被声明为final,在编译期检测值不让修改而已。

final关键字好像只在编译期起作用,不清楚是否JVM也会用到该标志。

所以JVM规范中的第二点也是正确的。
RednaxelaFX 2013-09-30
Java语言里final关键字有两种用法,一种是编译时常量的修饰符,另一种是运行时不变量的修饰符。在C#里这两种用法分别用const和readonly关键字修饰,就清楚很多。

Java接口里可以有成员字段,而这些字段都是默认final的。同样,它们既可以是编译时常量也可以是运行时不变量。而且Java里常量作为类型的成员出现的时候也照样会生成成员的元数据,主要为了支持反射。
simpleman7210 2013-10-02
谢谢两位的解释,我想自己明白了。接口中若有字段,一定是static的(同时是public和final),而不可能是实例变量。因此从道理上说,查找实例字段时,不可能在interface中找到,然而接口中可能包含静态字段,所以当查找静态字段时,需要在父接口上查找。

我按subchen的例子写了一段代码来验证。

class ClassHasStaticMethod {
  static int getInt() {
    return 10+20;
  }
}

public interface InterfaceHasField {
  int a = 10;
  int b = ClassHasStaticMethod.getInt();
}


结果我在解析接口InterfaceHasField时,确实发现这个接口有两个field,并且都是static。(字段的access_flags的ACC_STATIC标志被置位)
RednaxelaFX 2013-10-02
simpleman7210 写道
我按subchen的例子写了一段代码来验证。

class ClassHasStaticMethod {
  static int getInt() {
    return 10+20;
  }
}

public interface InterfaceHasField {
  int a = 10;
  int b = ClassHasStaticMethod.getInt();
}


结果我在解析接口InterfaceHasField时,确实发现这个接口有两个field,并且都是static。(字段的access_flags的ACC_STATIC标志被置位)

而且a有ConstantValue属性,b没有。
Global site tag (gtag.js) - Google Analytics