Skip to content

oracle certified programmer

Second book, i will keep them apart in case i still want to take the exams. But probably an even shorter extract will be made that merges both book extracts.

Like the oca book extract i will use the same chapter divisions here.

recap

Overloading rules are regarded in this order :

  • Exact match by type
  • Matching a superclass type
  • Converting to a large primitive type
  • Converting to an autoboxed type
  • Varargs

These overloading rules tend to go from exact to wider ...

Now mixing these could present some problems, because what order to keep in that case ?! But it usually complains about ambiguous calls so :

ambiguous
1
2
3
4
5
6
7
8
9
// compiles without problems
void f(int x, Integer y) { System.out.println("f(int,Integer)";}
void f(Integer x, int y) { System.out.println("f(Integer,int)";}

...

f(11,12);   // ERROR: method is ambiguous
f(11,new Integer(12)); // f(int,Integer)
f(new Integer(12),11); // f(Integer,int)

Just don't overload like that, choose a better name if it's really needed

Overloading means the same signature...

  • The access modifier must be the same or more accessible
  • The return type must be more specific/restrictive also known as covariant.
  • You cannot throw wider exceptions than the overridden method.

The methods may not be static, static methods are never overridden, they are hidden !!

Advanced Class Design

instanceof operator

It is a long one, but definitely an operator.

The operator a instanceof b returns true if A is an instance of class b, a subclass of b, or a class that implements b. Directly or indirectly.

This means for 'almost' all references a instanceof Object return true, except null because null is not an Object.

It is the internal object that's tested, not the reference type.

instanceof
public class Animal
{
    public static void main(String ... args) {
        Animal a = new Animal();
        Animal da = new Dog(); 
        Dog d = new Dog();
        Cat c = new Cat();

        System.out.println(d instanceof Animal); // true
        System.out.println(c instanceof Animal); // true
        //System.out.println(c instanceof Dog); // does not even compile !!!
        System.out.println(a instanceof Cat);   // false
        System.out.println(a instanceof Dog);   // false
        System.out.println(da instanceof Dog);  // true
        System.out.println(da instanceof Cat);  // false
        System.out.println(da instanceof Animal);  // true
        System.out.println(c instanceof Object);    // true

        //System.out.println(c instanceof a); // does not compile 

        c = null;
        System.out.println(c instanceof Object);    // false
    }
}

class Dog extends Animal {}
class Cat extends Animal {}

The format is : reference instanceof [Class|Interface], and it returns boolean

Note that comparing a dog and a cat, just like casting unrelated types does not even make it passed the compiler ! Note that this is only for classes, because a Cat just cannot be a Dog whatever happens. But for instance when we introduce an unrelated interface :

unrelated
interface Pet {}
class Dog extends Animal{}
class Cat extends Animal{}

public class Animal {
    public static void main(String ... args) {
        Animal a = new Animal();
        Dog d = new Dog();
        Cat c = new Cat();

        // can never happen without rearranging the hierarchy 
        // which the compiler can then check again :
        //System.out.println(c instanceof a); // does not compile 

        // not true, but could happen 
        System.out.println(a instanceof Pet); // compiles, prints false

        // for instance by adding implements Pet to class Dog and then
        a = d;
        System.out.println(a instanceof Pet); // actually true

        // note that this could happen in some other file not compiled 
        // yet or even built yet, so the compiler cannot check until runtime
    } 
} 

Mostly we use instanceof before a cast to test if it will work without ClassCastException.

virtual method invocation

This code explains it all :

virtual methods
public class Animal
{   
    public String name = "Animal name";
    public void call() { 
        System.out.println("Animal method ");
    }
    public void only() {
        System.out.println("Animal only method ");
    }


    public static void main(String ... args) {
        //Animal a = new Animal();
        Animal a = new Dog();
        a.call();   // Dog method
        System.out.println(a.name); // Animal name
        a.only();   // Animal only method
    }
}

class Dog extends Animal {
    public String name = "Dog name";

    public void call() {
        System.out.println("Dog method");
        super.call();
    }

}

This will print :

output
1
2
3
4
Dog method
Animal method
Animal name
Animal only method

Java encounters an Animal reference, but underneath is a Dog data structure. It has a choice between Dog.call() and Animal.call(). I can only imagine that it works like this :

I think java uses a virtual method table in every object. Probably pointers to functions so not THAT big, and so it is easy call super.call() without having to traverse or lookup functions.

I would imagine in c this might look like this:

c trial
// the methods :
void animal_call(void) { 
    printf("Animal method");
} 
void animal_only(void) { 
    printf("Animal only method");
} 
void dog_call(void) { 
    printf("Dog method");
} 

struct Dog {
    struct Animal super;
    char *name;
    int age;
    void (*call)(void);
}

struct Animal {
    char *name;
    void (*call)(void);
    void (*only)(void);
}

void main() {
    printf("Startingn");

    struct Animal *ap;
    struct Dog *dp;

    struct Animal a = {"Animal Name", animal_call, animal_only};
    struct Dog d = {a, "Dog Name", 6, dog_call };

    dp = (struct Dog*)&d;
    ap = (struct Animal*)&d;

    printf("%sn", ap->name);
    printf("%sn", dp->name);
    printf("%sn", dp->super.name);

    ap->call(); // nope, this also prints Dog name
}

With this structure in mind. Most of the behavior can be explained except the ap->call();

  • Reassigning from Dog pointer to Animal pointer makes it print "Animal name" instead of "Dog name".
  • Reassigning from Animal pointer to Dog pointer makes a complete mess, so is not allowed.
  • Invoking super get's us the members for the super class.

Only the method call will not work, but if you look something deeper you will see (with javap -v) that the call to call() and only() are done with the invokevirtual command :

Given this code :

base code
public class Animal
{
    public String name = "Animal name";
    public void call() {
        System.out.println("Animal method ");
    }
    public void only() {
        System.out.println("Animal only method ");
    }

    static int something=11;

    public static void main(String ... args) {
        //Animal a = new Animal();
        Animal a = new Dog();
        a.call();
        System.out.println(a.name);
        a.only();
    }
}

class Dog extends Animal {
    public String name = "Dog name";
    int age = 6;

    public void call() {
        System.out.println("Dog method");
        super.call();
    }
}

Here is a javap -v dump of only the main method :

javap -v
public static void main(java.lang.String...);
   descriptor: ([Ljava/lang/String;)V
   flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
   Code:
   stack=2, locals=2, args_size=1
       0: new           #8                  // class Dog
       3: dup
       4: invokespecial #9                  // Method Dog."<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #10                 // Method call:()V
       12: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       15: aload_1
       16: getfield      #3                  // Field name:Ljava/lang/String;
       19: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       22: aload_1
       23: invokevirtual #11                 // Method only:()V
       26: return

Actually, all calls are done with invokevirtual except the constructors (init).

From the java documentation this is what invokevirtual does :

Let C be the class of objectref. The actual method to be invoked is selected by the following lookup procedure:

  • If C contains a declaration for an instance method m that overrides (ยง5.4.5) the resolved method, then m is the method to be invoked, and the lookup procedure terminates.
  • Otherwise, if C has a superclass, this same lookup procedure is performed recursively using the direct superclass of C; the method to be invoked is the result of the recursive invocation of this lookup procedure.
  • Otherwise, an AbstractMethodError is raised.

methods are found at runtime with a recursive algorithm, and variables are NOT.

toString()

The default toString() method from Object just prints the hashCode , but you can use the apache commons library to do it automatically :

toString
1
2
3
4
5
6
Animal a = new Dog();
System.out.println(a);    // Dog@15db9742
String s[] = new String[1];
System.out.println(s);    // [Ljava.lang.String;@4e25154f]
List l = new ArrayList();
System.out.println(l);    // []

ArrayList has an overridden toString() method, String[] uses the one from Object

equals()

Just using == compares the references to objects (identity), so you need to use .equals() to see if the contents are the same (equality)

If equivalence is used it means equality. Just say it out loud, a person is equal to another if it has the same qualities ( .equals() ), a person is identical to another is it IS that person ( == )

Stringbuilder has not overridden .equals() and i can still not find a good explanation since == already compares references and people expect .equals() to do a full compare. But still you should watch out for this :

comparison
1
2
3
4
5
6
7
String s1 = new String("Lion");
String s2 = new String("Lion");
System.out.println(s1.equals(s2));      // true

StringBuilder sb1 = new StringBuilder("Lion");
StringBuilder sb2 = new StringBuilder("Lion");
System.out.println(sb1.equals(sb2));     // false

There are a number of rules (reflexive,symmetric,transitive,consistent) which are all automatically fulfilled in simple comparison. However you should always make sure x.equals(null) is false for x != null.

Always use @override on equals(). One easy oversight is that you should override boolean equals(Object) ... NOT boolean equals(YourObject) .

hashcode()

Simply the value used to put an object in some hashbucket when used in hash tables etc.

When you override equals() you are also expected to override hashCode() to match it. You will have to have a hashCode() function that puts two values that compare equal in the same bucket. (pt 2 below)

This means you will have to meet these criteria :

  • Within the same program the result of hashCode() may not change.
  • if equals() returns true on two objects, the result of hashCode() must be the same for both objects.
  • The other way around is not needed, which means the hashCode does not have to be unique.

Point 3 is actually not a rule but a relaxation, so remember the first 2.

The hashcode is just an int (not long) , if you make one for an object with multiple members you can construct one from calling .hashCode on reference members (like String), rounding floats and add them together.

enums

enum
1
2
3
enum weekdays {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

Since enums are effectively a set of constants/finals use uppercase letters.

The semicolon at the end is not mandatory when you use only values, but when anything else is present they are needed. I opt for just always add it !

Behind the scenes enums are classes containing mainly static members. So helper methods are also allowed. Some are already defined like .name(), ordinal() or values() to loop over the values.

enum
enum Weekday {
    MON, TUE, WED, THU, FRI, SAT, SUN
}

Weekday wd = Weekday.SUN;

System.out.println(wd);         // SUN
System.out.println(wd.name());  // SUN
System.out.println(wd.ordinal());  // 6

for (Weekday w : Weekday.values()) { 
    System.out.print(w);         // MONTUEWEDTHUFRISATSUN
} 

Every enum value has an ordinal() assigned but you can't compare int's with values like Weekday.SUN == 6. Ordinals can be used as index in arrays etc if needed.

Though ordinals behave a lot like classes they can NOT be extended.

switch

When used in a switch statement, it is actually NOT allowed to use the enum as prefix :

switch
1
2
3
4
5
6
7
8
Weekday wd = Weekday.SUNDAY;

switch (wd) { 
    case MONDAY: // ok
    break;
    case Weekday.TUESDAY: // Compile ERROR
    break;
}

constructors

You could add more data to each member, which acts almost like a subclass of the enum.

constructors
1
2
3
4
5
6
enum Weekday {
    MON("Maandag"), TUE("Dinsdag"), WED("Woensdag"), THU("Donderdag"), FRI("Vrijdag"), SAT("Zaterdag"), SUN("Zondag");
    String nedname;

    private Weekday(String s) { this.nedname = s; }
}

The member declarations must come before the rest, also all constructors are called when any of the enum members is used for the first time. So you get 7 calls at once in the Weekday case and after that no more.

The constructor is only allowed to be called from inside the enum and os therefore private !

You can also add a public abstract function to force all members to implement it.

nested classes

There are 4 flavours of nested classes, member, local, anonymous and static.

  • Member inner class : defined at the level of members, cannot declare static fields or methods, can access all members of the outer class.
  • Local inner class : defined inside a function, goes out of scope when the method ends. Has no access modifiers, cannot be static or use static members, can only access local variables if they are 'effectively final'.
  • Anonymous inner classes, like local inner classes but without a name.
  • Static inner class: defined at the static member level of a class, so no instance access.

member inner class

This is a normal class within a class, but to use it you need an instance of the outer class. This can be done in two ways :

  • From within the methods of the "Outer" class.
  • From outside of the outer class you use new as if it was a method of the outer class : new Outer().new Inner;
member inner class
public class Outer {

    private int field=100;

    public class Inner{ 
        public void func() { 
            System.out.println(field);
        }
    }

    void func( ) {
        Inner i = new Inner();  // works,
        Inner i2 = this.new Inner(); // equivalent 
        i.func();
    }

    public static void main(String[] args) {
        Inner i = new Inner(); // won't compile, there is no instance !!
        Inner i = new Outer().new Inner(); // OK!
        Outer o = new Outer(); i = o.new Inner();       // same !
        i.func();
    }

}

local inner class

This is local to a method, so it also goes out of scope at the end of the method.

local inner class
public class Outer {

    private int field=100;

    void func( ) {

        int localvar = 10;

        class Local { 
            int x=44 * field + localvar++;   // compile ERROR
            int x=44 * field + localvar;    // OK !
        }
        Local l = new Local();
        i.func();
        System.out.println(l.x);    // 4400
    }

    public static void main(String[] args) {
        Outer o = new Outer();
        o.func();
    }
}

Some rules for local classes :

  • They do(can) not have an access specifier.
  • They cannot be declared static or have static field or methods.
  • They have access to all fields of the enclosing class.
  • They have access to the local variables of the method if they are 'effectively' final.

That last one means they don't actually have to be declared final, like the localvar in the code above. localvar is effectively final in the second line, but not in the first line (++).

anonymous inner classes

Anonymous classes are also local but they have no name. But they need to either extend or implement so there is always something after new ....