Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 3

Exercise 3: Add a new method in the base class of Shapes.java that prints a message, but don’t override it in the derived classes. Explain what happens. Now override it in one of the derived classes but not the others, and see what happens. Finally, override it in all the derived classes.

package polymorphism.shape;
import java.util.*;
public class RandomShapeGenerator
{
    private Random rand = new Random();
    public Shape next()
    {
        switch(rand.nextInt(3))
        {
            default:
            case 0: return new Circle();
            case 1: return new Square();
            case 2: return new Triangle();
        }
    }
}
package polymorphism.shape;
public class Shape
{
    public void draw()
    {
    }
    public void erase()
    {
    }
    public void amend()
    {
        System.out.println("Shape.amend()");
    }
}
package polymorphism.shape;
public class Circle extends Shape
{
    @Override public void draw()
    {
        System.out.println("Circle.draw()");
    }
    @Override public void erase()
    {
        System.out.println("Circle.erase()");
    }
    @Override public void amend()
    {
        System.out.println("Circle.amend()");
    }
}
package polymorphism.shape;
public class Square extends Shape
{
    @Override public void draw()
    {
        System.out.println("Square.draw()");
    }
    @Override public void erase()
    {
        System.out.println("Square.erase()");
    }
    @Override public void amend()
    {
        System.out.println("Square.amend()");
    }
}
package polymorphism.shape;
public class Triangle extends Shape
{
    @Override public void draw()
    {
        System.out.println("Triangle.draw()");
    }
    @Override public void erase()
    {
        System.out.println("Triangle.erase()");
    }
    @Override public void amend()
    {
        System.out.println("Triangle.amend()");
    }
}
import polymorphism.shape.*;

public class Shapes3
{
    private static RandomShapeGenerator gen = new RandomShapeGenerator();
    public static void main(String[] args)
    {
        Shape[] s = new Shape[10];
        // fill up the array wth shapes:
        for(int i = 0; i < s.length; i++)
            s[i] = gen.next();
        // make polymorphic method calls:
        for(Shape shp : s)
        {
            shp.draw();
            shp.amend();
        }  
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 2

Exercise 2: Add the @Override annotation to the shapes example.

package polymorphism.shape;
public class Shape
{
    public void draw()
    {
    }
    public void erase()
    {
    }
}
package polymorphism.shape;
public class Square extends Shape
{
    @Override public void draw()
    {
        System.out.println("Square.draw()");
    }
    @Override public void erase()
    {
        System.out.println("Square.erase()");
    }
}
package polymorphism.shape;
public class Triangle extends Shape
{
    @Override public void draw()
    {
        System.out.println("Triangle.draw()");
    }
    @Override public void erase()
    {
        System.out.println("Triangle.erase()");
    }
}
package polymorphism.shape;
public class Circle extends Shape
{
    @Override public void draw()
    {
        System.out.println("Circle.draw()");
    }
    @Override public void erase()
    {
        System.out.println("Circle.erase()");
    }
}
package polymorphism.shape;
import java.util.*;
public class RandomShapeGenerator
{
    private Random rand = new Random();
    public Shape next()
    {
        switch(rand.nextInt(3))
        {
            default:
            case 0: return new Circle();
            case 1: return new Square();
            case 2: return new Triangle();
        }
    }
}
import polymorphism.shape.*;

public class Shapes
{
    private static RandomShapeGenerator gen = new RandomShapeGenerator();
    public static void main(String[] args)
    {
        Shape[] s = new Shape[10];
        // fill up the array wth shapes:
        for(int i = 0; i < s.length; i++)
            s[i] = gen.next();
        // make polymorphic method calls:
        for(Shape shp : s)
            shp.draw();
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 1

Exercise 1: Create a Cycle class, with subclasses Unicycle, Bicycle and Tricycle. Demonstrate that an instance of each type can be upcast to Cycle via a ride( ) method.

package polymorphism.biking;

class Cycle
{
    private String name = "Cycle";
    public static void travel(Cycle c)
    {
        System.out.println("Cycle.ride() " + c);
    }
    public String toString()
    {
        return this.name;
    }
}

class Unicycle extends Cycle
{
    private String name = "Unicycle";
    public String toString()
    {
        return this.name;
    }  
}

class Bicycle extends Cycle
{
    private String name = "Bicycle";
    public String toString()
    {
        return this.name;
    }  

}

class Tricycle extends Cycle
{
    private String name = "Tricycle";
    public String toString()
    {
        return this.name;
    }  
}

public class Biking
{
    public static void ride(Cycle c)
    {
        c.travel(c);
    }
    public static void main(String[] args)
    {
        Unicycle u = new Unicycle();
        Bicycle b = new Bicycle();
        Tricycle t = new Tricycle();
        ride(u);
        ride(b);
        ride(t);       
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 6 Exercise 24

Exercise 24: In Beetle.java, inherit a specific type of beetle from class Beetle, following the same format as the existing classes. Trace and explain the output.

class Insect
{
    private int i = 9;
    protected int j;
    Insect()
    {
        System.out.println("i = " + i + ", j = " + j);
        j = 39;
    }
    private static int x1 = printInit("static Insect.x1 initialized");
    static int printInit(String s)
    {
        System.out.println(s);
        return 47;
    }
}

class Beetle extends Insect
{
    private int k = printInit("Beetle.k initialized");
    public Beetle()
    {
        System.out.println("k = " + k);
        System.out.println("j = " + j);
    }
    private static int x2 = printInit("static Beetle.x2 initialized"); 
}

public class Scarab extends Beetle
{
    private int n = printInit("Scarab.n initialized");
    public Scarab()
    {
        System.out.println("n = " + n);
        System.out.println("j = " + j);
    }
    private static int x3 = printInit("static Scarab.x3 initialized");
    public static void main(String[] args)
    {
        System.out.println("Scarab constructor");
        Scarab sc = new Scarab();          
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 6 Exercise 23

Exercise 23: Prove that class loading takes place only once. Prove that loading may be caused by either the creation of the first instance of that class or by the access of a static member.

class A
{
    static int j = printInit("A.j initialized");
    static int printInit(String s)
    {
        System.out.println(s);
        return 1;
    }
    A()
    {
        System.out.println("A() constructor");
    }  
}

class B extends A
{
    static int k = printInit("B.k initialized");
    B()
    {
        System.out.println("B() constructor");
    }  
}
   
class C
{
    static int n = printInitC("C.n initialized");
    static A a = new A();  
    C()
    {
        System.out.println("C() constructor");
    }
    static int printInitC(String s)
    {
        System.out.println(s);
        return 1;
    }
}

class D
{
    D()
    {
        System.out.println("D() constructor");
    }
}

public class LoadClass extends B
{
    static int i = printInit("LoadClass.i initialized");
    LoadClass()
    {
        System.out.println("LoadClass() constructor");
    }
    public static void main(String[] args)
    {
        // accessing static main causes loading (and initialization)
        // of A, B, & LoadClass
        System.out.println("hi");
        // call constructors from loaded classes:
        LoadClass lc = new LoadClass();
        // call to static field loads & initializes C:
        System.out.println(C.a);
        // call to constructor loads D:
        D d = new D();
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 6 Exercise 21

Exercise 21: Create a class with a final method. Inherit from that class and attempt to overwrite that method.

class WithFinal
{
    final void f()
    {
        System.out.println("WithFinal.f()");
    }
    void g()
    {
        System.out.println("WithFinal.g()");
    }
    final void h()
    {
        System.out.println("WitFinal.h()");
    }
}

class OverrideFinal extends WithFinal
{
    // attempt to override:
    // public final void f() { System.out.println("OverrideFinal.f()"); } // no can do
    @Override public void g()
    {
        System.out.println("OverrideFinal.g()");
    } // OK, not final
    // final void h(); { System.out.println("OVerrideFinal.h()"); } // cannot override final    
}

public class FinalOverrideEx
{
    public static void main(String[] args)
    {
        OverrideFinal of = new OverrideFinal();
        of.f();
        of.g();
        of.h();
        // Upcast:
        WithFinal wf = of;
        wf.f();
        wf.g();
        wf.h();
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 6 Exercise 20

Exercise 20: Show that @Override annotation solves the problem in this section.

class WithFinals
{
    // Identical to private alone:
    private final void f()
    {
        System.out.println("WithFinals.f()");
    }
    // Also automatically "final":
    private void g()
    {
        System.out.println("WithFinals.g()");
    }
}

class OverridingPrivate extends WithFinals
{
    // attempt to override:
    private final void f()
    {
        System.out.println("OverridingPrivate.f()");
    }
    private void g()
    {
        System.out.println("OverridingPrivate.g()");
    }
    // @Override: compiler finds error - does NOT override
    // @Override private final void f() { System.out.println("OverridingPrivate.f()"); }
    // @Override private void g() { System.out.println("OverridingPrivate.g()"); }
}

class OverridingPrivate2 extends OverridingPrivate
{
    // attempt to override:
    public final void f()
    {
        System.out.println("OverridingPrivate2.f()");
    }
    public void g()
    {
        System.out.println("OverridingPrivate2.g()");
    }
    // use @Override so compiler with say "does NOT override or implement"
    // @Override public final void f() { System.out.println("OverridingPrivate2.f()"); }
    // @Override public void g() { System.out.println("OverridingPrivate2.g()"); }
}

public class FinalOverridingIllusionEx
{
    public static void main(String[] args)
    {
        OverridingPrivate2 op2 = new OverridingPrivate2();
        op2.f();
        op2.g();
        // You can upcast:
        OverridingPrivate op = op2;
        // But you can't call the methods:
        //! op.f(); // f() has private access in OverridingPrivate
        //! op.f();
        // Same here:
        WithFinals wf = op2;
        //! wf.f(); // f() has private access in WithFinals
        //! wf.g();
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 6 Exercise 19

Exercise 19: Create a class with a blank final reference to an object. Perform the initialization of the blank final inside all constructors. Demonstrate the guarantee that the final must be initialized before use, and that it cannot be changed once initialized.

class Poppet
{
    private int i;
    Poppet(int k)
    {
        i = k;
    }
    public String toString()
    {
        return ("Poppet " + i);
    }
}

public class BlankFinalEx
{
    private final Poppet p; // Blank final reference
    // Blank finals MUST be initialized in the constructor:
    public BlankFinalEx()
    {
        p = new Poppet(1); // Initialize blank final reference
    }
    public BlankFinalEx(int x)
    {
        p = new Poppet(x); // Initialize blank final reference
    }
    public static void main(String[] args)
    {
        BlankFinalEx b1 = new BlankFinalEx();
        BlankFinalEx b2 = new BlankFinalEx(47);
        // b1.p = new Poppet(2); // Errors: cannot assign values
        // b2.p = new Poppet(3); // to final variables p
        System.out.println("b1.p: " + b1.p);
        System.out.println("b2.p: " + b2.p);
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 6 Exercise 18

Exercise 18: Create a class with a static final field and a final field and demonstrate the difference between the two.

import java.util.*;

class Test
{
    Test()
    {
        System.out.println("Test()");
    }
}

public class Difference
{
    private String name;
    public Difference(String s)
    {
        name = s;  
    }
    static final Test sft = new Test(); // constant reference address
    private final Test ft = new Test();
    static final String SFS = "static final"; // class constant
    private final String fs = "final";
    private static Random rand = new Random();
    static final int SFI = rand.nextInt(); // class constant
    private final int fi = rand.nextInt();
    public String toString()
    {
        return (name + ": " + sft + ", " + ft + ", " + SFS + ", " + fs + ", " + SFI + ", " + fi);  
    }      
    public static void main(String[] args)
    {
        Difference d1 = new Difference("d1");
        Difference d2 = new Difference("d2");
        Difference d3 = new Difference("d3");
        System.out.println(d1);
        System.out.println(d2);
        System.out.println(d3);
    }
}

Output: