JavaTM Programming Language Basics, Part 2
Lesson 8: Object-Oriented Programming
[<<BACK] [CONTENTS] [NEXT>>]
You have probably heard a lot of talk about object-oriented programming.
And, if the JavaTM programming language is your first experience with
an object-oriented language, you are probably wondering what all the talk
is about.
You already know a little about object-oriented programming because
after working the example programs in
Java Programming Language Basics, Part 1
and
Part 2,
you are somewhat familiar with the object-oriented concepts of class, object,
instance, and inheritance plus the access levels public
and
private
.
But mostly, you have been doing object-oriented
programming without really thinking about it.
And that is one of the great things about the Java programming language. It is
inherently object oriented.
To help you gain a deeper understanding of object-oriented programming
and its benefits, this lesson presents a very brief overview of
object-oriented concepts and terminology as they relate to some of the
example code presented in this tutorial.
Object-Oriented Programming Defined
Object-oriented programming is a method of programming based on
a hierarchy of classes, and well-defined and cooperating objects.
Classes
A class is a structure that defines the data and
the methods to work on that data. When you write programs in the Java
language, all program data is wrapped in a class, whether it is
a class you write or a class you use from the Java platform API libraries.
The ExampleProgram
class from the simple program in the first
lesson of Part 1 is a programmer-written class that uses the
java.lang.System
class from the Java platform
API libraries to print a character string to the command line.
class ExampleProgram {
public static void main(String[] args){
System.out.println("I'm a simple Program");
}
}
Classes in the Java platform API libraries define a set of objects that share
a common structure and behavior. The java.lang.System
class used in the example defines such things as standard input, output,
and error streams, and access to system properties. In contrast, the
java.lang.String
class defines character strings.
In the example, you do not see an explicit use of the String
class, but in the Java language, a character string can be used anywhere
a method expects to receive a String
object. During execution,
the Java platform creates a String
object from the character
string passed to the System.out.println
call, but your
program cannot call any of the String
class methods because
it did not instantiate the String
object.
If you want access to the String
methods, you can
rewrite the example program to create a String
object
as follows. This way, you can call a method such as the
String.concat
method that adds text to the original
string.
class ExampleProgram {
public static void main(String[] args){
String text = new String("I'm a simple Program ");
System.out.println(text);
String text2 = text.concat(
"that uses classes and objects");
System.out.println(text2);
}
}
The output looks like this:
I'm a simple Program
I'm a simple Program that uses classes and objects
Objects
An instance is an executable copy of a class. Another name for
instance is object. There can be any number of objects of a given class
in memory at any one time.
In the last example, four different String
objects are
created for the concatenation operation, text
object,
text2
object, and a String
object
created behind the scenes from the " that uses classes
and objects" character string passed to
the String.concat
method.
Also, because String
objects cannot be edited,
the java.lang.String.concat
method converts the
String
objects to StringBuffer
(editable)
string objects to do the concatenation.
Besides the
String
object, there is an instance of the
ExampleProgram.java
class in memory as well.
The System
class is never instantiated by the
ExampleProgram
class because it contains only
static variables and methods, and therefore, cannot be
instantiated by a program, but it is instantiated
behind the scenes by the JavaTM virtual machine1 (VM).
Well-Defined Boundaries and Cooperation
Class definitions must allow objects to cooperate during execution.
In the previous section, you saw how the System
,
String
, and StringBuffer
objects cooperated to print a concatenated
character string to the command line.
This section changes the example program to display the
concatenated character string in a JLabel
component in a user interface to further illustrate the concepts
of well-defined class boundaries and object cooperation.
The program code to place the text in a label to display it in
a user interface uses a number of cooperating classes.
Each class has its own function and purpose as summarized
below, and where appropriate, the classes are defined
to work with objects of another class.
ExampleProgram
defines the program data and methods to
work on that data.
JFrame
defines the top-level window including the window title
and frame menu.
WindowEvent
defines behavior for (works with) the
Close option on the frame menu.
String
defines a character string to create the label.
JLabel
defines a user interface component to display
static text.
JPanel
defines the background color, contains the label,
and uses the default layout manager (java.awt.FlowLayout
)
to position the label on the display.
While each class has its own specific purpose, they all work together
to create the simple user interface you see here.
import javax.swing.*;
import java.awt.Color;
import java.awt.event.*;
class ExampleProgram extends JFrame {
public ExampleProgram(){
String text = new String("I'm a simple Program ");
String text2 = text.concat(
"that uses classes and objects");
JLabel label = new JLabel(text2);
JPanel panel = new JPanel();
panel.setBackground(Color.white);
getContentPane().add(panel);
panel.add(label);
}
public static void main(String[] args){
ExampleProgram frame = new ExampleProgram();
frame.setTitle("Fruit $1.25 Each");
WindowListener l = new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
};
frame.addWindowListener(l);
frame.pack();
frame.setVisible(true);
}
}
Inheritance
One object-oriented concept that helps objects work together
is inheritance. Inheritance defines relationships among classes
in an object-oriented language. In the Java programming language, all
classes descend from java.lang.Object
and
implement its methods.
The following diagram shows the class hierarchy as it descends from
java.lang.Object
for the classes in the user
interface example above.
The java.lang.Object
methods are also shown because
they are inherited and implemented by all of its subclasses, which
is every class in the Java API libraries.
java.lang.Object
defines the core set of behaviors
that all classes have in common.
As you move down the hierarchy, each class adds its own set of
class-specific fields and methods to what it inherits from its
superclass or superclasses. The java.awt.swing.JFrame
class inherits fields and methods from java.awt.Frame
, which
inherits fields and methods from java.awt.Container
,
which inherits fields and methods from java.awt.Component
,
which finally inherits from java.lang.Object
,
and each subclass adds its own fields and methods as needed.
Polymorphism
Another way objects work together is to define methods that
take other objects as parameters. You get even more cooperation
and efficiency when the objects are united by a common superclass. All classes
in the Java programming language have an inheritance relationship.
For example, if you define a method that takes a
java.lang.Object
as
a parameter, it can accept any object in the entire Java platform.
If you define a method that takes a java.awt.Component
as
a parameter, it can accept any component object.
This form of cooperation is called polymorphism.
You saw an example of polymorphism in
Part 2, Lesson 5: Collections where a collection
object can contain any type of object as long as it
descends from java.lang.Object
.
It is repeated here to show you that Set
collection can add a String
object and an
Integer
object to the Set
because the
Set.add
method is defined to accept any class instance
that traces back to the java.lang.Object
class.
String custID = "munchkin";
Integer creditCard = new Integer(25);
Set s = new HashSet();
s.add(custID);
s.add(creditCard);
Data Access Levels
Another way classes work together is through access level controls.
Classes, and their fields and methods have access levels
to specify how they can be used by other objects during execution,
While cooperation among objects is desirable, there are times
when you will want to explicitly control access,
and specifying access levels is the way to gain that control. When
you do not specify an access level, the default access level is in effect.
Classes
By default, a class can be used only by instances of other classes in the
same package. A class can be declared public
to make it accessible to all class instances regardless of what
package its class is in. You might recall that in
Part 1, Part 1, Lesson
3: Building Applets, the applet class had to be declared public
so it could be accessed by the appletviewer tool because
the appletviewer program is created from classes in another
package.
Here is an applet class declared to have a
public
access level:
public class DbaAppl extends Applet
implements ActionListener {
Without the public
access level (shown below),
its access level is package
by default. You get an
error when you try to interpret a class with an access level
of package
with the appletviewer tool. The same is
true if the access level is protected
or private
.
class DbaAppl extends Applet
implements ActionListener {
Also, in Part 2, Lesson 6: Internationalization
the server classes are made public so client classes can access
them.
Fields and Methods
Fields and methods can be declared private
,
protected
, public
, or package
.
If no access level is specified, the field or method access level is
package
by default.
private
: A private field or method is accessible only to
the class in which it is defined. In Part 1,
Lesson 7: Database Access and Permissions
the connection, user name, and password for establishing
the database access are all private. This is
to prevent an outside class from accessing
them and jeopardizing the database connection, or compromising
the secret user name and password information.
private Connection c;
protected
: A protected field or method is accessible to
the class itself, its subclasses, and classes in the same package.
public
: A public field or method is accessible to any
class of any parentage in any package. In
Part 2, Lesson 6: Internationalization
server data accessed by client programs is made public.
package
: A package field or method is accessible
to other classes in the same package.
Your Own Classes
When you use the Java API library classes, they have already
been designed with the above concepts in mind. They all
descend from java.lang.Object
giving them
an inheritance relationship; they have well-defined boundaries;
and they are designed to cooperate with each other where appropriate.
For example, you will not find a String
class
that takes an Integer
object as input because
that goes beyond the well-defined boundary for a String
.
You will, however, find the Integer
class
has a method for converting its integer value to a String
so its value can be displayed in a user interface component, which
only accepts String
objects.
But what about when you write your own classes? How can you
be sure your classes have well-defined boundaries, cooperate,
and make use of inheritance? One way is to look at the functions
you need a program to perform and separate them into
distinct modules where each functional module is defined
by its own class or group of classes.
Well-Defined and Cooperating Classes
Looking at the
RMIClient2
class from the
Part 2, Lesson 5: Collections lesson, you can
see it performs the following functions: Get data, display data,
store customer IDs, print customer IDs, and reset the display.
Getting data, displaying the data, and resetting the display are
closely related and easily form a functional module. But in a larger
program with more data processing, the storing and printing of customer
IDs could be expanded to store and print a wider range of data. In such a
case, it would make sense to have a separate class for storing data, and
another class for printing it in various forms.
You could, for example, have a class that defines how to
store customer IDs, and tracks the number of apples, peaches, and pears
sold during the year. You could also have another class that defines
report printing. It could access the stored data to print reports
on apples, peaches, and pears sold by the month, per customer, or
throughout a given season.
Making application code modular by separating out functional
units makes it easier to update and maintain the source code.
When you change a class, as long as you did not change any part
of its public interface, you only have to recompile that one class.
Inheritance
Deciding what classes your program needs means separating functions
into modules, but making your code more efficient and easier to
maintain means looking for common functions where you can use
inheritance. If you need to write a class that has functionality
similar to a class in the Java API libraries, it makes sense to
extend that API library class and use its methods rather than
write everything from scratch.
The RMIClient2
class from the
Part 2, Lesson 5: Collections lesson extends
JFrame
to leverage the ready-made functionality it
provides for a program's top-level window including, frame
menu closing behavior, background color setting, and a customized
title.
Likewise, if you want to add customized behavior to an existing class,
you can extend that class and add the functionality you want. For example,
you might want to create your own JButton
class with a
different look. To do this, you can write your own class that extends
JButton
and implement it to appear the way you
want. Then your program can instantiate your button class instead of the
JButton
class whenever you need a button with
the new look you created.
Access Levels
You should always keep access levels in mind when you declare
classes, fields, and methods. Consider which objects really need
access to the data, and use packages and access levels to protect your
application data from all other objects executing in the system.
Most object-oriented applications do not allow other objects to
access their fields directly by declaring them private. Then
they make their methods protected, public, or package as needed and
allow other objects to manipulate their private data by
calling the methods only. This way, you can update your class
by changing a field definition and the corresponding method implementation,
but other objects that access that data do not need to be changed because
their interface to the data (the method signature) has not changed.
Program Improvements
It is always best to restrict access as much as possible. Going
back to Part 2, Lesson 7: Packages and JAR Files,
the server classes had to be made public
and the
DataOrder
class fields also had to be made public
so the client programs can access them.
At that time, no access level was specified for the other classes and
fields so they are all package
by default. All methods
have an access level of public
.
A good exercise would be to go back to the client classes and
give the classes, fields, and methods an access
level so they are not accessed inappropriately by other objects.
Here is one possible solution for the
RMIClient1.java and
RMIClient2.java client
programs. Can you explain why the actionPerformed
method
cannot be made private
? If not, make it private
,
run the javac
command to compile, and see what the compiler has
to say about it.
More Information
You can find more information on
Object-oriented programming concepts files in the
Object-Oriented
Programming Concepts trail in
The Java Tutorial.
_______
1 As used on this web site,
the terms "Java virtual
machine" or "JVM" mean a virtual machine
for the Java platform.
[TOP]