📄 chap19.html
字号:
<P>This <FONT FACE="Courier New"><init</FONT>()</FONT> method corresponds to the default no-arg constructor generated automatically by the compiler for class <FONT FACE="Courier New">Dog</FONT>. It first pushes the reference to the object being initialized onto the stack from local variable 0, then it invokes the <FONT FACE="Courier New">Object</FONT>韘 <FONT FACE="Courier New"><init</FONT>()</FONT> method on that reference. (The "<FONT FACE="Courier New">#3</FONT>" specifies the constant pool entry that contains the reference to <FONT FACE="Courier New">Object</FONT>韘 <FONT FACE="Courier New"><init</FONT>()</FONT> method. Remember that this is not the same constant pool as that referred to by the methods of class <FONT FACE="Courier New">CockerSpaniel</FONT>. Each class has its own constant pool.) When all three of these <FONT FACE="Courier New"><init</FONT>()</FONT> methods have returned, the new <FONT FACE="Courier New">CockerSpaniel</FONT> object created by <FONT FACE="Courier New">main()</FONT> is fully initialized and ready for use.</P>
<P>Because every class has at least one <CODE><init</FONT>()</CODE> method, it is common that classes have <CODE><init</FONT>()</CODE> methods with the same signature. (A method's <I>signature</I> is its name and the number and types of its arguments.) The <CODE><init</FONT>()</CODE> methods for the three classes in the inheritance path for <CODE>CockerSpaniel, for example, all</CODE> have the same signature. <CODE>CockerSpaniel</CODE>, <CODE>Dog</CODE>, and <CODE>Object</CODE> all contain a method named <CODE><init</FONT>()</CODE> that takes no arguments.</P>
<P>It would be impossible to invoke a <CODE>Dog</CODE>'s <CODE><init</FONT>()</CODE> method from <CODE>CockerSpaniel</CODE>'s <CODE><init</FONT>()</CODE> method using <FONT FACE="Courier New">invokevirtual</FONT>, because <FONT FACE="Courier New">invokevirtual</FONT> would perform dynamic binding and invoke <CODE>CockerSpaniel</CODE>'s <CODE><init</FONT>()</CODE> method. With <FONT FACE="Courier New">invokespecial</FONT>, however, <CODE>Dog</CODE>'s <CODE><init</FONT></CODE> method can be invoked from <CODE>CockerSpaniel</CODE>'s <CODE><init</FONT>()</CODE> method, because the type of the reference placed in <FONT FACE="Courier New">CockerSpaniel</FONT>韘 class file (in constant pool entry "<FONT FACE="Courier New">#4</FONT>") is <CODE>Dog</CODE>.</P>
<H3><P>invokespecial and Private Methods</P>
</H3><P>In the case of private instance methods, it must be possible for a subclass to declare an instance method with the same signature as a private instance method in a superclass. (<FONT FACE="Courier New">invokespecial</FONT> is not used to invoke private class methods, just private instance methods. Private class methods are invoked with <FONT FACE="Courier New">invokestatic</FONT>.) For example, consider the following code in which <CODE>interestingMethod()</CODE> is declared as <CODE>private</CODE> in a superclass and with package access in a subclass:</P>
<PRE><P><FONT FACE="Courier New">begin</FONT></P>
<FONT SIZE="2"><P></FONT><FONT FACE="Courier New">// On CD-ROM in file invoke/ex2/Superclass.java
<P>class Superclass {</P>
<P> </P>
<P> private void interestingMethod() {</P>
<P> System.out.println("Superclass's interesting method.");</P>
<P> }</P>
<P> </P>
<P> void exampleMethod() {</P>
<P> interestingMethod();</P>
<P> }</P>
<P>}</P>
</FONT><FONT SIZE="2"><P> </P></P>
<P></FONT><FONT FACE="Courier New">// On CD-ROM in file invoke/ex2/Subclass.java
<P>class Subclass extends Superclass {</P>
<P> </P>
<P> void interestingMethod() {</P>
<P> System.out.println("Subclass's interesting method.");</P>
<P> }</P>
<P> </P>
<P> public static void main(String args[]) {</P>
<P> Subclass me = new Subclass();</P>
<P> me.exampleMethod();</P>
<P> }</P>
<P>}</P>
</FONT><FONT SIZE="2"><P> </P></FONT><FONT FACE="Courier New">end</FONT></P></PRE>
<P>When you invoke <CODE>main()</CODE> in <CODE>Subclass</CODE> as defined above, it instantiates a new <FONT FACE="Courier New">Subclass</FONT> object and invokes <FONT FACE="Courier New">exampleMethod()</FONT> on it. Here are the bytecodes for the <FONT FACE="Courier New">main()</FONT> method of class <FONT FACE="Courier New">Subclass</FONT>:</P>
<PRE><P><FONT FACE="Courier New">begin</FONT></P>
<FONT SIZE="2"><P></FONT><FONT FACE="Courier New"> 0 new #2 <Class Subclass</FONT>
<P> 3 dup</P>
<P> 4 invokespecial #6 <Method <init</FONT>() of class Subclass</FONT></P>
<P> 7 astore_1</P>
<P> 8 aload_1</P>
<P> 9 invokevirtual #8 <Method void exampleMethod() of class Superclass</FONT></P>
<P>12 return</P>
</FONT><FONT SIZE="2"><P> </P></FONT><FONT FACE="Courier New">end</FONT></P></PRE>
<P>Subclass inherits <FONT FACE="Courier New">exampleMethod()</FONT> from <FONT FACE="Courier New">Superclass</FONT>. When <FONT FACE="Courier New">main()</FONT> invokes <FONT FACE="Courier New">exampleMethod()</FONT> on a <FONT FACE="Courier New">Subclass</FONT> object, it uses <FONT FACE="Courier New">invokevirtual</FONT>. The Java Virtual Machine will create and push a new frame onto the stack and begin executing the bytecodes of <FONT FACE="Courier New">exampleMethod()</FONT> as defined in class <FONT FACE="Courier New">Superclass</FONT>. Here are the bytecodes for <FONT FACE="Courier New">exampleMethod()</FONT>:</P>
<PRE><P><FONT FACE="Courier New">begin</FONT></P>
<FONT SIZE="2"><P></FONT><FONT FACE="Courier New">0 aload_0
<P>1 invokespecial #7 <Method void interestingMethod() of Superclass</FONT></P>
<P>4 return</P>
</FONT><FONT SIZE="2"><P> </P></FONT><FONT FACE="Courier New">end</FONT></P></PRE>
<P>Note that <FONT FACE="Courier New">exampleMethod()</FONT> first pushes the reference passed in local variable 0 (the hidden <FONT FACE="Courier New">this</FONT> passed to all instance methods) onto the stack. It then invokes <FONT FACE="Courier New">interestingMethod()</FONT> on that reference with the <FONT FACE="Courier New">invokespecial</FONT> instruction. The class identified in the constant pool entry specified by the <FONT FACE="Courier New">invokespecial</FONT> instruction, entry #7, is <FONT FACE="Courier New">Superclass</FONT>. The Java Virtual Machine invokes the <FONT FACE="Courier New">interestingMethod()</FONT> defined in <FONT FACE="Courier New">Superclass</FONT> <I>even though</I> the object is an instance of class <FONT FACE="Courier New">Subclass</FONT> and there is an accessible <FONT FACE="Courier New">interestingMethod()</FONT> defined in <FONT FACE="Courier New">Subclass</FONT>.</P>
<P>The program correctly prints <CODE>"Superclass's interesting method"</CODE>. If <FONT FACE="Courier New">interestingMethod()</FONT> had been invoked with an <FONT FACE="Courier New">invokevirtual</FONT> instruction instead of <FONT FACE="Courier New">invokespecial</FONT>, the program would have printed <CODE>"Subclass's interesting method"</CODE>. Why? Because the virtual machine would choose the <CODE>interestingMethod()</CODE> to call based on the actual class of the object, which is <CODE>Subclass</CODE>. So it would use <CODE>Subclass</CODE>'s <CODE>interestingMethod()</CODE>. With <FONT FACE="Courier New">invokespecial</FONT> however, the virtual machine selects the method based on the type of the reference, so <CODE>Superclass</CODE>'s version of <CODE>interestingMethod()is</CODE> invoked.</P>
<P>invokespecial and <CODE>super</P>
</CODE><P>In the case of invocation of a method with the <CODE>super</CODE> keyword, as in <CODE>super.someMethod()</CODE>, you want the superclass韘 version of a method to be invoked, even if the current class overrides the method. Once again, <FONT FACE="Courier New">invokevirtual</FONT> would invoke the current class韘 version, so it can韙 be used. For an example, consider this code:</P>
<PRE><P><FONT FACE="Courier New">begin</FONT></P>
<FONT SIZE="2"><P></FONT><FONT FACE="Courier New">// On CD-ROM in file invoke/ex3/Cat.java
<P>class Cat {</P>
<P> </P>
<P> void someMethod() {</P>
<P> }</P>
<P>}</P>
</FONT><FONT SIZE="2"><P> </P></P>
<P></FONT><FONT FACE="Courier New">// On CD-ROM in file invoke/ex3/TabbyCat.java
<P>class TabbyCat extends Cat{</P>
<P> </P>
<P> void someMethod() {</P>
<P> super.someMethod();</P>
<P> }</P>
<P>}</P>
</FONT><FONT SIZE="2"><P> </P></FONT><FONT FACE="Courier New">end</FONT></P></PRE>
<P>Here are the bytecodes for <FONT FACE="Courier New">TabbyCat</FONT>韘 <FONT FACE="Courier New">someMethod()</FONT>:</P>
<PRE><P><FONT FACE="Courier New">begin</FONT></P>
<FONT SIZE="2"><P></FONT><FONT FACE="Courier New">0 aload_0
<P>1 invokespecial #4 <Method void someMethod() of class Cat</FONT></P>
<P>4 return</P>
</FONT><FONT SIZE="2"><P> </P></FONT><FONT FACE="Courier New">end</FONT></P></PRE>
<P>Had <FONT FACE="Courier New">invokevirtual</FONT> been used in this case, the <FONT FACE="Courier New">someMethod()</FONT> defined in class <FONT FACE="Courier New">TabbyCat</FONT> would have been invoked. Because <FONT FACE="Courier New">invokespecial</FONT> is used and the constant pool entry (#4, in this case) indicates the <FONT FACE="Courier New">someMethod()</FONT> declared in class <FONT FACE="Courier New">Cat</FONT> should be invoked, the Java Virtual Machine correctly invokes the superclass韘 (<FONT FACE="Courier New">Cat</FONT>韘) version of <FONT FACE="Courier New">someMethod()</FONT>.</P>
<P>Whether or not the Java Virtual Machine actually uses static binding to execute an <FONT FACE="Courier New">invokespecial</FONT> instruction, or instead uses a special kind of dynamic binding, depends on whether or not the referring class韘 <FONT FACE="Courier New">ACC_SUPER</FONT> flag is set. Prior to JDK version 1.0.2, the <FONT FACE="Courier New">invokespecial</FONT> instruction was called <FONT FACE="Courier New">invokenonvirtual</FONT> and always resulted in static binding. It turned out, however, that <FONT FACE="Courier New">invokenonvirtual</FONT>韘 stubborn insistence on static binding did not yield a correct implementation of the semantics of the Java language in all cases. (In other words, there was a "bug" in the instruction set.) In JDK 1.0.2, the <FONT FACE="Courier New">invokenonvirtual</FONT> instruction was renamed <FONT FACE="Courier New">invokespecial</FONT> and its semantics were changed. In addition, a new flag, <FONT FACE="Courier New">ACC_SUPER</FONT>, was added to the <FONT FACE="Courier New">access_flags</FONT> item of the Java class file.</P>
<P>The <FONT FACE="Courier New">ACC_SUPER</FONT> flag item of a class file indicates to the Java Virtual Machine which semantics it should use to execute <FONT FACE="Courier New">invokespecial</FONT> instructions it encounters in the bytecodes of that class file. If the <FONT FACE="Courier New">ACC_SUPER</FONT> flag is not set, the virtual machine uses the old (<FONT FACE="Courier New">invokenonvirtual</FONT>) semantics. Else, if the <FONT FACE="Courier New">ACC_SUPER</FONT> flag is set, the virtual machine uses the new semantics. All new Java compilers are supposed to set the <FONT FACE="Courier New">ACC_SUPER</FONT> flag in every class file they generate.</P>
<P>According to the old semantics, the virtual machine will perform static binding in all cases when executing <FONT FACE="Courier New">invokespecial</FONT>. By contrast, the new semantics requires static binding in all cases except one: the invocation of superclass methods.</P>
<P>According to the new semantics, when the Java Virtual Machine resolves an <FONT FACE="Courier New">invokespecial</FONT> instruction韘 symbolic reference to a superclass method, it dynamically searches the current class韘 superclasses to find the nearest superclass implementation of the method. (As used here, "nearest" means the implementation of the method declared in the superclass that is closest to the current class in its inheritance hierarchy.) In most cases, the virtual machine will likely discover that the nearest implementation of the method is in the superclass listed in the symbolic reference. But it is possible that the virtual machine could find the nearest implementation in a different superclass.</P>
<P>For example, imagine you create an inheritance hierarchy of three classes: <FONT FACE="Courier New">Animal</FONT>, <FONT FACE="Courier New">Dog</FONT>, and <FONT FACE="Courier New">CockerSpaniel</FONT>. Assume class <FONT FACE="Courier New">Dog</FONT> extends class <FONT FACE="Courier New">Animal</FONT>, class <FONT FACE="Courier New">CockerSpaniel</FONT> extends class <FONT FACE="Courier New">Dog</FONT>, and that a method defined in <FONT FACE="Courier New">CockerSpaniel</FONT> uses <FONT FACE="Courier New">invokespecial</FONT> to invoke a non-private superclass method named <FONT FACE="Courier New">walk()</FONT>. Assume also that when you compiled <FONT FACE="Courier New">CockerSpaniel</FONT>, the compiler set the <FONT FACE="Courier New">ACC_SUPER</FONT> flag. In addition, assume that when you compiled <FONT FACE="Courier New">CockerSpaniel</FONT>, class <FONT FACE="Courier New">Animal</FONT> defined a <FONT FACE="Courier New">walk()</FONT> method, but <FONT FACE="Courier New">Dog</FONT> didn韙. In that case, the symbolic reference from <FONT FACE="Courier New">CockerSpaniel</FONT> to the <FONT FACE="Courier New">walk()</FONT> method would give <FONT FACE="Courier New">Animal</FONT> as its class. When the <FONT FACE="Courier New">invokespecial</FONT> instruction in <FONT FACE="Courier New">CockerSpaniel</FONT>韘 method is executed, the virtual machine would dynamically select and invoke <FONT FACE="Courier New">Animal</FONT>韘 <FONT FACE="Courier New">walk()</FONT> method.</P>
<P>Now imagine that later, you added a <FONT FACE="Courier New">walk()</FONT> method to <FONT FACE="Courier New">Dog</FONT>, and recompiled <FONT FACE="Courier New">Dog</FONT>, but didn韙 recompile <FONT FACE="Courier New">CockerSpaniel</FONT>. <FONT FACE="Courier New">CockerSpaniel</FONT>韘 symbolic reference to the superclass <FONT FACE="Courier New">walk()</FONT> method still claims <FONT FACE="Courier New">Animal</FONT> as its class, even though there is now an implementation of <FONT FACE="Courier New">walk()</FONT> in <FONT FACE="Courier New">Dog</FONT>韘 class file. Nevertheless, when the <FONT FACE="Courier New">invokespecial</FONT> instruction in <FONT FACE="Courier New">CockerSpaniel</FONT>韘 method is executed, the virtual machine would dynamically select and invoke <FONT FACE="Courier New">Dog</FONT>韘 implementation of the <FONT FACE="Courier New">walk()</FONT> method.</P>
<H3><EM><P>The invokeinterface Instruction</P>
</EM></H3><P>The <FONT FACE="Courier New">invokeinterface</FONT> opcode performs the same function as <FONT FACE="Courier New">invokevirtual</FONT>, it invokes instance methods and uses dynamic binding. The difference between these two instructions is that <FONT FACE="Courier New">invokevirtual</FONT> is used when the type of the reference is a <I>class</I>, whereas <FONT FACE="Courier New">invokeinterface</FONT> is used when the type of the reference is an <I>interface</I>.</P>
<P>The Java Virtual Machine uses a different opcode to invoke a method on an interface reference because it can't make as many assumptions about the method table offset given an interface reference as it can given a class reference. (Method tables are described in Chapter 8, "The Linking Model.") Given a class reference, a method will always occupy the same position in the method table, independent of the actual class of the object. This is not true given an interface reference. The method could occupy different locations for different classes that implement the same interface.</P>
<P>For examples of the use of <FONT FACE="Courier New">invokeinterface</FONT> in bytecodes, see the "Examples of Method Invocation" section later in this chapter.</P>
<H3><EM><P>Invocation Instructions and Speed</P>
</EM></H3><P>As you might imagine, invoking a method given an interface reference is likely slower than invoking a method given a class reference. When the Java Virtual Machine encounters an <FONT FACE="Courier New">invokevirtual</FONT> instruction and resolves the symbolic reference to a direct reference to an instance method, that direct reference is likely an offset into a method table. From that point forward, the same offset can be used. For an <FONT FACE="Courier New">invokeinterface</FONT> instruction, however, the virtual machine will have to search through the method table every single time the instruction is encountered, because it can't assume the offset is the same as the previous time. </P>
<P>The fastest instructions will likely be <FONT FACE="Courier New">invokespecial</FONT> and <FONT FACE="Courier New">invokestatic</FONT>, because methods invoked by these instructions are statically bound. When the Java Virtual Machine resolves the symbolic reference for these instructions and replaces it with a direct reference, that direct reference will likely include a pointer to the actual bytecodes.</P>
<H3><EM><P>Examples of Method Invocation</P>
</EM></H3><P>The following code illustrates the various ways the Java Virtual Machine invokes methods and shows which invocation opcode is used in each situation:</P>
<PRE><P><FONT FACE="Courier New">begin</FONT></P>
<FONT SIZE="2"><P></FONT><FONT FACE="Courier New">// On CD-ROM in file invoke/ex4/InYourFace.java
<P>interface InYourFace {</P>
<P> void interfaceMethod ();</P>
<P>}</P>
</FONT><FONT SIZE="2"><P> </P></P>
<P></FONT><FONT FACE="Courier New">// On CD-ROM in file invoke/ex4/ItsABirdItsAPlaneItsSuperclass.java
<P>class ItsABirdItsAPlaneItsSuperclass implements InYourFace {</P>
<P> </P>
<P> ItsABirdItsAPlaneItsSuperclass(int i) {</P>
<P> super(); // invokespecial (of an <init</FONT>)</P>
<P> }</P>
<P> </P>
<P> static void classMethod() {</P>
<P> }</P>
<P> </P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -