Object Oriented Concepts in Java – Part 1

Tweet

A strong grounding in Java allows a developer to do more in less time than he or she could do using any other single programming language out there today. With Java, you can build complete applications featuring everything from accelerated 3D graphics and other multimedia features to strong cryptography and network connectivity. On the Web, Java can be used on the client side to create applets (once over-hyped but still useful, these small programs can run right inside a Web page with the full power that Java has to offer), and on the server side to create dynamic Web pages using Servlets and JavaServer Pages, which you’ll be seeing a lot more of later in this series.

To take full advantage of the Java language, you need a good appreciation of object oriented programming concepts. This article is the first of two parts that will teach you everything you need to know about object oriented programming in Java. Buckle your seatbelts and get set for a wild ride, because not only are these some of the most difficult concepts of the Java language to grasp, but they’re also some of the most exciting!

This article is the third in a series of articles aimed at teaching you the Java language with an eye towards the development of dynamic Web sites using Java technologies on the server side, and picks up right where the previous article, Java Language Basics, left off. If you have not read the two articles that came before this one, I would suggest going back and starting from the beginning, because subjects like compiling and running Java programs and basic features of the language such as variables and control structures are assumed knowledge from here on in!

But First, Some Jargon

As I have said before, writing programs (or Web pages) in Java is all about constructing a web of interrelated software components that work together to get the job done. These components are called Java Objects.

There are many different kinds of Java Objects, and in fact a big part of programming in Java is creating your own types of Objects. To create a new type of object that you can use in your Java programs, you have to provide a blueprint of sorts that Java will use to create new Objects of this type. This blueprint is called a Java class.

Fig. 1: Instantiating two Trees from the Tree classLet’s look at a conceptual example to help these ideas take hold. Say your program needed to keep track of a group of trees in a forest; specifically, say it needed to keep track of the heights of these trees. Fig. 1 shows an example of the class and objects that you might create as a Java programmer working on this program.

On the left we have a class called Tree. This class defines a type of Java Object — a Tree — that will serve as the blueprint from which all Tree Objects will be created. The class itself is not a Tree; it is merely a description of what a Tree is, or what all Trees have in common. In this example, our Tree class indicates that all Trees have a property called ‘height’.

On the right, we have two actual Tree Objects. These are Trees, and they were created based on the blueprint provided by the Tree class. These Objects are said to be instances of the Tree class, and the process of creating them is called instantiation. Thus, we can say that by instantiating the Tree class twice, we have created two instances of the Tree class, two Objects based on the Tree class, or just two Trees. Notice that in creating these Objects we have assigned a value to their height property. The first Tree is 2 meters high and the second is 5 meters high. Although the values of these properties differ, this does not change the fact that both objects are Trees. They are simply Trees with different heights.

Classes don’t only define properties of Objects; they also define operations that may be performed by those Objects. Such operations are called methods in object-oriented languages like Java. Continuing with our Tree example, we could define a method called ‘grow’ in the Tree class. The result of this would be that every Tree Object would then be able to perform the grow operation as defined in the class. For instance, performing the grow operation on a Tree might increase its height property by one meter.

A Java Tree

For our first foray into object-oriented programming, I propose to implement the Tree class discussed above in Java and then write a program that uses it to instantiate a couple of Trees and make them grow a little.

Open your text editor of choice and create a new text file called Tree.java. This file will contain the definition of the Tree class, and it is important that the name of the file match the name of the class defined within it right down to the case (thus, tree.java will not do). Type the following (as usual, the line numbers are provided for your convenience only, and should not be typed as part of the code):

1  /**  
2   * Tree.java  
3   * A simple Java class.  
4   */  
5  
6  class Tree {

We begin by announcing our intention to create a class called Tree. Note again that I am observing the convention of spelling class names with a capital letter.

7    public int height = 0;  
8

Get Your Copy of Kevin Yanks Book NOW!Aside from the word public at the start of this line, this looks just like a standard variable declaration. As it would seem, we are declaring an integer variable called height and assigning it a value of zero. Again, it is a matter of convention that variable names are not capitalized. Variables declared in this way just inside a class definition (and not inside the definition of a method, such as main, as we saw in previous examples) become properties for Objects of the class. Thus, this line says that every Object of class Tree will have a property called height that will contain an integer value, and that the initial value of the height property will be zero. The word public indicates that any code in your Java program can view and modify the value in this property. In part two of this article, we’ll see techniques for protecting data stored in an Object’s properties, but for now this will suffice.

That’s actually all there is to creating a Tree class that will keep track of its height; however, to make this example at least a little interesting, we’ll also implement the grow method that I mentioned in the previous section. It begins with the following:

9     /**  
10     * Grows this tree by 1 meter  
11     */  
12    public void grow() {

Let me explain this line one word at a time. The word public indicates that the grow method (operation) is publicly available, meaning that it may be triggered by code anywhere in the Java program. Methods may also be private or protected, and I’ll explain the meaning of each of these options later on. The word void indicates that this method will not return a value. Later on we’ll see how to create methods that produce some value as an outcome, and for such methods we would replace void with the type of value to be produced (e.g. int).

Finally, the word grow is the actual name of the method that is to be created. Note that I am observing the convention of spelling method names starting with a lowercase letter. The empty parentheses following this word indicate to Java that it is a method we are declaring (as opposed to another property, like height above). Later on we’ll see cases where the parentheses are not empty. Finally, the opening brace signifies the start of the block of code that will be executed each time the grow method of a Tree Object is triggered.

13      height = height + 1;

This operation happens to be a simple one. It takes the value of the height property and adds one to it, storing the result back into the height property. Note that we did not need to declare height as a variable in this method, since it has already been declared as a property of the Object on line 7 above. If we did declare height as a variable in this method, Java would treat it as a separate variable created anew every time the method was run, and our class would no longer function as expected (try it later if you’re curious).

14   }  
15 }

The closing brace on line 14 marks the end of the grow method, while that on line 15 marks the end of the Tree class. After typing all this in, save the file and then compile it as you would a Java program:

C:JavaTree> javac Tree.java

As you might expect, a file called Tree.class is created in the same directory. This is the compiled definition of the Tree class. Any Java program that you try to create a Tree in will look for this file to contain the blueprint of the Object to be created. In fact, that’s our next step.

Using the Tree Class

Okay, so now you have the blueprint of a tree. Big deal, right? Where things get interesting is when you use that blueprint to create and manipulate Tree Objects in a Java program. Create a new file in your text editor called PlantTrees.java and follow along as I talk you through writing such a program.

1  /**   
2   * PlantTrees.java  
3   * A simple Java program to try out the Tree class.  
4   */  
5  
6  class PlantTrees {

Yes, that’s right — our program is itself a Java class. When you run a Java program you are actually just providing Java with the name of a class containing a special method called main that will be executed automatically to kick off your program. Later in this series we’ll see some ways to take advantage of the fact that Java programs are actually classes, but for now just let this serve as a reminder of how every part of a Java program belongs to the web of classes and Objects, even the program itself.

7    public static void main(String[] args) {

This is the start of that special main method that is executed automatically to kick off your Java program. If you look closely you’ll see some resemblance between this line and the declaration of the grow method in the Tree class. Both are public methods that return nothing, as evidenced by the words public and void appearing before the name of the method. As for the meaning of static and the code appearing inside the parentheses, you’ll have to wait a little longer for the answers to those mysteries.

8      System.out.println("Let's plant some trees!");

The program starts by printing out a little message explaining what the program will do. This should be nothing new to you.

9      // Create a new tree   
10     Tree tree1 = new Tree();

As the comment on line 9 suggests, line 10 achieves the feat of creating a new Tree out of thin air. This is a really important line; so let me explain it in depth. The line begins by declaring the class (type) of Object to be created (in this case, Tree). We then give a name to our new Tree (in this case, tree1). This is in fact identical to declaring a new variable by specifying the type of data it will contain followed by the name of the variable (e.g. int roomTemp).

The rest of the line is where the real magic happens. The word new is a special Java keyword that triggers the instantiation of a new Object. After new comes the name of the class to be instantiated, followed by a pair of parentheses (again, in more complex cases that we shall see later, these parentheses may not be empty).
In brief, this line says, "create a new variable of type Tree called tree1, and assign it a value of a new Tree." So in fact this line isn’t just creating a Tree, it’s also creating a new variable to store it in. Don’t worry if this distinction is a little hazy for you at this point; later examples will serve to clarify these concepts significantly.

Now that we’ve created a tree, let’s do something with it:

11     // Print out the tree's height   
12     System.out.println("I've created a tree with a height of " +  
13                        tree1.height + " meter(s).");

Again this should not be too unfamiliar to you. This line simply prints out a line of text like any other, except that part of the line of text takes its value from the height of the tree1 variable (tree1.height). If you simply typed height instead of tree1.height, Java would think you were referring either to a variable called height declared in this method, or a property of the PlantTrees class (the program itself). Unable to find either of these, Java would print out an error message when you tried to compile the program. In order to tell Java that you are referring to the height property of the Tree in tree1, you need to tack on tree1 followed by the dot operator (.).

The dot operator may be thought of sort of like the Java way of saying "belonging to" when you read the expression backwards. Thus, tree1.height should be read as "height belonging to tree1." Since Trees are created with a height of zero, lines 12 and 13 should print out "I’ve created a tree with a height of 0 meter(s)."

Calling (or triggering) methods belonging to an Object is accomplished in a similar way:

14     tree1.grow();

This line calls the grow method belonging to the Tree in tree1, causing it to grow by a meter. Again, the set of parentheses indicate that it is a method we are referring to, not a property. So after this line if we print out the height of tree1 again…

15     System.out.println("After a bit of growth, it is now up to " +   
16                        tree1.height + " meter(s) tall.");

This line will print out "After a bit of growth, it is now up to 1 meter(s) tall."

To show that each Tree has its own height value that is independent of those of the other Trees, we’ll polish off this example by creating a couple more Trees and having them grow by different amounts:

17     Tree tree2 = new Tree();   
18     Tree tree3 = new Tree();  
19     tree2.grow();  
20     tree3.grow();  
21     tree2.grow();  
22     tree3.grow();  
23     tree2.grow();  
24     System.out.println("Here are the final heights:");  
25     System.out.println(" tree1: " + tree1.height + "m");  
26     System.out.println(" tree2: " + tree2.height + "m");  
27     System.out.println(" tree3: " + tree3.height + "m");  
28   }  
29 }

Save and compile this program:

C:JavaTree> javac PlantTrees.java

Make sure that PlantTrees.class is in the same directory as Tree.class, then run your PlantTrees program:

C:JavaTree> java PlantTrees   
Let's plant some trees!  
I've created a tree with a height of 0 meter(s).  
After a bit of growth, it is now up to 1 meter(s) tall.  
Here are the final heights:  
tree1: 1m  
tree2: 3m  
tree3: 2m

Inheritance

One of the strengths of object-oriented programming is inheritance. This feature allows you to create a new class that is based on an old class. Let’s say your program also needed to keep track of coconut trees, so you would need a new class called CoconutTree that kept track of the number of coconuts in each tree. You could write the CoconutTree class from scratch, copying all the code from the Tree class that is responsible for tracking the height of the tree and allowing the tree to grow, but for more complex classes that could involve a lot of duplicated code. What would happen if you later decided that you wanted your Trees to have non-integer heights (like 1.5 meters)? You would have to adjust the code in both classes!

Inheritance allows you to define your CoconutTree class as a subclass of the Tree class, such that it inherits the properties and methods of that class in addition to its own. To see what I mean, here’s the code for CoconutTree:

1  /**    
2   * CoconutTree.java    
3   * A more complex kind of tree.    
4   */    
5    
6  class CoconutTree extends Tree {    
7    public int numNuts = 0; // Number of coconuts    
8    
9    public void growNut() {    
10     numNuts = numNuts + 1;    
11   }    
12    
13   public void pickNut() {    
14     numNuts = numNuts – 1;    
15   }    
16 }

The words extends Tree on line 6 endow the CoconutTree class with a height property and a grow method in addition to the numNuts property and the growNut and pickNut methods that are declared explicitly for the class.

By building up a hierarchical structure of classes with multiple levels of inheritance, you can create powerful models of complex Objects with little or no duplication of code (which makes for less typing and easy maintenance). We’ll see more examples of the power of inheritance later in this series.

Copying Objects

There is a difference between the way Java handles basic data like integer and Boolean values and the way Java handles Objects. Consider the following code fragments:

1  int a = 5;     
2  int b = a;    
3  b = b + 1;    
4  System.out.println("a=" + a + " b=" + b);    
5      
6  Tree t1 = new Tree();    
7  t1.height = 5;    
8  Tree t2 = t1;    
9  t2.height = t2.height + 1;    
10 System.out.println("t1.height=" + t1.height +    
11                    " t2.height=" + t2.height);

Now, in both cases we are creating two variables, assigning the value of the first to the second, then changing the value of the second; however, the output of the above code is the following:

a=5 b=6
t1.height=6 t2.height=6

In the case of the integers, changing the value of the second variable did not affect the value stored in the first variable. In the case of the Trees, however, changing the height of the Tree stored in t2 appears of have also changed the height of the Tree stored in t1! Depending on how you think, one or the other of these outcomes probably seems to make sense, but either way there seems to be something very different going on in each of these two cases.

In the first case, which makes sense to most people at first glance, when the value of a is stored in b on line 2 Java actually creates a copy of the integer value 5 to store in b. So in fact we are dealing with two different copies of the number 5: one stored in a, and one stored in b.

The story is very different in the case of Objects. On line 8 when the value of t1 is stored in t2 we are not actually creating a copy of the Tree Object to store in t2! Instead, t2 is made to reference the very same instance of the Tree class in memory. In other words, t2 and t1 are made to point to the exact same Tree! This is why changing the height of t2 also changes the height of t1t2 and t1 aren’t separate like a and b were.

So, what to do if you want to create an actual copy of an object? Well, there are several options. The first is to actually create two trees and then just copy the height of one into the height of the other:

1  Tree t1 = new Tree();     
2  t1.height = 5;    
3  Tree t2 = new Tree();    
4  t2.height = t1.height;    
5  t2.height = t2.height + 1;    
6  System.out.println("t1.height=" + t1.height +    
7                    " t2.height=" + t2.height);

This will produce the desired output of "t1.height=5 t2.height=6", because the heights of the trees are in fact basic data values (integers), not Objects, so setting the value of t2‘s height to equal t1‘s height creates a separate copy of the integer value for t2 to use.

Another option is to implement a special method in your class for creating copies. This method should supply a direct copy of the object for separate use. Here’s what Tree class would look like with the copy method added:

1  class Tree {     
2    public int height = 0;    
3    public void grow() {    
4      height = height + 1;    
5    }    
6    public Tree copy() {    
7      Tree copy = new Tree();    
8      copy.height = height;    
9      return copy;    
10   }    
11 }

And here’s how you’d use this new method:

1  Tree t1 = new Tree();     
2  t1.height = 5;    
3  Tree t2 = t1.copy(); // Create a copy of t1    
4  t2.height = t2.height + 1;    
5  System.out.println("t1.height=" + t1.height +    
6                    " t2.height=" + t2.height);

Again, this will produce the desired result.

Comparing Objects

At the end of the previous article in this series, I promised that this article would help to demystify how Strings work in Java. Specifically, I promised to explain how two Strings could be compared for equality in a Java program. Well, to those of you who have been waiting two weeks by your email Inbox for the answer, I would like to say, "Get a life."

Seriously though, the method of comparing for equality is another important difference between basic data values and Objects in Java. Consider the following code fragment:

1  int a = 5;      
2  int b = 5;      
3  if (a == b) System.out.println("a and b are equal!");      
4        
5  Tree t1 = new Tree();      
6  t1.height = 5;      
7  Tree t2 = new Tree();      
8  t2.height = 5;      
9  if (t1 == t2) System.out.println("t1 and t2 are equal!");

Once again, this code exposes different behaviour for basic data values and Objects. While the comparison on line 3 will find that a and b are indeed equal, the fact that the heights of the two Trees t1 and t2 are equal on line 9 is not enough for t1 and t2 themselves to be found equal. In fact, for two variables containing Objects to be found equal using the == operator as above, the two variables must both refer to the exact same object! The following code, for example, will find t1 and t2 to be equal:

1  Tree t1 = new Tree();      
2  t1.height = 5;      
3  Tree t2 = t1; // Both t1 and t2 refer to the same Tree      
4  if (t1 == t2) System.out.println("t1 and t2 are equal!");

This form of comparison for equality is not especially useful though, is it?

Since Java uses a built-in class called String to represent text strings, the same problem occurs when you try to compare two strings for equality. For example, you might want to check if a value that was entered by the user is the correct password to obtain access to some feature of the program. Here’s what you might be tempted to do:

1  if (enteredPassword == "secret") {      
2    // Allow access      
3  } else {      
4    System.out.println("Access denied.");      
5  }

The problem, however, is that even if the variable enteredPassword did contain the String "secret", it would not be the same instance of the String class as the value it was being compared to, so the comparison would always evaluate to false, and access would always be denied.

Get Your Copy of Kevin Yanks Book NOW!The solution is to use the equals method provided by the String class instead of the == operator. This method lets you compare one string with another to check if they contain the same text. Here’s how to use the equals method in the above example:

1  if ( enteredPassword.equals("secret") ) {      
2    // Allow access      
3  } else {      
4    System.out.println("Access denied.");      
5  }

The equals method is different from other methods we’ve seen so far in that it takes a parameter. Instead of following the name of the method (equals) with an empty pair of parentheses, the parameter (in the case of equals, the String to compare to for equality) is typed between the parentheses. The equals method also results in a Boolean value (either true or false) depending on whether the Strings are equal or not. Don’t worry too much about the details of these special features; we’ll be seeing more examples of methods that take parameters and return values in part two of this article.

All of Java’s built-in classes support the equals method to check for equality, and the documentation for each of these classes explains under which conditions two Objects of the class are considered equal. To enable comparisons for equality for Objects of your own classes, you should implement your own equals methods in those classes. I’ll show you how to write an equals method for the Tree and CoconutTree classes in part two of this article.

To see a list of all the methods supported by String Objects, check out the Java API documentation for this class. Not all of it will make complete sense to you yet at this stage, but a lot of it will. For instance, look up the equalsIgnoreCase method, which compares two Strings for equality while ignoring case.

Summary and Resources for Further Reading

In this article I took you on a whirlwind tour of the main concepts of object oriented programming that figure prominently in the Java programming language. These concepts are the elements of the language that set it apart from other languages, making Java at once a challenge to master as well as a refreshing step up in power and convenience when compared to other languages.

In Part Two of this article, we’ll explore some of the more advanced features of classes, Objects, properties, and methods, and even discuss some principles of good class design.

In the meantime, you might like to take a little time to explore some of the built-in classes that Java provides. The online Java 2 Platform API Specification gives you the complete list, along with descriptions of all properties and methods of every single class. Try not to be intimidated by the sheer number of classes available; even I don’t know them all by heart!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments