object-oriented programming

In this chapter, we
• Introduce you to object-oriented programming;
• Show you how you can create objects that belong to classes in the standard Java library; and
• Show you how to write your own classes.

If you do not have a background in object-oriented programming, you will want to read this chapter carefully. 

Thinking about object-oriented programming requires a different way of thinking than for procedural languages. 

The transition is not always easy, but you do need some familiarity with object concepts to go further with Java.

For experienced C++ programmers, this chapter, like the previous chapter, presents familiar information; 

however, there are enough differences between the two languages that you should read the later sections of this chapter carefully. 

You’ll find the C++ notes helpful for making the transition.

Introduction to Object-Oriented Programming Object-oriented programming (or OOP for short) is the dominant programming paradigm these days, 

having replaced the “structured,” procedural programming techniques that were developed in the 1970s. 

Java is totally object oriented, and you have to be familiar with OOP to become productive with Java.

An object-oriented program is made of objects. Each object has a specific functionality that is exposed to its users, and a hidden implementation. 

Many objects in your programs will be taken “off-the-shelf” from a library; others are custom designed. 

Whether you build an object or buy it might depend on your budget or on time. 

But, basically, as long as objects satisfy your specifications, you don’t care how the functionality was implemented. 

In OOP, you don’t care how an object is implemented as long as it does what you want.

Traditional structured programming consists of designing a set of procedures (or algorithms) to solve a problem. 

After the procedures were determined, the traditional next step was to find appropriate ways to store the data. 

This is why the designer of the Pascal language, Niklaus Wirth, called his famous book on programming Algorithms + Data Structures = Programs (Prentice Hall, 1975). Notice that in Wirth’s title, algorithms come first, and data structures come second. This mimics the way programmers worked at that time. 

First, they decided the procedures for manipulating the data; then, they decided what structure to impose on the data to make the manipulations easier. 

OOP reverses the order and puts data first, then looks at the algorithms that operate on the data.

For small problems, the breakdown into procedures works very well. But objects are more appropriate for larger problems. 

Consider a simple web browser. It might require 2,000 procedures for its implementation, all of which manipulate a set of global data. 

In the object-oriented style, there might be 100 classes with an average of 20 methods per class (see Figure 4–1). 

The latter structure is much easier for a programmer to grasp. It is also much easier to find bugs. Suppose the data of a particular object is in an incorrect state. 

It is far easier to search for the culprit among the 20 methods that had access to that data item than among 2,000 procedures.

Chapter 4. Objects and Classes
Introduction to Object-Oriented Programming 107
Figure 4–1 Procedural vs. OO programming
Classes

A class is the template or blueprint from which objects are made. Thinking about classes as cookie cutters. Objects are the cookies themselves. 

When you construct an object from a class, you are said to have created an instance of the class.

As you have seen, all code that you write in Java is inside a class. 

The standard Java library supplies several thousand classes for such diverse purposes as user interface design, dates and calendars, and network programming. Nonetheless, you still have to create your own classes in Java to describe the objects of the problem domains of your applications.

Encapsulation (sometimes called information hiding) is a key concept in working with objects. 

Formally, encapsulation is nothing more than combining data and behavior in one package and hiding the implementation details from the user of the object. 

The data in an object are called its instance fields, and the procedures that operate on the data are called its methods. 

A specific object that is an instance of a class will have specific values for its instance fields. 

The set of those values is the current state of the object. Whenever you invoke a method on an object, its state may change.

The key to making encapsulation work is to have methods never directly access instance fields in a class other than their own. 

Programs should interact with object data only through the object’s methods. Encapsulation is the way to give the object its “black box” behavior, 

which is the key to reuse and reliability. This means a class may totally change how it stores its data, 

but as long as it continues to use the same methods to manipulate the data, no other object will know or care.

When you do start writing your own classes in Java, another tenet of OOP makes this easier: classes can be built by extending other classes. 

Java, in fact, comes with a “cosmic proceduren procedure

procedure
procedure
procedure
method
method
method
method
method
method
Global data
Object data
Object data
Object data
Chapter 4. Objects and Classes
Chapter 4 ■ Objects and Classes
108
superclass” called  Object . All other classes extend this class. You will see more about the Object class in the next chapter.

When you extend an existing class, the new class has all the properties and methods of the class that you extend. 

You supply new methods and data fields that apply to your new class only. 

The concept of extending a class to obtain another class is called inheritance. See the next chapter for details on inheritance.

Objects
To work with OOP, you should be able to identify three key characteristics of objects:
• The object’s behavior—What can you do with this object, or what methods can you apply to it?
• The object’s state—How does the object react when you apply those methods?
• The object’s identity—How is the object distinguished from others that may have the same behavior and state?

All objects that are instances of the same class share a family resemblance by supporting the same behavior. 

The behavior of an object is defined by the methods that you can call.

Next, each object stores information about what it currently looks like. This is the object’s state. An object’s state may change over time, but not spontaneously. 

A change in the state of an object must be a consequence of method calls. (If the object state changed without a method call on that object, someone broke encapsulation.)

However, the state of an object does not completely describe it, because each object has a distinct identity. 

For example, in an order-processing system, two orders are distinct even if they request identical items. 

Notice that the individual objects that are instances of a class always differ in their identity and usually differ in their state.

These key characteristics can influence each other. 

For example, the state of an object can influence its behavior. (If an order is “shipped” or “paid,” it may reject a method call that asks it to add or remove items. 

Conversely, if an order is “empty,” that is, no items have yet been ordered, it should not allow itself to be shipped.)

Identifying Classes

In a traditional procedural program, you start the process at the top, with the  main function. 

When designing an object-oriented system, there is no “top,” and newcomers to OOP often wonder where to begin. 

The answer is, you first find classes and then you add methods to each class.

A simple rule of thumb in identifying classes is to look for nouns in the problem analysis. Methods, on the other hand, correspond to verbs.
For example, in an order-processing system, some of these nouns are
• Item
• Order
• Shipping address
• Payment
• Account
These nouns may lead to the classes  Item , Order , and so on.
Chapter 4. Objects and Classes
Introduction to Object-Oriented Programming 109

Next, look for verbs. Items are added to orders. Orders are shipped or canceled. Payments are applied to orders. 

With each verb, such as “add,” “ship,” “cancel,” and “apply,” you identify the one object that has the major responsibility for carrying it out. 

For example,when a new item is added to an order, the order object should be the one in charge because it knows how it stores and sorts items. 

That is,  add should be a method of the Order class that takes an  Item object as a parameter.

Of course, the “noun and verb” rule is only a rule of thumb, and only experience can help you decide which nouns and verbs are the important ones when building your
classes.
Relationships between Classes
The most common relationships between classes are
• Dependence (“uses–a”)
• Aggregation (“has–a”)
• Inheritance (“is–a”)
The dependence, or “uses–a” relationship, is the most obvious and also the most general.

For example, the  Order class uses the  Account class because  Order objects need to access Account objects to check for credit status. 

But the  Item class does not depend on the  Account class, because  Item objects never need to worry about customer accounts. 

Thus, a class depends on another class if its methods use or manipulate objects of that class.

Try to minimize the number of classes that depend on each other. The point is, if a class A is unaware of the existence of a class  B , 

it is also unconcerned about any changes to  B !(And this means that changes to  B do not introduce bugs into  A .) In software engineering terminology, 

you want to minimize the  coupling between classes.

The aggregation, or “has–a” relationship, is easy to understand because it is concrete; for example, an  Order object contains  Item objects. 

Containment means that objects of class  A contain objects of class  B .

NOTE: Some methodologists view the concept of aggregation with disdain and prefer to use a more general “association” relationship. 

From the point of view of modeling, that is understandable. But for programmers, the “has–a” relationship makes a lot of sense. 

We like to use aggregation for a second reason—the standard notation for associations is less clear.

See Table 4–1.

The inheritance, or “is–a” relationship, expresses a relationship between a more special and a more general class. 

For example, a  RushOrder class inherits from an  Order class. 

The specialized  RushOrder class has special methods for priority handling and a different method for computing shipping charges, 

but its other methods, such as adding items and billing, are inherited from the  Order class. 

In general, if class  A extends class  B , class  A inherits methods from class  B but has more capabilities. 

(We describe inheritance more fully in the next chapter, in which we discuss this important notion at some length.)

Many programmers use the UML (Unified Modeling Language) notation to draw class diagrams that describe the relationships between classes. 

You can see an example of such a diagram in Figure 4–2. You draw classes as rectangles, and relationships as arrows with various adornments. 

Table 4–1 shows the most common UML arrow styles.

Chapter 4. Objects and Classes
Chapter 4 ■ Objects and Classes
110
Figure 4–2 A class diagram

NOTE: A number of tools are available for drawing UML diagrams. 

Several vendors offer high-powered (and high-priced) tools that aim to be the focal point of your development process. 

Among them are Rational Rose (http://www.ibm.com/software/awdtools/developer/rose) and Together (http://www.borland.com/us/products/together). 

Another choice is the open source program ArgoUML (http://argouml.tigris.org). A commercially supported version is available from GentleWare (http://gentleware.com). 

If you just want to draw a simple diagrams with a minimum of fuss, try out Violet (http://violet.sourceforge.net).

Table 4–1 UML Notation for Class Relationships
Relationship UML Connector
Inheritance
Interface inheritance
Dependency
Aggregation
Association
Directed association
Chapter 4. Objects and Classes
Using Predefined Classes 111
Using Predefined Classes

Because you can’t do anything in Java without classes, you have already seen several classes at work. 

However, not all of these show off the typical features of object orientation. 

Take, for example, the  Math class. 

You have seen that you can use methods of the  Math class, such as  Math.random , 

without needing to know how they are implemented—all you need to know is the name and parameters (if any). 

That is the point of encapsulation and will certainly be true of all classes. But the  Math class only encapsulates functionality;

it neither needs nor hides data. Because there is no data, you do not need to worry about making objects and initializing their instance fields—there aren’t any!
In the next section, we look at a more typical class, the  Date class. You will see how to construct objects and call methods of this class.
Objects and Object Variables
To work with objects, you first construct them and specify their initial state. Then you apply methods to the objects.

In the Java programming language, you use constructors to construct new instances. 

A constructor is a special method whose purpose is to construct and initialize objects. Let us look at an example. The standard Java library contains a  Date class. 

Its objects describe points in time, such as “December 31, 1999, 23:59:59 GMT”.

NOTE: You may be wondering: Why use classes to represent dates rather than (as in some languages) a built-in type? 

For example, Visual Basic has a built-in date type and programmers can specify dates in the format #6/1/1995#. 

On the surface, this sounds convenient  programmers can simply use the built-in date type rather than worrying about classes. 

But actually, how suitable is the Visual Basic design? In some locales, dates are specified as month/day/year, in others as day/month/year. 

Are the language designers really equipped to foresee these kinds of issues? 

If they do a poor job, the language becomes an unpleasant muddle, but unhappy programmers are powerless to do anything about it. 

With classes,the design task is offloaded to a library designer. 

If the class is not perfect, other programmers can easily write their own classes to enhance or replace the system classes. 

(To prove the point: The Java date library is a bit muddled, and a major redesign is underway;

see http://jcp.org/en/jsr/detail?id=310.)

Constructors always have the same name as the class name. Thus, the constructor for the Date class is called  Date . 

To construct a  Date object, you combine the constructor with the new operator, as follows:

new Date()
This expression constructs a new object. The object is initialized to the current date and time.
If you like, you can pass the object to a method:
System.out.println(new Date());

Alternatively, you can apply a method to the object that you just constructed. One of the methods of the  Date class is the  toString method. 

That method yields a string representation of the date. Here is how you would apply the  toString method to a newly constructed  Date object:

String s = new Date().toString();
Chapter 4. Objects and Classes
Chapter 4 ■ Objects and Classes
112

In these two examples, the constructed object is used only once. Usually, you will want to hang on to the objects that you construct so that you can keep using them. 

Simply store the object in a variable:

Date birthday = new Date();
Figure 4–3 shows the object variable  birthday that refers to the newly constructed object.
Figure 4–3 Creating a new object

There is an important difference between objects and object variables. For example,the statement Date deadline; 

// deadline doesn't refer to any object defines an object variable,  deadline , that can refer to objects of type  Date . 

It is important to realize that the variable  deadline is not an object and, in fact, does not yet even refer to an object. 

You cannot use any  Date methods on this variable at this time. The statement

s = deadline.toString(); // not yet
would cause a compile-time error.

You must first initialize the  deadline variable. You have two choices. 

Of course, you can initialize the variable with a newly constructed object:

deadline = new Date();
Or you can set the variable to refer to an existing object:
deadline = birthday;
Now both variables refer to the same object (see Figure 4–4).
It is important to realize that an object variable doesn’t actually contain an object. It only refers to an object.

In Java, the value of any object variable is a reference to an object that is stored else-where. 

The return value of the  new operator is also a reference. A statement such as

Date deadline = new Date();

has two parts. The expression  new Date() makes an object of type  Date , and its value is a reference to that newly created object. 

That reference is then stored in the  deadline variable.

birthday =
Date
Chapter 4. Objects and Classes
Using Predefined Classes 113
Figure 4–4 Object variables that refer to the same object
You can explicitly set an object variable to  null to indicate that it currently refers to no
object.
deadline = null;
. . .
if (deadline != null)
System.out.println(deadline);
If you apply a method to a variable that holds  null , then a runtime error occurs.
birthday = null;
String s = birthday.toString(); // runtime error!
Variables are not automatically initialized to  null . You must initialize them, either by
calling new or by setting them to  null .
C++ NOTE: Many people mistakenly believe that Java object variables behave like C++ ref-
erences. But in C++ there are no null references, and references cannot be assigned. You
should think of Java object variables as analogous to object pointers in C++. For example,
Date birthday; // Java
is really the same as
Date* birthday; // C++

Once you make this association, everything falls into place. Of course, a Date* pointer isn’t initialized until you initialize it with a call to new. 

The syntax is almost the same in C++ and Java.

Date* birthday = new Date(); // C++

If you copy one variable to another, then both variables refer to the same date—they are pointers to the same object. 

The equivalent of the Java null reference is the C++ NULL pointer.

All Java objects live on the heap. When an object contains another object variable, that variable still contains just a pointer to yet another heap object.
birthday =
Date
birthday =
Chapter 4. Objects and Classes
Chapter 4 ■ Objects and Classes
114

In C++, pointers make you nervous because they are so error prone. It is easy to create bad pointers or to mess up memory management. 

In Java, these problems simply go away. If you use an uninitialized pointer, the runtime system will reliably generate a runtime error instead of producing random results. You don’t worry about memory management, because the garbage collector takes care of it.

C++ makes quite an effort, with its support for copy constructors and assignment operators, to allow the implementation of objects that copy themselves automatically. 

For example, a copy of a linked list is a new linked list with the same contents but with an independent set of links. 

This makes it possible to design classes with the same copy behavior as the built-in types. 

In Java, you must use the clone method to get a complete copy of an object.

The GregorianCalendar Class of the Java Library In the preceding examples, we used the  Date class that is a part of the standard Java library. 

An instance of the  Date class has a state, namely a particular point in time.

Although you don’t need to know this when you use the  Date class, the time is represented by the number of milliseconds (positive or negative) from a fixed point, 

the so called epoch, which is 00:00:00 UTC, January 1, 1970. UTC is the Coordinated Universal

Time, the scientific time standard that is, for practical purposes, the same as the more familiar GMT or Greenwich Mean Time.

But as it turns out, the  Date class is not very useful for manipulating dates. 

The designers of the Java library take the point of view that a date description such as “December 31, 1999,

23:59:59” is an arbitrary convention, governed by a calendar. This particular description follows the Gregorian calendar, 

which is the calendar used in most places of the world.

The same point in time would be described quite differently in the Chinese or Hebrew lunar calendars, not to mention the calendar used by your customers from Mars.

NOTE: Throughout human history, civilizations grappled with the design of calendars that attached names to dates and brought order to the solar and lunar cycles. 

For a fascinating explanation of calendars around the world, from the French Revolutionary calendar to the Mayan long count, see Calendrical Calculations, Second Edition by Nachum Dershowitz

and Edward M. Reingold (Cambridge University Press, 2001).

The library designers decided to separate the concerns of keeping time and attaching names to points in time. 

Therefore, the standard Java library contains two separate classes: the  Date class, which represents a point in time, and the  GregorianCalendar class,

which expresses dates in the familiar calendar notation. In fact, the  GregorianCalendar class extends a more generic  Calendar class that describes the properties of calendars in general. In theory, you can extend the  Calendar class and implement the Chinese lunar calendar or a Martian calendar. 

However, the standard library does not contain any calendar implementations besides the Gregorian calendar.

Separating time measurement from calendars is good object-oriented design. In general, it is a good idea to use separate classes to express different concepts.

The Date class has only a small number of methods that allow you to compare two points in time. 

For example, the  before and  after methods tell you if one point in time comes before or after another:

Chapter 4. Objects and Classes
Using Predefined Classes 115
if (today.before(birthday))
System.out.println("Still time to shop for a gift.");

NOTE: Actually, the Date class has methods such as getDay, getMonth, and getYear, but these methods are deprecated. 

A method is deprecated when a library designer realizes that the method should have never been introduced in the first place.

These methods were a part of the Date class before the library designers realized that it makes more sense to supply separate calendar classes. 

When the calendar classes were introduced, the Date methods were tagged as deprecated. You can still use them in your programs, 

but you will get unsightly compiler warnings if you do. It is a good idea to stay away from using deprecated methods because they may be removed in a future version of the

library.

The GregorianCalendar class has many more methods than the  Date class. In particular, it has several useful constructors. 

The expression

new GregorianCalendar()
constructs a new object that represents the date and time at which the object was constructed.
You can construct a calendar object for midnight on a specific date by supplying year,
month, and day:
new GregorianCalendar(1999, 11, 31)

Somewhat curiously, the months are counted from 0. Therefore, 11 is December. 

For greater clarity, there are constants like  Calendar.DECEMBER :

new GregorianCalendar(1999, Calendar.DECEMBER, 31)
You can also set the time:
new GregorianCalendar(1999, Calendar.DECEMBER, 31, 23, 59, 59)
Of course, you will usually want to store the constructed object in an object variable:
GregorianCalendar deadline = new GregorianCalendar(. . .);

The GregorianCalendar has encapsulated instance fields to maintain the date to which it is set. 

Without looking at the source code, it is impossible to know the representation that the class uses internally. 

But, of course, the point of encapsulation is that this doesn’t matter. What matters are the methods that a class exposes.

Mutator and Accessor Methods

At this point, you are probably asking yourself: How do I get at the current day or month or year for the date encapsulated in a specific  GregorianCalendar object? 

And how do I change the values if I am unhappy with them? 

You can find out how to carry out these tasks by looking at the on-line documentation or the API notes at the end of this section. 

We go over the most important methods in this section.

The job of a calendar is to compute attributes, such as the date, weekday, month, or year, of a certain point in time. 

To query one of these settings, you use the  get method of the  GregorianCalendar class. 

To select the item that you want to get, you pass a constant defined in the  Calendar class, such as  Calendar.MONTH or  Calendar.DAY_OF_WEEK :

Chapter 4. Objects and Classes
Chapter 4 ■ Objects and Classes
116
GregorianCalendar now = new GregorianCalendar();
int month = now.get(Calendar.MONTH);
int weekday = now.get(Calendar.DAY_OF_WEEK);
The API notes list all the constants that you can use.
You change the state with a call to the  set method:
deadline.set(Calendar.YEAR, 2001);
deadline.set(Calendar.MONTH, Calendar.APRIL);
deadline.set(Calendar.DAY_OF_MONTH, 15);
There is also a convenience method to set the year, month, and day with a single call:
deadline.set(2001, Calendar.APRIL, 15);
Finally, you can add a number of days, weeks, months, and so on, to a given calendar
object:
deadline.add(Calendar.MONTH, 3); // move deadline by 3 months
If you add a negative number, then the calendar is moved backwards.

There is a conceptual difference between the  get method on the one hand and the  set and add methods on the other hand. 

The  get method only looks up the state of the object and reports on it. 

The  set and  add methods modify the state of the object. Methods that change instance fields are called mutator methods, 

and those that only access instance fields without modifying them are called accessor methods.

C++ NOTE: In C++, the const suffix denotes accessor methods. A method that is not declared as const is assumed to be a mutator. 

However, in the Java programming language,no special syntax distinguishes between accessors and mutators.

A common convention is to prefix accessor methods with the prefix  get and mutator methods with the prefix  set . 

For example, the  GregorianCalendar class has methods  getTime and setTime that get and set the point in time that a calendar object represents:

Date time = calendar.getTime();
calendar.setTime(time);

These methods are particularly useful for converting between the  GregorianCalendar and  Date classes. 

Here is an example. Suppose you know the year, month, and day and you want to make a  Date object with those settings. 

Because the  Date class knows nothing about calendars,first construct a  GregorianCalendar object and then call the  getTime method to obtain a date:

GregorianCalendar calendar = new GregorianCalendar(year, month, day);
Date hireDay = calendar.getTime();
Conversely, if you want to find the year, month, or day of a  Date object, you construct a
GregorianCalendar object, set the time, and then call the  get method:
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(hireDay);
int year = calendar.get(Calendar.YEAR);
We finish this section with a program that puts the  GregorianCalendar class to work. The
program displays a calendar for the current month, like this:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值