What is object-oriented programming?
Although the answer to this question will reveal itself as you work your way through this book, at this juncture it might be useful to draw parallels between object-oriented programming (OO) and the world around us. You are unlikely to dispute the assertion that during the last half century the following facts about societies have become amply clear: Societies function best when centralized control is kept to a minimum; when the intelligence needed for the smooth functioning of a society is as distributed as possible; when each person is sufficiently smart to know for himself or herself how to make sense of the various norms and mores of the society for the common good; and when the higher-level organizational structures, often organized in the form of hierarchies, facilitate the propagation of society-nurturing messages up and down the hierarchies.
Large object-oriented programs are no different. The idea is to think of large software (sometimes consisting of millions of lines of code) as consisting of a society of objects: objects that possess sufficient intelligence to interpret messages received from other objects and to then respond with appropriate behavior; objects that inherit properties and behaviors from higher-level objects and permit lower-level objects to inherit properties and behaviors from them; and so on. Just as decentralization of human organizations makes it easier to extend and maintain the various societal structures (because the intelligence needed for such maintenance and extension resides locally in the structures), a decentralized organization of software allows it to be extended and maintained more easily. If as a programmer you are not happy with the objects supplied to you by a software vendor, in most cases you'd be able to extend those objects with relative ease and customize them to your particular needs. And if any problems developed in one of the components of a large decentralized organization of objects, your troubleshooting would be easier because of its localized nature—this would apply as much to a society of people as it would to a society of software objects.
A discourse concerning societies is made more efficient if we group together all those objects that share common characteristics. We could then refer to such groups as classes. For example, all people engaged in the delivery of healthcare have to have certain common professional attributes. We could say that these common attributes define the class health-care professional. All medical doctors—the class medical doctor being a subclass of the class health-care professional—must possess the attributes of all health-care professionals; they must also possess additional attributes by way of specialized education and training.
This analogy carries over directly to software design based on objects. All objects that possess the same attributes and exhibit the same behaviors are grouped into a single class. In fact, we first define a class and then create individual objects by a process known as instantiating a class. All objects that possess the attributes and behaviors of a previously defined class, possessing at the same time additional more-specialized attributes and behaviors, are represented as a subclass of the previously defined class.
What good does OO do?
Over the years, object-oriented programming has become the preferred style of programming for graphical user interfaces (GUI)—so much so that even when using languages that do not directly support object orientation (such as C), programmers create software structures that simulate OO for GUI programming. Probably the most famous example of this is the GNOME/GTK+ toolkit for GUI design; it's all in C, yet it is "very OO" in its programming style and structuring. For purposes of comparative presentation, where the main focus is, of course, on C++ and Java. OO is also making strong inroads into database and network programming.
How do I master it?
It takes a three-pronged strategy to master the OO paradigm for solving actual problems involving large and complex systems. You must, of course, learn the syntax specific to the languages. Clearly, without a working level familiarity with all the major constructs of a language, you may not be able to bring to bear the most effective tools on a problem. This, however, does not mean that you must memorize all of the syntax. For example, it would be impossible to commit to memory all of the different Java classes and the attributes and the functions associated with each of the classes. Fortunately, it is not necessary to do so in this age of web-based documentation. A standard approach to Java programming is to display in one window the extremely well-organized on-line Java documentation while you are constructing your own program in another window.
In addition to the syntax, you must master for each language the concepts of encapsulation, inheritance, and polymorphism, as these three concepts form the cornerstones of a truly OO language. How each concept works varies in subtle ways from language to language. For example, C++ permits multiple inheritance which gives a programmer certain freedoms, but with an increased risk of writing buggy code. On the other hand, Java forbids multiple inheritance in the sense permitted by C++, but allows for a class to inherit from any number of interfaces. Similarly, the access modifiers that allow you to encapsulate information in a class with different levels of access work slightly differently in C++ and Java. Additionally, Java has the concept of a package that has a bearing on access control—a concept that does not exist in C++. Polymorphism allows a subclass type to be treated like a superclass type. Although it works essentially the same in all major OO languages, the manner in which it is invoked can place important constraints on programming. In C++, for example, polymorphism can only be invoked through pointers, a fact that can have a large bearing on how you might refer to an object in a program.
The last of the three-pronged strategy deals with learning OO design. As with all design activity, there is a certain mystique associated with it. This is not surprising, because it would be impossible to enunciate the design principles that would span all possible problems, those already solved and those yet to be solved. Much of learning how to design an OO solution to a large and complex problem is a matter of experience, aided perhaps by examining good OO code written by other people. Nonetheless, the accumulated wisdom over the years now dictates the following approach to the development of expertise in OO design: (1) mastering a "meta" language, such as the Unified Modeling Language (UML), that allows you to express your design visually at a conceptual level; and (2) learning the design patterns, these being template solutions to a host of subproblems likely to be encountered during the evolution of an OO program. Regarding design patterns, there is no specific chapter devoted to it, although the example code presented includes the implementation of some of the patterns. For a reader wanting to pursue more deeply both UML and the topic of design patterns, there are excellent books available on both [7, 13, 20, 21].
SIMPLE PROGRAMS
SUMMING AN ARRAY OF INTEGERS
Let's say you want to add 10 integers that are stored in an array. A C program for doing this would look like
/* AddArray1.c */
#include
int addArray (int [], int);
main()
{
int data[] = {4,3,2,1,0,5,6,7,8,9}; /* (A) */
int size = sizeof (data) /sizeof (data [0]); /* (B) */
printf("sum is %d/n", addArray( data, size )); /* (C) */
return 0;
}
int addArray( int a[] , int n ) { /* (D) */
int sum = 0;
int i;
for(i=0; isum += a[i] ; /* (E) */
return sum;
}
Line (A) of main declares an integer array data and initializes it as shown. Line (B) figures out the size of the array. The function addArray is called in line (C) to sum up all the integers in the array.
If there is anything noteworthy about this program at all, it lies in the fact that an array name in C (and also in C++) is treated like a pointer in some contexts. Whereas data is an array name when supplied as an argument to the operator sizeof in line (B), it is a pointer to the first element of the array when supplied as an argument to the function addArray in line (C).
Contrast this with the fact that the array name a in the called function addArray in line (D) is merely a pointer, in the sense that sizeof(a) computed anywhere inside the function addArray will return 4 for the four bytes it takes to store a memory address on many modern machines. On the other hand, sizeof ( data ) in line (B) will return 40 for the 40 bytes that it takes to store the 10 integers of the array data, assuming that your machine allocates 4 bytes for an int.[1]
So when main calls addArray in line (C), the memory address that is the value of data when treated as a pointer is assigned to the parameter a in line (D) and that the array itself is not copied. Subsequently, the function addArray visits each element of the array in line (E) through the memory address assigned to a and adds the element to the sum.
A more explicitly pointer version of the addArray function is shown in the following program that does the same thing:
/* AddArray2.c */
#include
int addArray( int*, int );
main()
{
int data[] = {4,3,2,1,0,5,6,7,8,9};
int size = sizeof(data)/sizeof(data[0]);
printf("Pointer Version: sum is %d\n", addArray( data, size ));
return 0;
}
int addArray( int* a, int n ) {
int sum = 0;
int i;
for(i=0; isum += *a++;
return sum;
}
The two programs shown above are essentially identical because, as mentioned already, declaring a function parameter to be an array (the first program) is the same as declaring it to be a pointer (the second program).
Now let's consider a C++ program for doing the same thing:
//AddArray.cc
#include//(A)
using namespace std; //(B)
int addArray( int*, int );
int main()
{
int data[] = {4,3,2,1,0,5,6,7,8,9};
int size = sizeof(data)/sizeof(data[0]);
cout << "C++ version: sum is " //(C)
<< addArray( data, size ) << endl;
}
return 0;
}
int addArray( int* a, int n) {
int sum = 0;
int i;
for(i=0; isum += *a++;
return sum;
}
This program shows this book's first use of an "object" in C++. The object is cout in line (C). This is an output stream object whose name is usually pronounced "c-out" as an abbreviation for "console out." This object knows how to send information to the standard output stream, which would generally be directed to the window of the terminal screen in which you are running your program. All objects in OO programming belong to some object class. The output stream object cout belongs to the class basic_ostream that is defined in the library header file iostream included in the program in line (A).
The header iostream is one of the many header files that constitute the C++ Standard Library.[2] This library is a culmination of the effort of the International Standards Organization (ISO) and the American National Standards Institute (ANSI) for the standardization of the C++ language. A significant portion of the C++ Standard Library includes what is informally referred to as the Standard Template Library (STL). STL consists of container classes for holding collections of objects and classes that play supporting roles for using the container classes. The Standard Library also includes the header file string that we will be using very frequently in this book for representing and processing C++ strings. Other header files in the C++ Standard Library contain classes for memory management (new and memory); representing exceptions (exception and stdexcept); representing complex numbers (complex); run-time type identification (typinfo;) and so on.
Although this point will become clearer after we have presented the idea of a namespace in Chapter 3, the directive
using namespace std;
in line (B) of the program takes account of the fact that all the identifiers (meaning the names of classes, functions, objects, etc.) used in the C++ Standard Library are defined within a special namespace known as the standard namespace and designated std. If we did not invoke this directive, we would need to call the output stream object by using the syntax std:: cout.
The symbol ‘<<’ in line (C) is called the output operator or the insertion operator. This operator, defined originally as the left bitwise shift operator, has been overloaded in C++ for inserting data into output stream objects when used in the manner shown here.[3] The ‘<<’ operator does formatted insertions into an output stream object. What that means is that if the operator is asked to insert an int into an output stream object, it will translate the four bytes of the int into its printable character representation and then insert the character bytes into the output stream object.
You can comment code in C++ the way you do it in C, that's by using the delimiters /* …. */. You can also comment individual lines, or the trailing part of a line, by //. The compiler will not see on that line any characters past //.
Note that, as indicated by the commented out statement at the beginning of the program, Unix-like platforms require the name of a file containing the C++ source code to end in the suffix .cc. One can also use the suffix .C or the suffix .cpp. To compile this program, you'd say
g++ filename
The compiler will deposit an executable file called a. out or a.exe in your directory. This assumes that you are using the GNU C++ compiler. This compiler comes prepackaged with Unix and Linux distributions, although, if needed, you could download the latest version from the Free Software Foundation (http://www.gnu.org). If you are using a PC and you do not have access to a pre-loaded C++ compiler, you can download the GNU compiler (and other very useful Unix-emulation utilities for Windows) from the site http://sourceware.cygnus.com/cygwin/. For Solaris platforms, you should also be able to use the CC compiler via the invocation
CC filename
where, again, the name of the file must end in either ’.C’ or ’.cc’ or ’.cpp’. As with g++, the compiler will deposit an executable file called a.out or a.exe in your directory.
For another point of difference—a difference regarding style—between the C programs we showed at the beginning of this section and the C++ program above is in the header of main. The main in both C and C++ programs returns a status code, which is 0 if the program terminates normally and a nonzero integer to indicate abnormal termination. By tradition, the return type of main in C programs is left unmentioned—it being int by default. On the other hand, C++ requires a program to explicitly mention the return type int of main.
Now let's see how one would write a Java program for doing the same thing:
//AddArray.java
public class AddArray { //(A)
public static void main( String[] args ) //(B)
{
int[] data = { 0, 1, 2, 3, 4, 5, 9, 8, 7, 6 }; //(C)
System.out.println( "The sum is: " //(D)
+ addArray(data) );
}
public static int addArray( int[] a ) { //(E)
int sum = 0;
for ( int i=0; i < a.length; i++ )
sum += a[i];
return sum;
}
}
As shown in the commented out line at the beginning of the program, this source code resides in a file called
AddArray.java
The program begins with a class declaration:
public class AddArray {
....
....
In Java, functions can exist only inside classes. So even though using a class for the simple task for which we are writing the program seems rather excessive, there is no choice.
Note that the name of the file before the suffix java is the same as the class name, AddArray. Ordinarily, this is necessary only if a class is declared to be public. A file containing Java classes is allowed to have no more than one public class. If no classes in a file are public, the file can be given any name, but, of course, it must end in the suffix .java. To compile this file, you invoke the Java compiler by
javac AddArray.java
The compiler outputs what's known as the bytecode for the class and, in this case, deposits it in a file called
AddArray.class
This bytecode is machine-independent, unlike the executables for C or C++ programs, and can be run by another program called the Java Virtual Machine (JVM). A JVM will execute the program either in the interpreted mode or by first converting the bytecode into a machine-dependent executable using a second round of what is known as just-in-time (JIT) compilation and then executing the binaries thus obtained. The latter is the default mode and results in a tenfold increase in execution speed over the interpreted mode. For the bytecode file named AddArray. class, a Java Virtual Machine is invoked by
java AddArray
Before you can compile and run a Java program, you may have to tell the system how to find the classes you created with your program. The default is your current directory. But if you wanted to compile a class that was stored in some other directory, you have to tell both javac and java tools how to locate the class. The preferred way to do this is by using the -classpath option when invoking the javac compiler or the java application launcher. The -classpath option is also needed even if you are trying to compile a Java program in the directory in which it resides if your program uses other Java classes, your own or written by a third party, that reside in other directories.[4]
Suppose your program uses third-party classes that are stored in directories directory_1 and directory_2,[5] you'd want to invoke javac and java with the following syntax on Unix and Linux platforms:
javac -classpath .:directory_1:directory_2 sourceCode.java
java -classpath .:directory_1:directory_2 className
and on Windows platforms by[6]
javac -classpath .;directory_1; directory_2 source.java
java -classpath .;directory_1; directory_2 className
where the symbol ’.’ is used to designate the current directory where presumably the main application resides. Note that the delimiter between the directories for Unix and Linux platforms is the character ’:’ and for the Windows platform the character ’;’. The third-party classes, or, for that matter, even your previously programmed classes may come packaged in the form of an archive called the JAR archive.[7] If that's the case, you'd need to specify the pathname to such archives in your classpath specification, as for example in
javac -classpath .:/path_to_archive/archive.jar your_program.java
java -classpath .:/path_to_archive/archive.jar your_class_name
If the classpath strings become too long, you can create shell files containing the above invocations on Unix and Linux platforms. On Windows platforms, the same is accomplished by using batch files. On Unix and Linux platforms, it is also possible to set up aliases for the compiler and the application launcher that include the classpath string.
On Unix and Linux platforms and on some of the older Windows platforms, instead of using the classpath option as shown above, it is also possible to set the CLASSPATH environment variable. For example, if you are using either the csh or the tcsh shells, you can define a classpath by, say, including the following in a .cshrc file,
setenv CLASSPATH .:directory_1:directory_2:....
which would create the same classpath setting as our earlier examples. If, on the other hand, you are using either sh, ksh, or bash, you can achieve the same effect by including the following strings in your .profile file:
CLASSPATH=.:directory_1:directory_2:....
export CLASSPATH
If desired, you can "unset" the value of the environmental variable by invoking unsetenv CLASSPATH in csh and tcsh and by invoking unset CLASSPATH in sh and ksh.
Even if you use the CLASSPATH environment variable, you may still have to use the -classpath option as shown previously to customize the classpath for a particular application. The classpath as set by the -classpath option overrides the classpath as set by the environmental variable. Note again the importance of including the character ’.’ in the CLASSPATH environment variable since, as was the case with the -classpath option, setting the environment variable overrides the default.
Getting to the program itself, the code that is inside the class definition is very much like the C or the C++ code we showed earlier. We have the function main() in line (B) and the method[8] addArray() inside the class definition in line (E). In Java, any class can include main(). When a class includes main(), the class becomes executable as an application. Since main() does not return anything in Java, its return type is declared as void. The significance of the labels public and static in the header for main will be explained in Chapter 3. In the body of main, we declare the identifier data as an array of ints and initialize it at the same time, very much like we did for C and C++.
The invocation System.out.println( … ) in line (D) is a call to the println() method that is defined for the output stream object out. More precisely, out is a field of type OutputStream defined in the class System. System is a class that comes with the java. lang package.[9] This package is loaded in automatically by the Java compiler. println() is a method defined for the class OutputStream. One could also use the method print() via the invocation System. out.print( … ) if it is not necessary to display the output in a separate line of text. The println and the print methods are as defined for the PrintStream class. The argument to these methods must either be a string or a type that Java would know how to convert into a string for display. In our example, the second part of the argument, of type int, gets converted into a string automatically.
The rest of the program consists of the method addArray() in line (E), which is very much like the C++ function of the same name in the earlier program, except for the manner in which the size of the array is determined inside the function. For both C and C++, the size of the array had to be passed explicitly to the function. But in Java, that is not necessary. Arrays in Java are objects that have data members[10] associated with them. The data member that is associated with an array object is length. When we access this data member through the call data.length, we can determine the length of the array data.
Also note from the above examples that C++ and Java have exactly the same way of commenting code. You can either use the C-style comment delimiters /* …. */ or //. However, the latter can only be used for comments on a single line, because the compiler will not see any characters past //.
With regard to comments in Java programs, a special tool called javadoc can automatically generate documentation for your program using text that is delimited by /** and */. This tool generates HTML files that can be viewed with a browser.
[1]It is important to bear in mind that while an array name can "decay" into a pointer, it is not a pointer. The extent to which an array name decays into a pointer depends, among other things, on whether an array name is the name of a parameter of a callable function. So, whereas the array name data in main will act like a pointer in some contexts only, the array name a in the function addArray of the program AddArray1. c will act like a pointer in practically all contexts.
[2]The C++ Standard Library consists of these header files: algorithm, bitset, complex, deque, exception, fstream, functional, iomanip, ios, iosfwd, iostream, istream, iterator, limits, list, locale, map, memory, new, numeric, ostream, queue, set, sstream, stack, stdexcept, streambuf, string, typeinfo, utility, valarray, vector. Of these, the following are informally referred to as the Standard Template Library (STL): algorithm, bitset, deque, functional, iterator, list, map, queue, set, stack, valarray and vector.
[3]Operator overloading, discussed in detail in Chapter 12, allows the same operator to be used in different ways. The operands determine as to which meaning of such an operator applies in a given context.
[4]You do not need to specify the classpath for the classes that come with the Java platform. Both the compiler and the application launcher can locate those automatically.
[5]By directory name here is meant the pathname to the directory.
[6]If you are using a Cygnus emulation of Unix on Windows, you may need to place the classpath string between double quotes.
[7]A JAR file in Java is an archive file, just like a Unix tar (tape archive) file. Jar files are created and manipulated by using the Java jar tool. To create a JAR archive of all your classes, including the sources, in your current directory, you'd say
jar cvf archiveName.jar *.class *.java
To list the contents of a JAR file, you'd say
jar tvf archiveName.jar
and to unpack a jar archive, you'd say
jar xvf archiveName.jar
If you don't want to unpack the entire archive, but would like to extract a single class, you'd say
jar xf archiveName.jar className.java
or
jar xf archiveName.jar className.class
as the case may be.
TERMINAL I/O
Let's now compare simple C, C++, and Java programs for eliciting information from a user and then printing something out in response on the terminal.
Here is a C program that asks the user to type in a sequence of integers, all in one line. The integers are allowed to be anywhere in a line, not necessarily starting at the beginning, and the entry of the data is considered completed when the user presses ‘Enter’ on the keyboard. The program sums up all the integers and types out the sum.
Therefore, if a user types in
####3#######56##20#1#####19########
where # stands for a space, the program should print out 99. The following C program does the job.
/* TermIO.c */
#include
main()
{
int i;
int sum = 0;
char ch;
printf("Enter a sequence of integers: ");
while ( scanf( "%d", &i ) == 1 ) { /* (A) */
sum += i; /* (B) */
while ( ( ch = getchar() ) & = ' ') /* (C) */
;
if ( ch == '\n' ) break;
ungetc( ch, stdin ); /* (D) */
}
printf( "The sum of the integers is: %d\n", sum );
return 0;
}
The integers are read in by the scanf() function call in line (A) and the summing of the numbers done in line (B). Lines (C) through (D) take care of the following property of scanf: Most conversion specifiers for this function skip over the whitespace characters—meaning the tabs, the space, the newline character, and so on—before the beginning of an input item (and not after). Therefore, after consuming an integer, scanf will simply wait for the next integer, ignoring any blank spaces and the end of the data entry line. This creates a problem at the end of the data line when the user hits "Enter"—scanf will simply gobble up the newline character and wait for the next integer. The statements in lines (C) through (D) peek ahead, while consuming blank spaces, and look for the newline character in case the user has hit "Enter" on the keyboard. If the character found after all the blank spaces have been consumed does not turn out to be a newline character, we put it back in the input stream in line (D).
Here is a C++ program that does the same thing:
//TermIO.cc
#include
using namespace std;
int main()
{
int sum = 0;
cout << "Enter a sequence of integers: ";
int i;
while ( cin >> i ) { //(A)
sum += i;
while ( cin.peek() == ' ' ) cin.get(); //(B)
if ( cin.peek() == '\n' ) break; //(C)
}
cont << "Sum of the numbers is: " << sum << endl;
return 0;
}
This program uses the input stream object cin whose name is usually pronounced "c-in" for "console-in." This object is of type istream and it knows how to read data from a user's terminal. The expression in line (A)
cin >> i;
causes the input operator ’>>’, which is also known as the extraction operator, to extract one int at a time from the input stream object cin. As the user makes keystrokes, the corresponding characters are entered into the operating system's keyboard buffer and then, when the user hits the "Enter" key on the keyboard, the operating system transfers the contents of the keyboard buffer into the cin stream's internal buffer. The operator ’>>’ then extracts the needed information from this buffer. Clearly, the program will block if the user did not provide the necessary keystrokes.[11] The operator ’>>’, originally defined to be the right bitwise shift operator, has been overloaded in C++ for extracting information from input stream objects when used in the manner shown here. Because the operator has been overloaded for all the built-in data types, it can be used to extract an int, a float, a double, a string, and so on, from an input stream object.
To understand the controlling expression in line (A) of the while loop:
while ( cin >> i )
the expression
cin >> i
returns the input stream object itself, meaning cin. However, the returned cin will evaluate to false when either the end-of-file is encountered or when the extraction operator runs into an illegal value. As an example of the latter case, if you were trying to read a floating point number into an int variable, the extraction operator, when it runs into the decimal point, would place the input stream object in an error state and cause cin to evaluate to false.
Lines (B) and (C) deal with the fact that the default behavior of the extraction operator ’>>’ skips over the white space characters, which includes blank space, tabs, newlines, and so on. So the controlling expression in line (A) will not by itself stop the while loop when the user hits "Enter" after entering the desired number of integers in a line. We invoke[12] peek() on the object cin in lines (B) and (C) to ascertain the character immediately after the most recently consumed integer. If it's a blank space, we consume it in line (B) by invoking get() on the object cin; and do the same to all the successive blank spaces until there are no more blank spaces left. If the next character is a newline, it would be trapped in line (C) and the while loop of line (A) exited. Otherwise, we continue reading the data in the next iteration of the loop.
This program also demonstrates that, unlike in C, C++ allows you to declare a variable anywhere in a program. We declared the variable i after the cout statement asking the user to enter data.[13] This feature improves the readability of large C++ programs as one can declare variables just before they are actually needed.
We will now show an equivalent Java program:
//TermIO.java
import java.io.*;
class TermIO {
static boolean newline; //(A)
public static void main( String[] args ) {
int sum = 0;
System.out.println( "Enter a sequence of integers:" );
while ( newline == false ) {
String str = readString(); //(B)
if ( str != null ) {
int i = Integer.parseInt( str ); //(C)
sum += i;
}
}
System.out.println( "Sum of the numbers is:" + sum );
}
static String readString() { //(D)
String word = "";
try {
int ch;
while ( ( ch = System.in.read() ) == ' ' ) //(E)
;
if ( ch == '\n' ) { //(F)
newline = true; //(G)
return null; //(H)
}
word += (char) ch; //(I)
while ( ( ch = System.in.read() ) != ' '
&& ch != '\n' ) //(J)
word += (char) ch; //(K)
if ( ch == '\n' ) newline = true; //(L)
} catch( IOException e ) {}
return word; //(M)
}
}
Since Java does not provide a function that can directly read an integer value into an int variable, the logic of the program is slightly more complex than that of the C and the C++ programs shown earlier. To make sense of this program, recall that the program is supposed to extract the integer values from the numbers entered by a user in a single line and the user is allowed to place any number of spaces before the first integer, between the integers, and after the last integer. In other words, we want our program to be able to extract integer numbers from the following sort of a line entered by a user:
####3#######56##20#1#####19########
where the symbol # stands for a space. To explain the working of the program:
The program reads each integer value entered by the user as a string which is of type String, a Java class This is done by invoking the method readString() in line (B). Therefore, for the data entry line shown above, the first string read will correspond to the number 3, the second to the number 56, and so on.
If the string read in the previous step is not null, we invoke the method parseInt of the Integer class in line (C) to convert the string into its integer number value, assuming that the reader did not try to fool the system by typing nondigit characters.[14]
With regard to the readString() method, we strip off all the empty spaces before a string in the while loop in line (E). Each character is read from the user's terminal in this loop by the read() method that of the java.io.InputStream class. The standard input stream System.in is an object of type java.io.InputStream. We invoke the method read() inside a try-catch block since it throws an exception of type IOException that must either be caught or rethrown.
If the last character read in the while loop in line (E) is the newline character, the test in line (F) causes readString to terminate with null for the returned value in line (H). We also set the newline variable to true in line (G) to tell main that the data entry has come to an end. If the last character read in the while loop in line (E) is not the newline character, we then start a new word with this character in line (I).
The while loop that starts in line (J) keeps on adding fresh characters to the word started in line (I) as long as a new character is neither a space nor a newline. If the latest character read in the while loop of line (J) is a newline, we set our flag newline to true in line (L).
No comments:
Post a Comment