Java Lesson 11: Objects and Classes

2
38

Introduction

A foundation of modern programming is Object-oriented programming. The Java architecture is based upon the object-oriented paradigm. All types, except for the primitive types, are objects, or as Java calls them, classes. In this lesson we will discuss the principles of object-oriented programming in general, how Java implements the object-oriented principles, and finally write a program step-by-step to demonstrate Java’s object-oriented language features.

Principles of Object-Oriented Programming

Data and Functions

Programs are comprised of two parts:

  1. Data fields that contain information.
  2. Functions that act upon the data fields.

Object-Oriented Programming claims that data is more important than functions, and are the primary consideration when developing an application. Objects are nouns (e.g., customer, payment, interest rate, pencil, dog, joystick) and the data for objects typically describe the objects or provide some detailed information about them. Functions are verbs (e.g., process, calculate, draw, bark, move) and are operations that the objects do or are performed on objects. Classes are the Java equivalent of objects. In other words, Java uses the term class when referring to an object. Methods are the Java equivalent of functions.

Defining Your Own Data Types

Early computer programming languages only had built-in data types (what Java calls primitive types) such as boolean, integer, and floating point.

Java and other modern object-oriented programming languages allow you to define your own data types (like records in the Pascal language or structures in the C language). These data types can be very complex and contain several data fields and also functions or methods.

Example of a C structure.
Example of a C structure.
Diagram of structure memory usage.
Diagram of structure memory usage.

 

 

Encapsulation

Encapsulation is the packaging of data fields and methods into a single component. The features of encapsulation are supported using classes in most object-oriented programming languages. It allows selective hiding of properties and methods in an object to protect the code from accidental corruption.[1]

Inheritance

Inheritance is when an object is based on another object. It is a mechanism for code reuse and to allow independent extensions of the original software.[2]

Polymorphism

A polymorphic type is a type whose operations can also be applied to values of some other type, or types.[3] A typical example are several methods all with the same name but which take a different type of argument or return a different type of result.

Summary of Object-Oriented Programming

Object-Oriented Programming is a key concept. The remaining sections will apply these concepts specifically to the Java language. Remember, classes are the Java equivalent of objects, and methods are the Java equivalent of functions. We will revisit these object-oriented principles again in the context of the Java language.

Java Classes

Classes are the Java equivalent of an object. In other words, Java uses the term class when referring to an object. We have used classes starting with our very first program and in every example program since then without even being aware of it, since every Java program executable is a class. Picture of an architect's blueprint for a house.Think of a class as a template or a blueprint which we use as a guide to create one or more objects. Just as a builder uses an architect’s blueprint to build one or many houses, a class definition can make one or more objects which are instances of the class. Thus, a class declaration is a template for creating objects (instances). After declaring a class you can use it to create as many instances of that type as you wish. You can use classes to create new types that you define. In other words, instead of being restricted to the types that Java provides (e.g., boolean, int, charfloat, etc.), you can define your own types to extend the Java’s functionality. And, once you create a class, you can reuse it over and over in the same or other programs. A class is a data type defined by the programmer and made up of two members:

  1. Instance Variables – These are “data fields.” Often they are int, double, or String, but they can be more complex data types such as arrays or other classes.
  2. Methods – These are functions or subroutines. They are actions that an instance of that class might do. For example, a dog barks and eats. A joystick moves.

So, using our blueprint analogy, doorWidth is a field, and doorOpen() is a function that a door does.

Hierarchical diagram of classes.
Figure 1

Classes are hierarchical, in that classes can inherit the characteristics of a parent class, and classes can have child classes which are subordinate to them. A parent class is called a superclass, and a child class is a subclass. In Figure 1, which shows the hierarchical relationship among several classes, we see that Class 2b has two subclasses; Class 3a and Class 3b. Furthermore, Class 2b is a subclass of Class 1b. All of the classes are ultimately subclasses of Object. A subclass inherits the properties of the superclass above it. Classes can be nested many levels deep. In Java, every class is required to inherit from another class. The Java specification defines the top-level superclass Object. All classes are ultimately inherited from the Object superclass, thus Object is the top-level class and the only one that does not have a superclass. Classes are made up of two things, instance variables and instance methods. Instance variables contain data or information. Instance methods are usually just called methods, and contain operations performed on the data.

Encapsulation

Good object-oriented programming practice recommends that each instance variable should only be accessed or modified by the methods defined for the class. Only the class’s methods should read or write the data belonging to an object. The methods encapsulate, or enclose, any operations which affect the data.

Inheritance

Inheritance is the concept that some data types (i.e., some classes) may overlap and have variables or methods in common. Consider the following diagram. Diagram of a hierarchy of Pets. The Pets class has two subclasses: Dogs and Hamsters. Dogs inherit all the members of the Pets class. Hamsters also inherit all the members of the Pets class. The class ShowDogs is a subclass of Dogs, and it inherits all the members of the Dogs class and the Pets class. Without inheritance we would have to explicitly define all the members in Dogs and ShowDogs. This causes more duplication and is error-prone. Dogs and Hamsters are subtypes of Pets. Conversely, Pets is a supertype of Dogs and Hamsters. We say that a dog is “a kind of” pet, and that a dog is a specific example of a pet.

Public vs. Private

Variables and methods can be public or private. Public means the object is visible anywhere in the program or even outside of the program. Private means that the object is only accessed by methods of the class. Variables are usually kept private to prevent inadvertent disclosure or modification outside of the defined methods.

Declaring a Class

The format to declare a class is:

Class declarations look like methods, but lack a return type and parentheses “()”. Let’s start by declaring a class called Pets, for an application about household pets.

Instance Variables

Classes store information in instance variables which are declared just like regular variables by specifying its type and an identifier name, in the format:

You can have as many variables as you need, of any type, including primitives, wrappers, arrays, Strings, or types defined in other classes. We will declare some instance variables which contain information (data) about pets. By doing so, all instances of type Pets will contain these instance variables, just like all houses made from our blueprint contain a kitchen.

Methods

If you want to do anything useful with the data contained in a class, you need to declare methods which act upon the instance variables. As we learned in the previous lesson, declare methods with a statement of the form:

Suppose we want to display the pet’s weight on the console. We can write a method to do this:

Polymorphism

Polymorphism is a kind of method overloading where the method which is called depends upon the type of the object or arguments. An example is the Java-provided Math.abs() method. Java actually defines four methods with the same name, each of which take an argument of either int, long, float, or double.

Overriding a method redefines an inherited method so it can do a slightly different operation. For example, if we have a method called calculateTotalPrice(), we might override it for products which are leased instead of purchased.

Constructors

Now that we have designed a “blueprint” for our objects using the class declaration, we must define a construction method which constructs new objects (variables) from the blueprint. After this is complete, another class (often in the main() method) can create an instance of the class by using the new operator. The construction method, called a constructor, has the same name as the class but it does not return a value. Typically constructors initialize variables at creation and get the variable ready for use. In some cases the constructor simply gives the instance variables default values. In other cases the constructors run lots of code and do complex computations. A constructor method looks very much like a static method, except that there is no return type specified in the declaration. The following constructor prepares a new Pets object for future use by initializing the instance variables:

The new Operator

As we learned in the lesson on Variables, to use a variable you must first declare it. The following declares a variable of type Pets:

As we did with arrays, we must use the new operator to allocate memory space for the variable.

It is permissible, and common practice, to combine both of these statements into a single statement of the form:

So, to declare and allocate space for a variable called dog, we write:

The statement above is telling Java that:

  1. You want to declare a new variable with the “Pets” type.
  2. The variable is named “dog“.
  3. You want to allocate memory space for the variable.
  4. The variable is initialized according to the Pets constructor, which sets all instance variables to an empty string or to zero.

Accessing Instance Variable Information

To access the data in an instance variable or method we use something known as dot notation, which is the name of the variable followed by a dot “.”, and then the name of the instance variable or method. This has the form:

To access the name member variable:     dog.name To access the printWeight method:    dog.printWeight() Here are some example statements:

More About Inheritance: The extends Keyword

Class inheritance is a powerful tool in object-oriented programming. Inheritance allows you to reuse an existing class and build upon it. Suppose you have developed the Pets class, and later discover a special need to enhance the program for Show Dogs. You could either start from the beginning, or you could build upon the Pets class. You can inherit the Pets class’s functionality without rewriting all the code. The extends keyword is the way that Java enables programmers to inherit from existing classes. The format to extend a class is:

So, to extend our Pets class, we will type:

Abstract Class

An abstract class is a class which cannot be instantiated. It only defines fields and methods but no objects are ever directly created from it. It is used as the superclass of other classes. Building upon our example, we could define the abstract class Animals and derive Pets from it.

Demonstration of Java’s Object-Oriented Language Features

Defining a Class

We begin with a program template – the minimal amount of code needed for a Java program. Enter (or copy and paste) this program into your computer, compile, and run it. There will be no output, but it should run.

We will define a class called Pets which will contain two pieces of information about each of our pets: the pet’s name  (a String) and it’s age (an int):

The enhanced program looks like this:

Next we will use the new operator to create an instance of Pets. The instance’s name is dog.

We access pet‘s two instance variables with dot notation. We can put values into the variables and later retrieve the values to display them on the console.

Enter, compile, and run the program as shown below.

Compile and execute Program11a.javaYou can create as many instances (objects) of Pets as you need. You can also perform computations with the variables and update them. Let’s create another pet object called pet2 and then add one year to pet2‘s age.

The program and it’s output now looks like this:

Compile and execution of Program11a.java Recall that classes can contain methods as well as instance variables. We will enhance and shorten our program by moving the output statements from main() to a method called talk() in the class definition.

The new talk() method is invoked (i.e., called) using dot notation: the variable name followed by a dot, then followed by the name of the method:

Note that we no longer need the System.out.println() statements in main(), because they moved to the talk() method. Edit your program to look like the following, compile, and run it. The console output looks the same as before even though the program changed. If your program won’t compile or your output looks different then compare your program to the code below and check carefully for typos.

Compile and execution of Program11a.java An important point to note is that when we call talk() on the object pet with the statement pet.talk(), the talk() method knows to use the instance variables associated with pet. When we call talk() on the object pet2 with the statement pet2.talk(), the talk() method knows to use the instance variables associated with the other object pet2.

Classes can be confusing at first, so at this point, I recommend you try writing your own program to become familiar with the Java syntax for creating and using classes. After a while it will not look so intimidating. Use your editor to create a new Java source file. For example, you could create Student.java, then define a class called Students, create two students (student1 and student2), and assign a name and age to each student. Finally, use a method to make the students talk.

Returning a Value from a Method

You may have noticed the talk() method has a return type of void, which means it executes but does not return a value to the caller. As we learned in the lesson on Methods we can optionally return a value of any type.

Assuming each year for a dog is equal to seven years for a human, let’s write a method to return a pet’s age in “dog years.”

The pet2 object calls the calcDogYears() method using dot notation with a statement like:

Our enhanced program is:

Compile and execution of Program11a.java

Getters and Setters

Previously in our main() method we used statements like the following to initialize instance variables:

Although convenient, doing so is not considered good programming practice because it violates the principles of encapsulation. Instead, we should access instance variables with Getters and Setters instead of reading and writing them directly. Getters and Setters are so common that many Integrated Development Environments (IDEs) automatically generate Getters and Setters for you.

Getters

A Getter is a method which returns a value of an instance variable. Since we have two instance variables, name and age, we will write two Getters.

You call a Getter method just like any other method; either by assigning the return value to a variable or using it within another method such as println(). An example of each is:

To demonstrate these concepts, copy Program11a.java to another file called Program11b.java, or in your editor do a Save As to save a new copy of the program to the filename Program11b.java. Add the Getter methods and call them from main(). Our new program and its output is:

Compile and execution of Program11b.java, which uses Getter methods.

Setters

Similarly, a Setter is a method which updates a value in an instance variable. Since we have two instance variables, name and age, we will write two Setters.

You call a Setter method just like any other method – by passing an argument containing the new values of the instance variables. Two examples of calling a Setter method are:

We will edit Program11b.java to add the Setter methods and call them from main(). Our new program and its output are shown below:

Compile and execution of Program11b.java with Setter methods.

Constructors

Constructors are special methods which run when you create a new instance (i.e., a new object) of your class. Constructors use the same name as that of the class, and can optionally take parameters, which usually contain values to initialize the instance variables of the new object. Here is a constructor for Pets which has no parameters and simply reports that it is running.

Like before, copy Program11b.java to another file called Program11c.java, or in your editor do a Save As to save a new copy of the program to the filename Program11c.java. Add the constructor method and call it from main(). We will also delete some unused code from main() so the program is less cluttered. Our new program and it’s output is:

Compile and execution of Program11c.java, with a constructor with zero parameters.

Looking at the output, notice that the program echos that it is running the constructor. The variables display as “null” and “0” because the constructor doesn’t initialize the name and age instance variables. This may be fine if you are going to assign values later.

Another option is to write a constructor for Pets which takes parameters for one or more instance variables.

As with the earlier constructor with no parameters, this constructor with parameters is called when declaring a new object, but it initializes the instance variables. To declare and instantiate a new object we use a statement like:

Add these lines to Program11c.java and compile and run it:

Compile and execution of Program11c.java, with constructor with two parameters.

A program can have many constructors with the same name but with a different number of parameters. Java automatically selects the proper constructor to run.

Summary

In this lesson we learned how Java incorporates object-oriented programming concepts into the language via classes, instance variables, methods, constructors, and the extends keyword to create a powerful means to encapsulate the behavior of the object into sophisticated programs. As you write more programs you will find yourself reusing classes which increase your productivity.

Next Lesson

Next we will move on to Exceptions.

Further Reading

  1. Gamma E., et al. Design Patterns: Elements of Reusable Object-Oriented Software. 1994.

References

  1. Encapsulation (object-oriented programming). Wikipedia.
  2. Inheritance (object-oriented programming). Wikipedia.
  3. Polymorphism (computer science). Wikipedia.

 

2 COMMENTS

  1. […] Methods are common in Classes, and that’s the subject of our next tutorial on Objects and Classes. […]

  2. […] In Java, a string is an object of type String. Objects are complex and will be covered in a later Objects and Classes lesson, but because strings are so commonly used we will briefly discuss them […]

Leave a Reply