From this point on we’re going to be dealing with a lot of commonly-used Java classes and interfaces. It’s now time to discuss the nature of Java classes, beyond the mere fact that they’re repositories of data (fields) and operations (methods).

Recall that in object oriented programming theory, a class is intended to model some kind of object in the real world. To take the classic example, here’s the class every Java course is obligated (I think there’s a Federal statute about this) to use as an illustration–animals and how they speak:

public class Horse {
    public String speak() {
        return "Hello, I'm Mr. Ed";
    }
}

Encapsulation

Our Horse class illustrates one of the fundamentals of object oriented programming. Encapsulation is the hiding of data and processes to the world outside the class. (If you had a class called Vegas, what happens in Vegas literally stays in Vegas.) Encapsulation means that outside the Horse class, it’s known that a Horse can speak–but not how a Horse speaks.

Consider driving a car. You depress the accelerator and hundreds of operations occur: cylinder intake and exhaust ports are opened and shut, pistons rise and fall, spark plugs fire–all precisely timed and repeating thousands of times. But all you care about is that the drive wheels spin faster. That in-between stuff, fortunately, has been encapsulated.

We would use our Horse class like this:

Horse mrEd = new Horse();
String speech = mrEd.speak();

Inheritance

Now consider that a Horse is (of course, of course) just one kind of animal, and shares certain attributes and abilities with other animals. So now we define a second class called Animal, and relate it to Horse thus:

public abstract class Animal {
    public String speak() {
        return "No comment";
    }
}

public final class Horse extends Animal {
    public String speak() {
        super.speak();
        return "Hello, I'm Mr. Ed";
    }
}

There’s a lot to unpack here, so let’s begin.

First off, notice that Horse now uses the phrase “extends Animal“. This indicates that Horse is a subclass of Animal, which is a superclass, and can use whatever fields and methods Animal allows it to–i.e., any that aren’t marked private. (It’s assumed that Animal and Horse are in the same package so Horse can inherit items with protected or default access.)

This is the second fundamental concept of object oriented programming: Inheritance, the ability of subclasses like Horse to inherit artifacts from superclasses like Animal. Horse inherits the method speak() from Animal.

Notice that the speak() method differs between Animal and Horse. We say that the method speak() in Horse overrides the method in Animal. Overriding methods in a superclass is usually optional; if Horse did not override speak(), then calling speak() on an instance of Horse would return “No comment”, just as it does for the superclass Animal. However, since Horse overrides the method (it has the same name and number and types of arguments), its version of speak() is executed instead.

super

speak() in Horse returns “Hello, I’m Mr. Ed” instead of “No comment”. But before that, it has this line (which is here for illustration only):

super.speak();

super qualifies instance methods or variables to use the versions defined in the superclass, and this line calls the speak() method in Animal before returning “Hello, I’m Mr. Ed” in place of the value returned by the superclass. And a superclass method needn’t be called only from the same-named method in the subclass; super.speak() can be called from any instance method in a subclass of Animal.

this

Conversely, this applies to the current instance, whether used from within a superclass or subclass–even if the class is abstract. this is frequently used in constructors:

public class ThisIsMyClass {
    private String nameOfMyInstance;

    public ThisIsMyClass(String nameOfMyInstance) {
        this.nameOfMyInstance = nameOfMyInstance;
    }
}

Abstract Classes

Now please notice that Animal has the attribute abstract. This means that you cannot instantiate (i.e., create an instance of) Animal. This statement is illegal:

Animal myAnimal = new Animal();

You can only create instances of concrete classes (classes without the “abstract” attribute).

Abstract Methods

Since Animal is abstract, in the real world we probably would have defined the speak() method in Animal this way and wouldn’t call it from Horse:

public abstract String speak();

An abstract method can only appear in an abstract class. It has no body, but in ensures that the method can be called on any instance of one of the abstract class’s concrete subclasses–i.e., every Animal can speak():

private Animal mrEd = new Horse();
mrEd.speak();

Every concrete subclass of Animal must override any abstract methods in their superclasses, and provide a body for them.

An abstract method isn’t permitted to have any method body; it can’t do anything on its own. But thanks to polymorphism, it makes sense to call even abstract methods. Keep reading.

Polymorphism

Different animals, of course, talk in different ways. Let’s add one more.

public final class Goat extends Animal implements MilkGivers {
	public String speak() {
        return "Something ba-a-a-ad is happening in Oz";
    }
}

(MilkGivers is an interface, and we’ll discuss interfaces shortly.)

And let’s create a driver class that uses all three:

public class Barnyard {

    public static void main(String[] args) {
	Horse mrEd = new Horse();
	Goat drDillamond = new Goat();

	String mrEdSpeech = callSpeak(mrEd);
	String drDillamondSpeech = callSpeak(drDillamond);
		
	System.out.println(mrEdSpeech);
	System.out.println(drDillamondSpeech);
    }

    private static String callSpeak(Animal speaker) {
	return speaker.speak();
    }
}

Now, maybe you’ve noticed that we initialized a Horse and a Goat this way:

Horse mrEd = new Horse();
Goat drDillamond = new Goat();

… and passed them to callSpeak(), which accepts an Animal as an argument. This works perfectly well because Horses and Goats, as subclasses of Animal, are also Animals themselves.

But what does the output look like?

Hello, I'm Mr. Ed
Something ba-a-a-ad is happening in Oz

What happened? When the speak() method is called, each Animal executes speak() in its own way: as Horses do for mrEd because mrEd is a Horse, and as Goats do for drDillamond because drDillamond is a Goat–even though all the callSpeak() method can tell is that they’re Animals.

This behavior that differs from subclass to subclass is polymorphism (from the Greek words for “many shapes”): the “shape-shifting” done by different subclasses of a given superclass.

Deciding on the Declared Class

When you instantiate a class, you have three choices:

  • Declare it as an instance of the instantiated class: Goat myGoat = new Goat();
  • Declare it as an instance of a superclass: AnimalmyGoat = new Goat();
  • Declare it as an instance of an interface that the class implements: MilkGiversmyGoat = new Goat();
    (We discuss interfaces later on in this lesson.)

In most cases, the first way works just fine. You can pass a Goat anywhere an Animal or a MilkGivers is needed.

Goat myGoat = new Goat;
myGoat.callSpeak();
...
public String callSpeak(Animal myAnimal) {...}

But you can’t go the other way around…

Animal myGoat = new Goat;
myGoat.callSpeak();
...
public String callSpeak(Goat myAnimal) {...}

… because not every Animal is a Goat.

However, there are times when instead of using new to instantiate an instance, you call a method that returns an instance to you. And you can’t always tell what concrete class the instance belongs to. So you code something like this example from Lesson 22:

Connection conn = DriverManager.getConnection(url, user, password);

In this example, getConnection() returns an instance of an implementation of the Connection interface.

The Grand-Daddy of All: the Object Class

Although your code won’t mention it explicitly, every class extends one more class: java.lang.Object.

Object, and thus every class in the system, has some methods you should know about.

Method inherited
from Object
What it does
equals(Object obj)Returns boolean true if this is “equal to” obj. What does it mean for two objects to be “equal?” Sometimes it’s obvious: one Integer is “equal to” another Integer if they’re of the same class and hold the same integer value.

But how is one Chicken “equal to” another? That’s totally up to the developer, and it’s not usually restricted to they’re being one and the same instance, like

chicken1 == chicken2.

More likely it’s something like the id number of chicken1 is the same as the id number of chicken2.
getClass()Returns the Class to which the object belongs. Yes, Class is itself a Class, which has a lot of useful methods of its own.
hashCode()Returns an integer which is a hash code of the object’s data. Again, this is up to the developer.
wait()
wait(long timeout)
wait(long timeout, int nanos)
These are all used in multithreading applications. They cause the current thread to wait until another thread issues a notify() or notifyAll() on this object. In the latter two cases, the wait also ends when the specified amount of time elapses. We’ll explain all this in the lesson on Multithreading.
notify()
notifyAll
Wakes up a single thread (notify) or all threads (notifyAll) waiting for this object.
toString()Returns a String representation of the object intended to be meaningful to a human. What gets returned is, again, up to the developer.
The default is to return a String whose value is:

getClass().getName() + '@' + Integer.toHexString(hashCode())

Thus Oracle recommends that every class implement an override of toString() to return something meaningful.

Putting it together

Say you have an abstract superclass called Dictionary, and under it two concrete subclasses: OxfordEnglishDictionary and DictionaryDotCom. Either one might be instantiated as a Dictionary:

Dictionary oed = new OxfordEnglishDictionary();
Dictionary dcom = new DictionaryDotCom();

And suppose Dictionary has a public abstract method called lookup(), inherited by the subclasses, which returns the meaning of a word. Because lookup() is abstract, each of the two concrete classes (OxfordEnglishDictionary and DictionaryDotCom) are required to provide a non-abstract override for it.

In DictionaryDotCom, the lookup() method finds the meaning of the word in a local database and returns it to the caller.

But lookup() in OxfordEnglishDictionary sends the word to a librarian in a musty hall at Oxford University in England. The librarian reads the email and looks up the word in a massive copy of the OED, then laboriously transcribes into an email which they send back to our computer. Finally, lookup() returns the text of the email to the caller.

All this activity is hidden from the caller (encapsulation) and transparently works differently for each of the two subclasses (polymorphism).

And there you have it.

Exercise

In this exercise, we’ll create a number of different classes of Temperature.

Start with an abstract class called Temperature. It will have a number of subclasses:

  • TemperatureFahrenheit
  • TemperatureCelsius
  • TemperatureKelvin, a subclass of TemperatureCelsius

The Temperature class will hold a double variable representing a temperature, and each of the subclasses will implement a means for accepting a double value and storing it. This value will be Celsius degrees in the TemperatureCelsius and TemperatureKelvin classes (not that you’d do this in the real world, but we want to illustrate using a sub-subclass), and Fahrenheit in TemperatureFahrenheit. (By the way, try the Source/Generate Constructor using Fields menu item in Eclipse when writing the Temperature class. We’ll discuss constructors later, but once you see the generated code, the IDE will ensure that when you instantiate one of the objects, you provide a value. Passing the value to a constructor is a great way to create an immutable object.)

Each class should also implement a getFahrenheit() method, a getCelsius() method, and a getKelvin() method to return the stored value converted to Fahrenheit, Celsius, and Kelvin respectively. Ignore for now the fact that conversions may not be precise.

Also write a TemperatureDriver class to create instances of the other three, provide Long values for them, and try converting from one scale to another using the three methods described above.

The formula to convert Fahrenheit to Celsius is:

c = (f - 32) * 5 / 9

The formula to convert Celsius to Fahrenheit is:

f = (c * 9 / 5) + 32

The formula to convert Celsius to Kelvin is:

k = c + 273.15

Download a sample solution here. Unlike the previous exercises, we urge you to examine the code in this one to get a feel for how classes and subclasses interact.

What You Need to Know

  • Classes usually (but not always) model the behaviors of things in the real world.
  • Superclasses define data and operations common to one or more subclasses.
  • Classes have three important attributes:
    • Encapsulation–the hiding of data and operations from other classes.
    • Inheritance–the ability of subclasses to use data declarations and method bodies from their superclasses.
    • Polymorphism–the ability of a method to behave differently for different subclasses of any given superclass.
  • The keyword extends indicates that a class is a subclass and of what superclass.
  • Subclasses can also be extended.
  • Methods defined in superclasses can be (but don’t have to be) overridden by methods in subclasses.
  • Abstract classes are not meant to be instantiated; instead, they provide data elements and methods common to their concrete subclasseses.
  • Abstract classes can have abstract methods, which are fleshed out by non-abstract versions in subclasses (whether abstract or concrete).
  • Without the need to declare it, every class extends the Object class, and thus inherits the methods of Object, including equals(), hashCode(), and toString().
  • The word super used in an instance method of a subclass refers to an artifact described in the superclass of the current instance of the class; super not used as a qualifier refers to a constructor of the superclass. For example, super.speak() in class Horse would refer to the method speak() as defined in its superclass, Animal.
  • The word this is used in an instance method to refer to the current instance.

Next: Internal Classes