编译器构造/Java
在Java中,有四种方法调用,分别是invokestatic、invokespecial、invokevirtual、invokeinterface。顾名思义,第一个用于调用静态方法,其余用于调用实例方法。由于静态方法不能被重写,因此invokestatic非常简单;它本质上与在C中调用函数相同。
现在我们来看看实例方法调用的机制。考虑以下代码。
class A { public static int f () { return 1; } private int g_private () { return 2; } public final int g_final () { return 3; } public int g_non_final () { return 4; } public void test (A a) { a.f (); // static; this is always 1. a.g_private (); // special; this is always 2. a.g_final (); // special; this is always 3. a.g_non_final (); // virtual; this may be 4 or something else. } } class B extends A { public int g_non_final () { return 6; } } class C extends B { public int g_non_final () { return 7; } public int foo () { return A.this.g_non_final (); } }
invokestatic 使用对类名和方法名的引用来调用,并从堆栈中弹出参数。表达式A.f (2)
被编译成
iconst_2 // push a constant 2 onto the stack invokestatic A.f // invoke a static method // the return value is at the top of the stack.
在Java中,私有方法不能被重写。因此,必须根据类来调用方法,而与对象的创建方式无关。invokespecial 允许这样做;该指令与 invokestatic 相同,只是它除了提供的参数之外,还会弹出对象引用。到目前为止,动态绑定尚未使用,在运行时也不需要关于私有方法的绑定信息。
具体来说,invokespecial
可以用于 (1) 调用私有方法或 (2) 调用超类的方法(包括超类的构造函数,即 <init>)。要调用除 <init> 之外的超类方法,必须像 super.f ()
那样写,其中 f 是超类方法的名称。
从语义上来说,invokeinterface
与 invokevirtual
并没有区别,但它可以给编译器一个关于调用的提示。
类方法可以用 static
修饰符定义。私有类方法可以在同一个对象中,如果它们属于不同的类。两个公共类方法不能在同一个对象中;换句话说,类方法不能被重写。这也意味着 final
修饰符对类方法在语义上没有意义。
每个字段都是基于类访问的。考虑以下内容。
class A { public int i = 2; } class B extends A { public int i = 3; }
B b = new B (); A a = b; b.i++; // this would be 3 + 1 = 4 a.i++; // this would be 2 + 1 = 3
换句话说,访问控制修饰符(无、public、private 和 protected)只影响类客户是否可以访问给定字段。这意味着 Java 虚拟机可能会忽略访问标志,以相同的方式处理每个字段。