Oracle Certified Professional (OCP) Java Programmer, 1.2 - Object Orientation ☕

|

Encapsulation, IS-A, HAS-A (Objective 5.1)

  • Encapsulation is the bundling of data with the methods that operate on that data. It results in a self-contained and complete entity
  • Information-hiding is a design principle that strives to shield client classes from the internal workings of a class
  • It is common to use the term encapsulation to refer to encapsulation AND information hiding.
  • Classes that use information-hiding properly have two features:
    • Instance variables should be kept shielded to outside classes (usually with the private modifier).
    • Getter and setter methods provide access to instance variables.
  • IS-A, “inherits from”, and “is a subtype of “ are all equivalent expressions.
  • IS-A refers to class inheritance (extends) or interface implementation (implements).
  • HAS-A means an instance of one class “has a” object reference of another class.
  • HAS-A relationships always rely on member variables (including static).

Inheritance (Objective 5.5)

  • Inheritance allows a class to inherit public and protected members of a superclass, and default members within the same package.
public class Alpha {
	int over1 = 1;
	protected int over2 = 2;
	public int over3 = 3;

	int getOver1(){
		return over;
	}

	protected int getOver2(){
		return over2;
	}

	public int getOver3(){
		return over3;
	}
}
class Beta extends Alpha {
  public static void main(String args[]){
    Alpha a = new Alpha();
    Alpha b = new Beta();
    Beta c = new Beta();

    System.out.println(a.getOver1());	//prints 1
    System.out.println(b.getOver1());	//prints 4
    System.out.println(c.getOver1());	//prints 4
    System.out.println(a.getOver2());	//prints 2
    System.out.println(b.getOver2());	//prints 5
    System.out.println(c.getOver2());	//prints 5
    System.out.println(a.getOver3());	//prints 3
    System.out.println(b.getOver3());	//prints 6
    System.out.println(c.getOver3());	//prints 6
  }

  //overriding Alpha methods
  int getOver1(){
    return 4;
  }

  protected int getOver2(){
    return 5;
  }

  public int getOver3(){
    return 6;
  }
}
  • Static members are inherited.
public class Animal {
	static String name = "animal";
	static String getName(){
		return name;}
}

class Dog extends Animal {
	static String name = "dog";
	public static void main(String args[]){
		Animal a = new Dog();
		System.out.println(a.getName()); //prints: animal
		System.out.println(a.name);// prints: animal
		Dog d = new Dog();
		System.out.println(d.getName()); //prints: animal
		System.out.println(d.name); //prints: dog
		}
}
  • For subclasses outside of the package of its superclass, its protected members cannot be accessed by a superclass reference.
package pkga;

public class clasA{
      protected void doThis(){}
 }
package pkgb;
import pkga.clasA;

public class clasB extends clasA{
      public static void main(String args[]){
           clasA ca = new clasB();
           ca.doThis(); ///compiler error: class not visible
     new clasB().doThis(); //legal
      }
 }
  • All classes are subclasses of type Object, therefore they inherit Object’s methods.

Polymorphism (Objective 5.2)

  • Polymorphism allows objects to treated in a substitutable way.
  • A reference variable is always of a single unchangeable type, but it can refer to a subtype object.
  • A reference variable can be declared as a class type or an interface type. If the variable is declared as an interface type, it can reference any object of any class that implements that interface.
  • The reference variable’s type determines which methods can be called on that reference!
  • At runtime, ONLY instance methods are dynamically selected based on the actual object.

Overriding and Overloading (Objectives 1.5 and 5.4)

  • Methods can be overridden or overloaded; constructors can only be overloaded.

  • Rules for overriding a method:
    1. Must be an inherited method.
    2. Must have same argument list.
    3. Must have same return type (as of Java 5, the return type can be a subclass).
    4. CANNOT have a more restrictive access modifier.
    5. May have a less restrictive access modifier.
    6. Must NOT throw new or broader checked exceptions.
    7. May throw narrower, fewer or no checked exceptions e.g. a method that declares a IOException can be overridden by a method that declares a FileNotFoundException (it’s a subclass of IOException).
    8. The overriding method CAN throw any unchecked exception, regardless of whether the overridden method declares the exception
  • Overloading means reusing a method name, but with different arguments.
  • Rules for overloading a method:
    1. Must have different argument list
    2. May have a different return type
    3. May have different access modifiers
    4. May throw different exceptions
    5. Methods from a superclass can be overloaded in a subclass.
  • Reference type determines which overloaded method will be used at compile time.
  • Object type determines which overridden method is used at runtime.
class Animal {
  public void eat() {
    System.out.println("Animal eating");
  }
}

class Horse extends Animal {
  public void eat() {
    System.out.println("Horse eating");
  }
  public void eat(String s) {	//overloaded eat()
    System.out.println("Horse eating " + s);
  }
  /*public String eat(){
  	return null;
  } this is not a legal override or overload
  */
}

class ZooKeeper{
	static void makeItEat(Animal subject){
		subject.eat();
	}
}

public class TestAnimals {
  public static void main (String [] args) {
    Animal a = new Animal();
    Animal b = new Horse(); //Animal ref, but a Horse object
    Horse c = new Horse();
    a.eat(); // Calls the Animal version of eat()
    b.eat(); // Calls the Horse version of eat()
    //b.eat(“carrots”);  Compiler error as based on ref type
    c.eat(carrots);	 //Calls overloaded eat(String s) in Horse
    ZooKeeper.makeItEat(a);	// Calls the Animal version of eat()
    ZooKeeper.makeItEat(b); // Calls the Horse version of eat()
  }
}

//Prints:
//Animal eating
//Horse eating
//Horse eating carrots
//Animal eating
//Horse eating
  • A subclass can call the superclass version of an overridden method by preceding it with the keyword super. This lets you reuse the code in the superclass version of a method, yet still override it to provide some additional specific behavior.
public class Dog extends Animal
	public void eat(){
		super.eat(); //Prints:	Animal eating
		System.out.println(..dog food!); //..dog food!
	}
}
  • You can refer to an instance variable named in a superclass by preceding it with super.
  • Variables are NOT overridden; they are shadowed. A variable in the subclass does NOT replace the variable of the same name in the superclass; it merely hides it.
class Alpha {
  int over = 1;
}

class Beta extends Alpha {
  int over = 2;
}

public class Gamma extends Beta{
  int over = 3;

  public static void main(String args[]){
    new Gamma().go();
    }

	public void go(){
		Alpha a = new Gamma();
		Beta b = new Gamma();
		System.out.println(super.over + " " + a.over + " " + b.over);
	}
}

//prints: 2 1 2
  • You CANNOT override a static method; you are really redefining it. Often referred to as “hiding”.
class Foo {
    public static void method() {
        System.out.println("in Foo");
    }
}

class Bar extends Foo {
    public static void method() {
        System.out.println("in Bar");
    }
}

class Test {
    public static void main(String[] args) {
        Foo f = new Bar();
        f.method(); // prints “in Foo”
    }
}
  • Since f is declared as type Foo, the compiler looks at f.method() and decides it means Foo.method(). It doesn’t matter that the instance referred to by f is actually a Bar - for static methods the compiler only uses the declared type of the reference. So a static method does NOT have run-time polymorphism.
  • Sometimes you will see error messages from the compiler that talk about overriding static methods. Whoever wrote these particular messages has not read the Java Language Specification and does not know the difference between overriding and hiding.

  • Static methods are viewed as creating the same “overriding” errors in terms of modifiers however:
public class sup{
  final static void method(){
    System.out.println("Super");
  }
}
class test extends sup{
  static void method(){
    System.out.println("test");//compiler error: CANNOT “override” final method
  }
}
  • You cannot use super or this in a static method.

Reference Variable Casting (Objective 5.2)

  • The Referencing rule: A reference of type X can point to objects of class X, or to objects of any of X’s subtypes.
  • The compiler checks the reference types to see if a cast is needed (for a downcast), it does NOT check if it will result in a reference that is assignment compatible.
  • The JVM checks the actual object type to see if the it is a supertype of the reference it is being assigned to, if it is, a ClassCastException is thrown

For the answering questions in the exam, simple rules to follow:

class A{}
class B extends A{}

class MyClass{}

class Test{
	public static void main(String... x){
    A a = new A();
    B b;
    MyClass myClass = new MyClass();
    //enter line here
	}
}
  • When assigning a supertype reference (‘a’ in this case ) to a reference (‘b’), it’s a downcast. There must be an explicit cast or it will result in a compiler error.
    b = (B) a;	// compiles
    
  • If the object you are casting (‘myClass’) is not a supertype of, a subtype of, or the same type as that of the assignment reference type (‘b’), this will result in a compiler error.
    B b = (B) myClass; 	//compilation error
    
  • If the actual object (that ‘a’ points to) is a supertype of the assignment reference type (‘b’), a ClassCastException will be thrown. This means the code example under rule#1 will compile fine; but fail at run time.
    b = (B) a;	//runtime exception. a is an A type, this is a supertype of B, so b cannot be assigned to a
    

    whereas this compiles and executes:

    A x = new B();
    B b = (B) x; //supertype -downcast req: x is a B which is the same type as b, so it will run!
    

casting

class Bango{}
class Bongo extends Bango{}
class Bingo extends Bongo{}

class TestAssign{
  public static void main(String args[]){
    Bango b1 = new Bongo();
    Bongo b2 = new Bingo();
    Bango b3 = new Bingo();

    Bongo b4 = b1; //supertype – downcast req: wont compile
    Bango b5 = b3; //same type – no cast req: compiles + runs.
    Bango b6 = b2; //subtype – upcasted, no cast req: compiles + runs.
    Bango b7 = (Bongo) b3; //same type – no cast req: downcasted explicitly, so compiles + runs.
    Bingo b8 = (Bingo) b1; //supertype – downcast req: Compiles as is explicitly cast; but it does NOT run as b1 is supertype object.
  }
}
  • Multiple casts in a single expression will be evaluated from right to left. (A) (B) c first the ref variable is cast to a B, then it is cast to a C.

Implementing an Interface (Objective 1.2)

  • Rules for implementing an interface:
    1. Provide concrete implementations for all methods from the declared interface.
    2. Follow all the rules for legal overrides.
    3. Make sure implementation doesn’t try to restrict access modifier, method must be public!
  • An interface can extend multiple other interfaces, but it can never implement anything.
interface Bounceable extends Moveable, Spherical { // ok!
  void bounce();
  void setBounceFactor(int bf);
}

interface Moveable {
  void moveIt();
}

interface Spherical {
  void doSphericalThing();
}

class Ball implements Bounceable {
   public void bounce() { } // implement all of Bounceable's methods
   public void setBounceFactor(int bf) { }
   public void moveIt() { }
   public void doSphericalThing() { }
}

Return Types (Objective 1.5)

As of Java 5, you’re allowed to change the return type in the overriding method as long as the new return type is a subtype of the declared type of the overridden method. This is a covariant return.

class Alpha{
	Alpha doStuff(){
		return new Alpha();}
}

class Beta extends Alpha{
	Beta doStuff(){	//legal override
		return new Beta();}
}

Return type rules:

  • You can return null in a method with an Object reference return type.
    Beta doStuff(){
      return null;}
    
  • An array is a legal return type.
    public String[] go(){
      return new String[] {Fred, Barney};
    }
    
  • For methods with primitive return types, any value that can be implicitly converted to the return type can be returned.
    public int foo() {
    char c = 'c';
    return c; // char is widened to an int
    }
    
  • Nothing can be returned from a void, but you can return nothing. You’re allowed to simply say return, this busts out of a method straight away.
    public void bar() {
    return;
    }
    
  • Methods with an object reference return type can return a subtype.
    public Animal getAnimal() {
    return new Horse(); // Assuming that Horse extends Animal
    }
    
  • Methods with an interface return type can return any implementer.
public interface Chewable { }

class Gum implements Chewable { }

class TestChewable {
  Chewable getChewable() {
      return new Gum(); // Return interface implementer
  }
}

Constructors and Instantiation (Objectives 1.6 and 5.4)

  • A constructor is always invoked when a new object is created.
  • Rules for constructors:
    1. Must have the same name as the class.
    2. Can use any access modifier
    3. Constructors do NOT have a return type. If you see a method with the same name as the class and a return type, it is NOT a constructor but it is a legal method.
    4. The compiler creates a default no-arg constructor if you do NOT create one yourself.
    5. If you want a no-arg constructor and you’ve created another constructor in your class, the compiler won’t provide the no-arg constructor by default.
    6. The first statement of every constructor must be a call to either this() or super().
    7. The compiler will insert a call to super() as the first line of the constructor if you have NOT already put in a call to this() or super().
    8. A constructor can only be invoked from within another constructor.
    9. Abstract classes have constructors that are called when a concrete subclass is instantiated.
    10. Interfaces do NOT have constructors.
    11. Calls to this() and super() cannot be in the same constructor.
  • When you say Horse h = new Horse(); the following happens:
    1. Horse constructor is invoked.
    2. Animal constructor is invoked
    3. Object constructor is invoked
    4. Object instance variables are given their explicit values.
    5. Object constructor completes.
    6. Animal instance variables are given their explicit values (if any).
    7. Animal constructor completes.
    8. Horse instance variables are given their explicit values (if any).
    9. Horse constructor completes.

Statics (Objective 1.3)

  • Use static methods to implement behaviours that are not affected by the state of any instance.
  • Use static variables to hold data that is class specific as opposed to instance specific—there will be only one copy of a static variable.

  • A static method CANNOT access an instance variable directly.

  • Using a reference variable with the dot operator to access a static method is really a syntax trick, the compiler will substitute the class name for the reference variable.
d.doStuff();

becomes

Dog.doStuff();
  • Static methods are NOT overridden; they are hidden.

Coupling and Cohesion (Objective 5.1)

  • Coupling refers to the degree to which one class knows about another class.

  • Classes are tightly coupled if they depend on the internal implementations of each other.
  • If the only knowledge that class One has about class Two, is what class Two has exposed through its interface, then class One and class Two are said to be loosely coupled.

  • Good encapsulation promotes loose coupling.
  • Poor encapsulation can allow tight coupling, because it’s possible to access unprotected variables directly from another class, which allows one class to become dependent on the implementation of the class containing those variables.

  • In the example below, class One and Two are loosely coupled. A change in class One would not affect class Two.
class One {  
  private int x;

  int getX() {
    return x;
  }  
}  

class Two {  
  private int y;

  public void setY(One o) {
    y = o.getX();  
  }
}
  • Cohesion refers to the degree in which a class has a single, well-defined purpose.
  • High cohesion is the desirable state of a class whose members support a single, well-focused purpose.
  • Encapsulation and high cohesion makes it easier to reuse classes.

Related Posts