Appendix C. Answers to Selected Exercises

Exercise 2.3

 .class Refrigerator .field temperature F .field numberOfEggs I .field leftoverPizzas [LPizza; 

Exercise 2.4

 .class BinaryTree .field left LBinaryTree; .field right LBinaryTree; .field data Ljava/lang/Object; 

Exercise 2.5

 .method printTable(Ljava/io/OutputStream;[[Ljava/lang/String;)V .method protected addInts     (Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer .method native static shutdown()V 

Exercise 2.6

 .method public computeSum(FFF)F fload_1      ; Push the first argument fload_2      ; Push the second argument fadd         ; Add them fload_3      ; Push the third argument fadd         ; Add that to the result so far freturn      ; Return the result .end method 

Exercise 2.7

 .method static computeSum(FF)F fload_0      ; First argument is argument 0 now fload_1      ; Second argument is argument 1 now fadd         ; Sum them freturn      ; Return the result .end method 

Exercise 2.8

The class must implement all the methods of MarathonRunner and JavaProgrammer. To successfully implement JavaProgrammer, the class must provide implementations for all the methods of Amiable and Clever.

 .class MarathonRunningJavaProgrammer .super Person .implements MarathonRunner .implements JavaProgrammer ; implement run from marathonRunner .method public run(F)V ;; Implementation omitted .end method ; Implement solvePuzzles from Clever (via JavaProgrammer) .method public solvePuzzles()V ;; Implementation omitted .end method ; Implement smile and shakeHands from Amiable (via JavaProgramer) .method public shakeHands (LAmiable;)V ;; Implementation omitted .end method .method public smile ()V ;; Implementation omitted .end method 

Exercise 3.1

Original: More efficient:
sipush 9 bipush 9
bipush 4 iconst_4
ldc 1.0 fconst_1
ldc -901 sipush -901
ldc 123456 ldc 123456

The last one is a trick question. For a number that big, an ldc is pretty much the most compact this will get.

Exercise 3.2

Each instruction uses up exactly one stack slot. That's because all the values loaded are ints, even the ones with bipush, sipush, and ldc. Therefore, the answer is 5.

Exercise 3.3

 .method public static icalc(IIII)I .var 0 is a .var 1 is b .var 2 is c .var 3 is x iload_0      ; Push a iload_3      ; Push x iload_3      ; Push x again imul         ; Calculate x^2 imul         ; Calculate ax^2 iload_1      ; Push b iload_3      ; Push x imul         ; Push bx iload_2      ; Push c iadd         ; Compute bx+c iadd         ; Compute ax^2+bx+c ireturn .end method 

The result of calling this method with a=1, b=-2, c=-35, and x=7 is 0. The maximum stack height written this way is 3. By rearranging the equation, the maximum stack height can be reduced to 2.

Exercise 3.4

This is more than just a matter of substituting D's for I's; you also have to change the variable numbers because each value takes two slots. The answer:

 .method public static Dcalc(DDDD)D .var 0 is a .var 2 is b .var 4 is c .var 6 is x dload_0      ; Push a dload 6      ; Push x dload 6      ; Push x again dmul         ; Calculate x^2 dmul         ; Calculate ax^2 dload_2      ; Push b dload 6      ; Push x dmul         ; Push bx dload 4      ; Push c dadd         ; Compute bx+c dadd         ; Compute ax^2+bx+c dreturn .end method 

The maximum stack height written this way is 6.

Exercise 3.5

 .method public static icalc2(IIII)I iload_0      ; Push a iload_3      ; Push x imul         ; Calculate ax iload_1      ; Push b iadd         ; Calculate ax+b iload_3      ; Push x imul         ; Compute ax^2+bx iload_2      ; Push c iadd         ; Compute bx+c ireturn .end method 

This way of calculating reduces the max stack to 2. It also reduces the number of instructions: 2 multiplies instead of 3, and you also save a push of x.

Exercise 3.6

 .method static public count(I)I ; Variable 0 contains the number to be counted; call it i ; Variable 1 contains the count of bits so far; call it counter iconst_0 istore_1          ; Initialize counter to 0 ; Begin iload_0           ; Compute i & 1 iconst_1 iand iload_1           ; Add i&1 to counter iadd istore_1 iload_0           ; Shift i right 1 iconst_1 ishr istore_0 ;; Repeat from begin to here 31 more times iload_1           ; Return the counter ireturn .end method 

The important section of code begins at the Begin comment. It looks at the rightmost bit of i by doing the bitwise and with 1. The result will be 0 if the rightmost bit is 0 and 1 if the rightmost bit is 1. This number is added to the counter.

To prepare for the next counting, the value of i is shifted right by 1, then stored back in variable 0. This means that the next-to-rightmost bit is now the rightmost bit. By repeating the code 32 times, you count all the bits.

Exercise 3.7

For the right barrel shift by 1:

 .method public static bshr(I)I iload_0      ; Get the rightmost bit iconst_1 iand bipush 31    ; Move it 31 places to the left ishl         ; This leaves us with the former rightmost bit              ; followed by 31 zeros. iload_0      ; Shift the original number right 1 iconst_1 iushr        ; There is a zero in the leftmost bit ior          ; Use or to set the leftmost bit to the same              ; as the former rightmost bit. ireturn .end method 

The left shift is similar.

Exercise 3.8

The test is fairly easy. Use a shift to move the xth bit to be the rightmost bit. Then use a mask of 1 to make sure only that bit is set (if any bits are set):

 .method public static test(II)Z ; Variable 0 is set ; Variable 1 is x iload_0      ; Push set iload_1      ; Push x ishr         ; Shift set x bytes to the right iconst_1     ; Select just the rightmost bit iand ireturn .end method 

The set is somewhat harder. Making a mask that sets only the xth bit to v is easy, but the JVM has no operation that says, "Set all the nonzero bits to look like this mask." One way is to force that bit in set to be 0, then use ior to set the bit to match the mask. Here's how:

 .method public static set(IIZ)I ; Variable 0 is set ; Variable 1 is x ; Variable 2 is v iconst_1      ; Begin the mask by making a number with all iload_1       ; zeros except the x-th bit, which is 1 ishl iconst_m1     ; Invert the mask so it has all 1's except for the ixor          ; x-th bit, which is 0 iload_0       ; Compute set & mask, which sets only the x-th iand          ; bit to 0 iload_2       ; Make a number containing just the x-th iload_1       ; bit set to v; all 0s otherwise. ishl ior           ; Use ior to set that bit in set ireturn       ; Return the new set .end method 

Exercise 3.9

The .limit locals should be 9 because of the iload_9. Variables 0, 1, and 4 through 8 are not used, but space must be allocated for them anyway.

To compute the stack limit, annotate each instruction with the height of the stack after the instruction is executed:

 aload_3      ; 1 iload 9      ; 2 swap         ; 2 <- Note no change dup          ; 3 astore_2     ; 2 sipush 99    ; 3 bipush 101   ; 4 imul         ; 3 imul         ; 2 

Therefore, the .limit stack should be 4. The stack height at the end of this code fragment is 2.

Exercise 4.1

 .class Dinosaur .field name Ljava/lang/String; .field carnivorous Z .method <init>(Ljava/lang/String;Z)V aload_0                                     ; Invoke the                                             ; superclass invokespecial java/lang/Object/<init>()V    ; constructor aload_0                                     ; Push this aload_1                                     ; Push the name putfield Dinosaur/name Ljava/lang/String;   ; Store the name aload_0                                     ; Push this iload_2                                     ; Push the boolean putfield Dinosaur/carnivorous Z             ; Store in                                             ; carnivorous return .end method 

Exercise 4.2

 new Dinosaur             ; Create a dinosaur dup                      ; Dup it ldc "Veclociraptor"      ; Push the name iconst_1                 ; Yes, it's carnivorous                          ; Invoke the constructor invokespecial Dinosaur/<init>(Ljava/lang/String;Z)V 

Exercise 4.3

 .class CarnivorousDinosaur .super Dinosaur .method <init>(Ljava/lang/String;)V aload_0                   ; Push this aload_1                   ; Push the name iconst_1                  ; Carnivorous is true                           ; Invoke the superclass constructor invokespecial Dinosaur/<init>(Ljava/lang/String;Z)V return .end method 

To create a Velociraptor:

 new CarnivorousDinosaur dup ldc "Velociraptor" invokespecial CarnivorousDinosaur/<init>(Ljava/lang/String;)V 

Exercise 4.4

 .class RightTriangle .field a F .field b F .method hypotenuse()F ; Variable 0 contains a RightTriangle object (this) aload_0                                         ; Push this aload_0                                         ; Get the field a getfield RightTriangle/a F aload_0                                         ; Get the field b getfield RightTriangle/b F invokevirtual RightTriangle/sumOfSquares(FF)F   ; Compute a^2+b^2 f2d                                             ; Convert to                                                 ; double invokestatic java/lang/Math/sqrt(D)D            ; Compute square                                                 ; root d2f                                             ; Back to float freturn .end method 

Exercise 4.5

 .field color Ljava/lang/String; .method setColor(Ljava/lang/String;)V aload_0                                      ; Get this aload_1                                      ; Get the color putfield Dinosaur/color Ljava/lang/String;   ; Store the color return .end method .method setColor(Ljava/awt/Color;)V aload_0                                      ; Get this aload_1                                      ; Get the color                                              ; Call toString invokevirtual java/awt/Color/toString()Ljava/lang/String; putfield Dinosaur/color Ljava/lang/String;   ; Store the color return .end method 

Exercise 4.6

 aload_0                                              ; Push a                                                      ; Dinosaur getstatic java/awt/Color/green Ljava/awt/Color;      ; Push the                                                      ; color invokevirtual Dinosaur/setColor(Ljava/awt/Color;)V   ; Set the                                                      ; color aload_0                                              ; Push a                                                      ; Dinosaur ldc "green" invokevirtual Dinosaur/setColor(Ljava/lang/String;)V 

Exercise 5.1

 .method static isUpper(C)Z iload_0 bipush 'A'            ; Is it < 'A'? if_icmplt fail        ; Go to fail if it is iload_0 bipush 'Z'            ; Is it > 'Z'? if_icmpgt fail        ; Go to fail if it is iconst_1              ; If we get here, ireturn               ; A <= arg <= Z, so                       ; return true fail: iconst_0              ; Return false ireturn .end method 

Exercise 5.2

 .method public static main([Ljava/lang/String;)V ; Call variable 0 args ; Call variable i i iconst_0 istore_1         ; Initialize i to 0 loop: iload_1 aload_0          ; See if i >= the length of args arraylength if_icmpge done   ; Break the loop if it is getstatic java/lang/System/out Ljava/io/PrintStream; aload_0 iload_1          ; Print a[i] aaload invokevirtual java/io/PrintStream/println (Ljava/lang/String;)V iinc 1 1         ; Increment i goto loop        ; Loop again done: return .end method 

Exercise 5.3

Here's the loop:

 .method static printFromAtoB(II)V loop: getstatic java/lang/System/out Ljava/io/PrintStream; iload_0          ; Push and print a invokevirtual java/io/PrintStream/println (I)V iinc 0 1         ; Increment a iload_0          ; Push a iload_1          ; Push b if_icmpgt done   ; a>b? goto done goto loop        ; Otherwise loop again done: return .end method 

If a > b, then the loop will print out a, then stop, because it will continue only until the lower number (kept in a) is greater than b.

A better solution would be to swap a and b if a > b before the loop begins. There are a number of ways to do this, but the most straightforward is

 iload_0 iload_1 if_icmple loop   ; Start the loop if a <= b iload_0          ; Swap a and b using iload_1          ; the stack istore_0 istore_1 

Exercise 5.4

 .method static isUpper(C)Z iload_0 tableswitch 'A' success success success success success success success success success success success success success success success success success success success success success success success success success success default: fail success: iconst_1 ireturn fail: iconst_0 ireturn .end method 

Exercise 5.5

 .method static isUpper(C)Z iload_0 lookupswitch 'A': success 'B': success 'C': success 'D': success 'E': success 'F': success 'G': success 'H': success 'I': success 'J': success 'K': success 'L': success 'M': success 'N': success 'O': success 'P': success 'Q': success 'R': success 'S': success 'T': success 'U': success 'V': success 'W': success 'X': success 'Y': success 'Z': success default: fail success: iconst_1 ireturn fail: iconst_0 ireturn .end method 

Exercise 5.6

The two subclasses are

 .class OutOfMilkException .super SnackException .method <init>()V aload_0                   ; Push this ldc "Out of milk"         ; Default message invokespecial SnackException/<init> (Ljava/lang/String;)V return .end method .end class .class OutOfCookiesException .super SnackException .method <init>()V aload_0                   ; Push this ldc "No more cookies"     ; Default message invokespecial SnackException/<init> (Ljava/lang/String;)V return .end method .end class 

To add a specific out-of-milk exception handler, add a .catch directive before any of the others:

 .catch OutOfMilkException from begin2 to end2 using     NoMilkHandler .catch SnackException from begin2 to end2 using handler2 .catch java/lang/Exception from begin1 to end1 using handler1 ;; Rest of the method NoMilkHandler:     ;; Handle the out-of-milk exception 

If an OutOfCookiesException or any other non-milk-related SnackException occurs between begin2 and end2, then the ordinary handler at handler2 will take care of it. A milk-related OutOfMilkException can be handled differently at NoMilkHandler. More general Exceptions will still be handled at handler1. Non-Exception exceptions, like Throwable and Error, aren't handled in this method at all and are instead passed up to the calling method.

Exercise 5.7

The Java compiler issues an error message like this: catch not reached. 

The Oolong equivalent to this code is

 .catch Exception from e1begin to e1end using ExceptionHandler .catch NullPointerException from e2begin to e2end         using NullPointerExceptionHandler 

In this code, no code can reach the NullPointerExceptionHandler because any NullPointerException is handled by the ExceptionHandler, which is more general and comes first.

Code that cannot be reached makes the Java language designers nervous; why would you write code you could never execute? They made it an error to have code that can't be reached, including exception handlers that will never execute.

Exercise 5.8

 .class ArrayIndexExceptionExercise .method public static main([Ljava/lang/String;)V .catch java/lang/ArrayIndexOutOfBoundsException        from loop to end using done ; Call variable 0 args ; Call variable i i iconst_0 istore_1     ; Initialize i to 0 loop: getstatic java/lang/System/out Ljava/io/PrintStream; aload_0      ; Print a[i] iload_1      ; If we've gone too far, the exception aaload       ; will occur invokevirtual java/io/PrintStream/println (Ljava/lang/String;)V iinc 1 1     ; Increment i goto loop    ; loop again end: done:        ; When an exception is thrown, pop          ; just pop it off the stack and return       ; return .end method 

This is not the recommended way to handle arrays, but you can do it this way.

Exercise 6.1

The most specific superclass of java.awt.Dialog and java.awt.Panel is java.awt.Container. If you answered java.awt.Component, you're incorrect: Container is more specific than Component.

Exercise 8.1

This program treats each argument as a class name. It loads the class, then tries to create an instance using the default constructor. Throughout, it prints copious information about what classes are being loaded.

First, it looks in the already loaded classes. This uses the JDK 1.1 specification. If you are using JDK 1.0, adjust accordingly.

If the class is not found, it looks to see if the class is stored in the file system relative to the current directory. Following the class file naming convention, it substitutes / for ., which turns the package names into directory names. Then it adds .class to the end.

If it cannot find the class in the file system, it looks for system class using findSystemClass.

 import*; import java.util.*; class DebuggingClassLoader extends ClassLoader {    public static void main(String a[])    {        try {            DebuggingClassLoader c = new DebuggingClassLoader();            for(int i = 0; i < a.length; i++)            {                 Class cls = c.loadClass(a[i], true);                 System.out.println("Making an instance of "                    + a[i]);                 cls.newInstance();            }        }        catch(Exception e) {             e.printStackTrace();        }    }    int level = 0;    public Class loadClass(String name, boolean resolve)        throws ClassNotFoundException    {        Class c = null;        indent(level);        System.out.println("Loading " + name +                        (resolve ? " and " :                                   " but not " )                                   + "resolving");          level++;       c = findLoadedClass(name);       if(c != null)       {          indent(level);          System.out.println("Found already loaded class " + c);       }       else       {          try {             // Convert the class name to a file name             InputStream is = new FileInputStream(                  name.replace('.', '/') + ".class");             byte[] b = new byte[is.available()];   ;             c = defineClass(b, 0, b.length);          }          catch(ClassFormatError e1) {             indent(level);             System.out.println(e1.getMessage());             throw new ClassNotFoundException(name+                            ":" + e1.getMessage());          }          catch(IOException e) {             indent(level);             System.out.println(name + " must be a system                class");             c = findSystemClass(name);          }       }       indent(level);       System.out.println("Class " + name + " defined as " + c);       if(resolve)       {          indent(level);          System.out.println("Resolving " + c);          resolveClass(c);       }       level--;       indent(level);       System.out.println("Done with " + name);       return c;    }    void indent(int level)    {       for(int i = 0; i < level; i++)          System.out.print(" ");    } } 

Exercise 14.1

Assume that variable 1 holds i. A simple Java compiler might generate

 iload_1       ; Push i iload_1       ; Push i iadd          ; Compute i+i istore_1      ; Store into i 

This is equivalent to multiplying i by 2. Although multiplication may seem more expensive than addition, it is cheaper in this case because it's multiplication by 2, which can be done by a simple ishl instruction:

 iload_1       ; Push i iconst_1      ; Push 1 ishl          ; Compute i << 1 == i * 2 = i+i istore_1      ; Store into i 

Programming for the Java Virtual Machine
Programming for the Javaв„ў Virtual Machine
ISBN: 0201309726
EAN: 2147483647
Year: 1998
Pages: 158
Authors: Joshua Engel

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: