1. But that's not all.

Suppose the Cow class has a printAll() method that calls two other methods. Then the code will work like this:

Code Description
class Cow
{
   public void printAll()
   {
      printColor();
      printName();
   }

   public void printColor ()
   {
      System.out.println("I'm a white whale");
   }

   public void printName()
   {
      System.out.println("I'm a cow");
   }
}

class Whale extends Cow
{
   public void printName()
   {
      System.out.println("I'm a whale");
   }
}
public static void main(String[] args)
{
   Whale whale = new Whale ();
   whale.printAll();
}
The screen output will be:
I'm a white whale
I'm a whale

Note that when the printAll() method in the Cow class is called on a Whale object, the printName method of the Whale class is used, not the one in the Cow method.

The main thing isn't the class the method is written in, but rather the type (class) of the object on which the method is called.

Only non-static methods can be inherited and overridden. Static methods are not inherited and therefore cannot be overridden.

Here's what the Whale class looks like after applying inheritance and method overriding:

class Whale
{
   public void printAll()
   {
      printColor();
      printName();
   }

   public void printColor()
   {
      System.out.println("I'm a white whale");
   }

   public void printName()
   {
      System.out.println("I'm a whale");
   }
}
Here's what the Whale class looks like after applying inheritance and method overriding: We don't know about any old printName method.

2. Typecasting

There is an even more interesting point here. Because a class inherits all the methods and data of its parent class, a reference to an object of the child class can to stored in (assigned to) variables whose type is the same as the parent class (and the parent's parent, etc. — all the way up to the Object class). Example:

Code Description
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printColor();
}
The screen output will be:
I'm a white whale
public static void main(String[] args)
{
   Cow cow = new Whale();
   cow.printColor();
}
The screen output will be:
I'm a white whale
public static void main(String[] args)
{
   Object o = new Whale();
   System.out.println(o.toString());
}
The screen output will be:
Whale@da435a.

The toString() method is inherited from the Object class

This is a very valuable property: a little later you will understand how to use it in practice.


3. Calling a method on an object

When a method is called on a variable, the method is actually called on an object. This mechanism is called dynamic method dispatch.

Here's how it looks:

Code Description
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printName();
}
The screen output will be:
I'm a whale
public static void main(String[] args)
{
   Cow cow = new Whale();
   cow.printName();
}
The screen output will be:
I'm a whale

Note that the specific implementation of the printName() method that gets called — the one in the Cow or the one in the Whale class — is not determined by the variable's type, but by the type of the object that the variable refers to.

The Cow variable stores a reference to a Whale object, and the printName() method defined in the Whale class is what is called.

This is not very obvious. Remember the main rule:

The set of methods available to be called on a variable is determined by the variable's type. And the specific method implementation that gets called is determined by the type/class of the object referred to by the variable.

You'll encounter this all the time, so the sooner you remember this, the better.