跳转到内容

编译器构造/Java

来自维基教科书,开放的书籍,为开放的世界

在Java中,有四种方法调用,分别是invokestaticinvokespecialinvokevirtualinvokeinterface。顾名思义,第一个用于调用静态方法,其余用于调用实例方法。由于静态方法不能被重写,因此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 是超类方法的名称。

从语义上来说,invokeinterfaceinvokevirtual 并没有区别,但它可以给编译器一个关于调用的提示。

类方法

[编辑 | 编辑源代码]

类方法可以用 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 虚拟机可能会忽略访问标志,以相同的方式处理每个字段。

华夏公益教科书