Let Us Java 6th Edition 6nbsped Compress 1
Let Us Java 6th Edition 6nbsped Compress 1
com
www.bpbonline.com
Dedicated to
Nalinee and Prabhakar Kanetkar
- Yashavant Kanetkar
iii
About The Author
Through his books and Quest Video Courseware DVDs on C, Java, C++,
Data Structures, VC++, .NET, Embedded Systems, etc. Yashavant
Kanetkar has created, moulded and groomed lacs of IT careers in the last
iv
Acknowledgments
Let Us Java is not an outcome of my work alone. A book on such a
dynamic language required inputs, help and suggestions from several
people. Topmost amongst them were our students at Nagpur training
center and the participants in various seminars and workshops. Their
inputs have gone a long way in getting this book in the shape and form
in which you are holding it.
Over the years, I have used Java to build many applications. All these
practical experiences and usage scenarios are factored into Let Us Java.
I am indebted to Manish Jain of BPB Publications who had a faith in this
book idea, believed in my writing ability, whispered the words of
encouragement and made helpful suggestions from time to time. I hope
every author gets a publisher who is as cooperative, knowledgeable and
supportive as Manish.
I thank my family for enduring the late nights, the clicking keyboard and
mostly for putting up with yet another marathon book effort.
v
Contents
1. An Overview of Java 1
The Evolution 3
The Birth of Java 4
What is Java? 5
Traditional Programming Model
How is Java Different? 7
How Java Addresses Security? 8
Java or C++? 9
The Java Environment
Tools of the Trade 10
Exercise 12
KanNotes 1
2. Getting Started 17
Java Data Types 19
Rules for Constructing Constants 21
Rules for Constructing Variable Names 22
Java Keywords 23
The First Java Program 2
Compilation and Execution 28
One More Program 28
Exercise 29
KanNotes 3
7. Functions 111
What is a Function? 113
Why use Functions? 118
Passing Values between Functions 118
Exercise 122
KanNotes 127
xii
You remain well-grounded when you know your roots. Same is
true about Java. So a look at how it came into existence and
where it stands amongst other programming languages...
1
2 Let Us Java
The Evolution
The Birth of Java
What is Java?
Traditional Programming Model
How is Java Different?
How Java addresses Security?
Java or C++?
The Java Environment
Tools of the Trade
Exercises
KanNotes
Chapter 1: An Overview of Java 3
The Evolution
Approaches to programing keep evolving all the time. These approaches are
more or less driven by the computing needs of those times. When these
needs cannot be addressed by languages of that era, a need is felt for a new
language. These needs have become more and more complex over the
years.
In the early days of computing when the need was that a machine should
somehow be able to execute instructions, programming was done by
manually keying in the binary machine instructions. So long as the
programming task was small, programmers were ready to take the pains of
keying in instructions in binary.
As the tasks became more complex and the program lengths increased,
need was felt for a new language that could make it easier to write
(c) All these languages were not designed around structured programming
principles. Hence, in programs of sizeable length it became difficult to
follow the flow of control.
was the decade of C++. Since C++ was built on foundation of C, it became
easier for programmers to migrate to this new language quite quickly. It
was largely accepted that C++ is a prefect language and there would be
possibly no need for a new language. But this belief got dented as you
would see in the next section.
What is Java?
Java is a programming language developed at Sun Microsystems in 1995. It
was designed by James Gosling. The language derives much of
its syntax from C++ (and its predecessor, C), but is simpler to use than
C++. Reputation of Java has spread wide and far and has captured the
imagination of most software professionals. Literally thousands of
applications are being built today in Java for different platforms including
desktop, web and mobile.
Possibly why Java seems so popular is because it is reliable, portable and
Figure 1.1
Any running program needs to make use of services of an Operating System
(OS) during its execution. These include services like performing
input/output, allocating memory, etc. You must be aware of the fact that
on the same microprocessor, different OS can be used. For example,
suppose there are two laptops having same Intel Pentium microprocessor.
On one laptop one can run Windows whereas on the other, one can run
Linux. But since the way these OSs offer different services is different,
during conversion to machine language these changes have to be
accommodated. So for the same program, machine language instructions
for Intel + Windows combination would be different than those for Intel +
Linux combination. This is shown in Figure 1.2.
Chapter 1: An Overview of Java 7
Figure 1.2
Figure 1.3
Java or C++?
After learning C, it is often a question whether one should migrate to Java
or C++. Answer is both; and that too in any sequence that you want. Though
both are Object Oriented programming languages, neither is an advanced
version of the other. Learning one before the other would naturally help to
learn the second.
It is important to note that both address different sets of problems. C++
primarily addresses complexity, whereas Java addresses portability and
security. In my opinion, both languages would continue to rule the hearts of
programmers for years to come.
As you start learning Java, you would find that there are many features in it
that are similar to C and C++. This is not by accident, but by intent. Java
designers knew that they had to provide a smooth transition path to
learners of Java language. That is why Java uses a syntax which is similar in
many ways to that of C and it follows many of the object oriented features
of C++, though in a refined fashion.
However, if you wish to also develop programs on your machine, you need
Java Developer Kit (JDK). JDK contains tools needed to develop the Java
programs, as well as JRE to run the programs. The tools include compiler
(javac.exe), Java application launcher (java.exe), Appletviewer, etc.
Compiler converts Java code into bytecode. Java application launcher opens
a JRE, loads the class, and calls its main( ) method. Figure 1.4 shows all
these pieces of Java environment.
Figure 1.4
would be a good idea to attempt the exercise on the next page to help you
fix these ideas, before we formally begin learning Java language from next
chapter onwards.
(a) Structured
(b) Object Oriented
(c) Portable
(d) Secure
(e) Suitable for Internet programming
(f) Simple syntax
(g) Architecturally neutral
(h) Management of complexity
[D] Pick up the correct alternative for each of the following questions:
(c) At what stage does byte code get converted into machine language
instructions?
(1) during compilation
(2) during assembly
(3) during preprocessing
(4) during execution
14 Let Us Java
(d) Java is
(1) microprocessor-independent language
(2) platform-independent language
(3) OS-independent language
(4) library-independent language
(e) For the same Java program to run on different devices we need
(1) appropriate JVM for those devices
(2) appropriate compiler for those devices
(3) appropriate interpreter for those devices
(4) appropriate preprocessor for those devices
2 categories of software :
- System software - OS, Compilers, Device Drivers
- Application software - software for desktop/laptop, Web, Mobile
Technologies used in Java world for different platforms :
- Desktop - J2SE, Mobile - J2ME, Web - J2EE
Reasons of popularity of Java :
- Same language for varied applications
- Rapid Application Development (RAD) possible
- Easy development cycle
- Easy to manage large projects
Acronymns:
- API = Application Programming Interface
- JVM = Java Virtual Machine
- JRE = Java Runtime Environment
- JDK = Java Development Kit
API = Library of classes in form of packages
17
18 Let Us Java
F our important aspects of any language are the way it stores data, the
way it operates upon this data, how it accomplishes input and
output, and how it lets you control the sequence of execution of
instructions in a program. We would discuss the first three of these
building blocks in this chapter.
For example, an integer data type can take values in the range
-2147483648 to +2147483647, and operations like addition, subtraction,
multiplication, division, etc., can be performed on it. Similarly, a boolean
data type can take a value true or false, and permits comparison
operations on it.
Based on where a data type can be created in memory, it is called a
primitive type (often also known as a value type) or a reference type.
Java forces primitive data types to get created only in stack and
reference types only on heap. For example, an integer (like 2341) always
gets created in stack, whereas a string (like "Quest") always gets created
in heap. The guiding principle on the basis of which Java does this
decision making is all data types that are small in size are created in
stack, and all those that occupy larger memory chunks are created in
heap.
A primitive type as well as a reference type can be further categorized
into pre-defined and user-defined categories. Here pre-defined means
the data types that Java provides ready-made, whereas user-defined
means those data types which a common user like us can create. For
example, integer is a pre-defined primitive data type, whereas an
Enumeration is a user-defined value type. Figure 2.1 shows the different
categories of data types available in Java. Note that the pre-defined
value types are often also called Primitives.
20 Let Us Java
Figure 2.1
Amongst all the data types shown in Figure 2.1, to begin with, we would
concentrate on the pre-defined value data types. To use the data types
in a Java program, we have to create constants and variables. A constant
is nothing but a specific value from the range of values offered by a data
type, whereas a variable is a container which can hold a constant value.
The container is typically a memory location and the variable is the
name given to the location in memory. For example, if we use an
expression x = 5, then the constant value 5 would be stored in a memory
location and a name x would be given to that location. Whenever, we
wish to retrieve and use 5, we just have to use the variable name x. This
is shown in Figure 2.2.
Figure 2.2
Chapter 2: Getting Started 21
As the name suggests, value of a constant cannot change (fixed),
whereas, value of a variable's can change (vary). A constant is often
called a literal, whereas, a variable is also known as an identifier. Figure
2.3 gives list of commonly used pre-defined primitive data types along
with the range of values that they can take and the numbers of bytes
they occupy in memory.
Figure 2.3
There are certain rules that one needs to observe while creating
constants and variables. These are discussed below.
(c) The bytes occupied by each constant are fixed and do not change
from one compiler to another.
(d) Variable names are case-sensitive. So, abc, ABC, Abc, aBc, AbC are
treated as different variables.
(a) A variable name usually begins with an alphabet. Ex. speed, average
(c) If a variable name containing multiple words, the words are either
connected using underscore or follow a camel-case notation. Ex.
current_speed, currentSpeed, avg_salary, avgSalary.
While following these rules and conventions, one must avoid the
temptation of creating long variable names as it unnecessarily adds to
the typing effort.
The rules remain same for constructing variables of any type. Naturally
the question follows how is Java able to differentiate between these
variables? This is a rather simple matter. Java compiler makes it
compulsory for us to declare the type of any variable name that we wish
to use in a program. Here are a few examples showing how this is done.
Java Keywords
Keywords are the words whose meaning has already been explained to
the Java compiler. When we make the declaration like the one shown
below,
int age ;
Figure 2.4
24 Let Us Java
(b) Blank spaces may be inserted between two words to improve the
readability of the statement. However, no blank spaces are allowed
within a variable, constant or keyword.
Let us now write our first Java program. It would simply calculate simple
interest for a set of values representing principal, number of years and
rate of interest.
Figure 2.5
main( ) is a function. A function contains a set of statements.
Though a Java program can contain multiple functions, to begin
with, we would concentrate on only those programs which have
only one function. All statements that belong to main( ) are
enclosed within a pair of braces { } as shown below.
public static void main ( String[ ] args )
{
statement 1 ;
statement 2 ;
statement 3 ;
}
Chapter 2: Getting Started 27
The way functions in a calculator return a value, similarly, functions
in Java also return a value. Since we do not wish to return any value
from main( ) function, we have to specify this using the keyword
void before main( ). main( ) is always preceded by the keyword
static. The purpose of this keyword and detailed working of
functions would be discussed in Chapters 9 and 7 respectively.
Any variable used in the program must be declared before using it.
For example,
int p, n ; /* declaration */
float r, si ; /* declaration */
si = p * n * r / 100 ; /* usage */
Any Java statement always ends with a semicolon ( ; ). For example,
float r, si ;
r = 15.5f ;
In the statement,
si = p * n * r / 100 ;
(b)
t
/* Calculation of average */
package calofavg ;
public class CalOfAvg
Chapter 2: Getting Started 29
{
public static void main ( String[ ] args )
{
int x, y, z, avg ;
x = 73 ;
y = 70 ;
z = 65 ;
avg = ( x + y + z ) / 3 ;
System.out.println ( avg ) ;
}
}
[B] Point out the errors, if any, in the following Java statements:
(a) int = 314.562f * 150 ;
(b)
(c)
(e) k = ( a * b ) ( c + ( 2.5fa + b ) ( d + e ) ;
(j) k = ( ( a * b ) + c ) ( 2.5f * a + b ) ;
(k) a = b = 3 = 4 ;
[C] Pick up the correct alternative for each of the following questions:
(a) Which of the following is the correct way to write a comment?
(1) // This is a comment
(2) / This is a comment
(3) /* This is a comment
(4) /* This is a /* comment */ */
(b) The maximum value that an integer constant can have is:
(1) -2147483647
(2) 2147483647
(3) 3.4 × 1038
(4) -3.4 × 1038
(c) A Java variable cannot start with:
(1) An alphabet
(2) A number
(3) A special symbol other than underscore
(4) Both (2) and (3) above
(d) Which of the following is odd one out?
(1) +
(2) -
(3) /
(4) **
(b) Assume a suitable value for distance between two cities (in km.).
Write a Java program to convert and print this distance in meters,
feet, inches and centimeters.
Chapter 2: Getting Started 31
(c) Assume suitable values for marks obtained by a student in five
different subjects are input through the keyboard. Write a Java
program to find out the aggregate marks and percentage marks
obtained by the student. Assume that the maximum marks that can
be obtained by a student in each subject is 100.
(e) Assume suitable values for length and breadth of a rectangle, and
radius of a circle. Write a Java program to calculate the area and
perimeter of the rectangle, and the area and circumference of the
circle.
Primtive types :
- Char - 2 bytes
- Integers - byte, short, int, long ( sizes - 1, 2, 4, 8 bytes)
- Real - float, double ( sizes - 4, 8 bytes respectively)
- Boolean - true / false (1 bit)
Derived types (classes) :
- Library : String, System, Exception, etc.
- User-defined : CalOfSi, CalOfAvg, etc.
Variable names are case-sensitive and should begin with an alphabet
3 types of comments :
- Single line - //
- Multiline -
- Documentation -
Well begun is half done! Learn the basic building blocks of Java
language...
33
34 Let Us Java
Integer Types
Java provides 4 types of integers byte, short, int and long of sizes 1
byte, 2 bytes, 4 bytes and 8 bytes respectively. Figure 3.1 shows
different types of integers available in Java along with their sizes and
ranges.
Figure 3.1
Let us now discuss some finer points associated with integer types.
(a) By default, number without a decimal point is treated as an int.
During assignment, if the value being assigned exceeds the range of
the variable, an error occurs. This is shown below.
byte a = 300 ; // error
short b = 40000 ; // error
int c = 2200000000 ; // error
Real Types
Real numbers can be represented as float and double. The difference
between them is the number of bytes occupied by each, their ranges
and their precision. This is shown in Figure 3.2.
Figure 3.2
There are some finer points associated with these real types. These are
discussed below.
(a) By default, a number with a decimal point is treated as a double. If
we wish to treat it as a float, we need to add a suffix f or F at the end
of it to make it a float, as shown below.
float x = 3.5 ; // error
float y = 3.5f ; // correct
double d = 3.5f ; // correct
double e = 3.5 ; // correct
(b) If we want an integer number to be treated as double, use the suffix
d or D, for example,
double a = 3d ;
boolean a = false ;
System.out.println ( "a = " + a ) ;
38 Let Us Java
boolean b = 4 > 2 ;
System.out.println ( "b = " + b ) ;
a = false
b = true
int a ; boolean b ;
a = 3 < 4 ; // error
b = 3 < 4 ; // works
Receiving Input
In the simple interest program of Chapter 2, we assumed the values of
p, n and r to be 1000.5, 3 and 15.5. Every time we run this program we
would get the same value for simple interest. To calculate simple
interest for some other set of values, we would be required to replace
the existing set with the new set of values, and again compile and
execute the program. Thus, the program is not general enough to
calculate simple interest for any set of values without being required to
make a change in the program.
To make the program general, the program should ask the user to
supply the values of p, n and r through the keyboard during execution.
When the user supplies these values, they can be read by the program
using the functions of Scanner class as illustrated in the program given
below.
Let us now see what happens when we execute this program. To begin
with, the first println( ) outputs
the output window of NetBeans. In this window we are supposed to
supply three numbers either in same line or in three distinct lines.
Our program should read each of these numbers. To do this, we have to
first create an object of type Scanner through the statement
import java.util.* ;
package sibyreceivinginput ;
import java.io.* ;
public class SiByReceivingInput
{
public static void main ( String[ ] args ) throws Exception
{
float p, r, si ;
int n ;
InputStreamReader isr ;
BufferedReader br ;
isr = new InputStreamReader ( System.in ) ;
br = new BufferedReader ( isr ) ;
System.out.println ( "Enter values of p, n and r" ) ;
p = Float.parseFloat ( br.readLine( ) ) ;
n = Integer.parseInt ( br.readLine( ) ) ;
r = Float.parseFloat ( br.readLine( ) ) ;
si = ( p * n * r ) / 100 ;
System.out.println ( "Simple interest = Rs. " + si ) ;
}
}
Command-line Arguments
In our simple interest program, instead of reading values using the
Scanner object during execution, we can provide the values at
command-line itself. This would be another way of supplying input to
the program.
Now you can compile and execute the program as usual using F6. When
we execute the program, the command-line arguments are available in
main( ) as an array (collection) of strings in args. From this collection we
can access the individual strings using args[ 0 ], args[ 1 ] and args[ 2 ].
These strings contain the values that we gave as command-line
arguments. However, we cannot perform arithmetic on these strings. So
it is necessary to first convert them into numbers. This conversion into
float and int is done using the statements:
p = Float.parseFloat ( args[ 0 ] ) ;
n = Integer.parseInt ( args[ 1 ] ) ;
r = Float.parseFloat ( args[ 2 ] ) ;
Note that int and float are primitives whereas Float and Integer are
classes. These classes have functions like parseFloat( ) and parseInt( ).
The converted numbers are assigned to p, n and r. Finally, simple
interest is calculated and printed out. To be able to use Integer and
Float classes we must import them from java.lang package.
Java Instructions
Now that we have written a few programs, let us look at the instructions
that we have used in these programs. There are basically five types of
instructions in Java. The purpose of each of these instructions is given in
Figure 3.3.
Figure 3.3
Chapter 3: Java Data Types and Instructions 43
Since, the elementary Java programs would usually contain only the type
declaration and the arithmetic instructions; we would discuss only these
two instructions at this stage. The other types of instructions would be
discussed in detail in the subsequent chapters.
(a) While declaring the type of variable we can also initialize it as shown
below.
int i = 10, j = 25 ;
float a = 1.5f, b = 1.99f + 2.4f * 1.44f ;
(b) The order in which we define the variables is sometimes important
sometimes not. For example,
int i = 10, j = 25 ;
is same as
int j = 25, i = 10 ;
However,
is alright, but
int a, b, c, d ;
a = b = c = 10 ;
int a = b = c = d = 10 ;
Arithmetic Instruction
A Java arithmetic instruction consists of a variable name on the left hand
side of =, and variable names and constants on the right hand side of =.
The variables and constants appearing on the right hand side of = are
connected using arithmetic operators like +, -, *, and /.
Here,
int z ;
z=x+y;
I think a few practical examples shown in Figure 3.4 would put the issue
beyond doubt.
Figure 3.4
Here are a few more implicit conversion rules.
(a) An operation between any byte, short, int or chars results into an
int. Hence, the result of these operations should always be assigned
to an int variable. Otherwise, an error would be reported. The
following code segment illustrates this:
byte a = 100, b = 50 ;
byte c = a + b ; // error, as resulting int cannot be assigned to a byte
int d = a + b ; // works
short l = 45, m = 20 ;
short n = l + m ; // error, as resulting int can t be assigned to a short
int p = l + m ; // works
char ch = 'A', dh = 'B' ;
char eh = ch + dh // error, resulting int can t be assigned to a char
int fh = ch + dh ; // works
(b) During assignment, the type on left hand side may not be same as
type on right hand side. In such a case, if the value of right hand side
is within the range of type on left hand side then no error results.
The following code snippet illustrates this point:
byte s ;
s = 20 ; // though 20 is an int, it is within range of byte
short a ;
48 Let Us Java
If value of right hand side is not within the range of type on left hand
side then an error is reported. For example,
byte s ;
s = 200 ; // error as 200 is not within range of byte
short a ;
a = 40000 ; // error as 40000 is not within range of short
(c) A char can be implicitly converted to short, int, long, float, or
double. However, there are no implicit conversions from other types
to the char type. The following code snippet illustrates this:
float a = 'A' ; // works
char ch = a ; // error
Explicit Conversion
At times we are required to explicitly convert one type into another. This
is done using a type casting operation. Let us consider an example.
float a, b ;
int x = 6, y = 4 ;
a=x/y;
b = ( float ) x / y ;
System.out.println ( "Value of a = " + a ) ;
System.out.println ( "Value of b = " + b ) ;
Value of a = 1.0
Value of b = 1.5
float a = 6.35f ;
System.out.println ( "Value of a on type casting = " + ( int ) a ) ;
Chapter 3: Java Data Types and Instructions 49
System.out.println ( "Value of a = " + a ) ;
Hierarchy of Operations
While executing an arithmetic statement, which has two or more
operators, we may have some problems as to how exactly does it get
executed. For example, does the expression 2 * x - 3 * y correspond to
(2x) - (3y) or to 2(x - 3y)? Similarly, does A / B * C correspond to A / (B *
C) or to (A / B) * C? To answer these questions satisfactorily, one has to
Hierarchy decides the order in
which the operations in an expression are performed. The hierarchy of
commonly used operators is shown in Figure 3.5.
Figure 3.5
Within parentheses the same hierarchy as mentioned in Figure 3.5 is
operative. Also, if there are more than one set of parentheses, the
operations within the innermost parentheses would be performed first,
followed by the operations within the second innermost pair and so on.
An example would clarify the issue further.
Example 3.1: Determine the hierarchy of operations and evaluate the
following expression, assuming that i is an integer variable:
i=2*3/4+4/4+8-2+5/8
i=2*3/4+4/4+8-2+5/8
i=6/4+4/4+8-2+5/8 operation: *
i=1+4/4+8-2+5/8 operation: /
i=1+1+8-2+5/8 operation: /
i=1+1+8-2+0 operation: /
i=2+8-2+0 operation: +
i = 10 - 2 + 0 operation: +
i=8+0 operation : -
i=8 operation: +
Note that 6 / 4 gives 1 and not 1.5. This is because operations between
two integers always evaluates to an integer. Similarly, 5 / 8 evaluates to
zero, since 5 and 8 are integers.
Associativity of Operators
When an expression contains two operators of equal priority, the tie
between them is settled using the associativity of the operators. All
operators in Java have either Left to Right associativity or Right to Left
associativity. Let us understand this with the help of a few examples.
Consider the expression a = 3 / 2 * 5 ;
Here there is a tie between operators of same priority, that is between /
and *. This tie is settled using the associativity of / and *. Both enjoy Left
to Right associativity. Therefore firstly / operation is done followed by *.
Consider one more expression.
a=b=3;
z=a*b+c/d;
Here * and / enjoy same priority and same associativity (Left to Right).
Compiler is free to perform * or / operation as per its convenience, since
no matter which is performed earlier, the result would be same.
Note that the precedence and associativity of all operators is
predetermined and we cannot change it.
Chapter 3: Java Data Types and Instructions 51
Constant Variables
Many a times we have to use constant values in a program. For example,
values of pi, Plank's constant or Avogadro's number. Suppose we need
the value of pi at several places in a Java program. It would be a bad idea
if we directly use the value 3.14 at all these places, as we might commit
a typing error at one of the places and this error would go unnoticed by
the compiler. To avoid this we can store the value 3.14 in a float variable
pi, and then use this variable wherever we need the value of pi. This
solution suffers from the limitation that being a variable, pi is liable to
change. So if by mistake we assign a new value to this variable, the
compiler would not be able to report this as an error.
A solution for this is to declare the variable as a constant variable using
the keyword final. This is illustrated in the code snippet given below.
final float pi = 3.14f ;
float radius = 1.5f ;
float area = pi * radius * radius ;
Output:
Average = 346
Percentage = 69.5
In the first println( ) we have merely printed the string Output:, whereas
in the next we have converted the integer avg and float per into strings
and then appended them at the end of Average = and Percentage =,
52 Let Us Java
The different format specifiers that can be used in the format string of
the format( ) function are shown in Figure 3.7.
Figure 3.7
We can also provide four optional specifiers with the above format
specifications width, zero, comma and sign. Their usage is shown in the
following code snippet:
0000762432
+762432
762,432
+762,432
5.05
413.25
(a)
Chapter 3: Java Data Types and Instructions 55
7.7b ( xy + a ) / c - 0.8 + 2b
(d) A=
( x + a ) (1 / y )
System.out.println ( si, p, n, r ) ;
(d) All command-line arguments are received by main( ) as strings.
59
60 Let Us Java
Decisions! Decisions!
The if-else statement
More Complex Decision Making
The else if Clause
The & and | Operators
The ! Operator
Hierarchy of Operators Revisited
A Word of Caution
The Conditional Operators
Exercises
KanNotes
Chapter 4: Decision Control Instruction 61
Decisions! Decisions!
In the programs written in Chapters 2 and 3, the instructions in them got
executed sequentially. However, in many programming situations, we
want one set of instructions to get executed in one situation, and an
entirely different set in another situation. Such situations are dealt with
in Java programs using a decision control instruction. A decision control
instruction can be implemented in Java using:
(a) The if-else statement
(b) The conditional operators
Now let us learn each of these and their variations in turn.
The keyword if tells the compiler that what follows is a decision control
instruction. The condition following the keyword if is always enclosed in
a pair of parentheses. If the condition is true, then the statements 1, 2
62 Let Us Java
evaluated in Java.
Figure 4.1
The relational operators should be familiar to you except for the
equality operator == and the inequality operator !=. Note that = is used
for assignment, whereas, == is used for comparison of two quantities.
Let us understand the usage of relational operators using a simple
program based on Example 4.1.
Example 4.1: In a company an employee is paid as under:
If his basic salary is less than Rs. 1500, then HRA = 10% of basic salary
and DA = 90% of basic salary. If his salary is either equal to or above Rs.
1500, then HRA = Rs. 1500 and DA = 98% of basic salary. If the
employee's salary is input through the keyboard write a program to find
his gross salary.
Now let us look at the program that implements this logic.
(a)
(b) Notice that the else is written exactly below the if. The statements
in the if block and those in the else block have been indented to the
right. This formatting convention is followed throughout the book to
enable you to understand the working of the program better.
64 Let Us Java
(c) Had there been only one statement to be executed in the if block
and only one statement in the else block we could have dropped the
pair of braces.
(d) In the first run of the program, the condition evaluates to true, as
1200 (value of bs) is less than 1500. In this case the statements in
the if block get executed. In the second run, the condition evaluates
to false, as 2000 (value of bs) is greater than 1500. Now the
statements in the else block get executed.
N
The first two operators, && and ||, allow two or more conditions to be
combined in an if statement. Let us see how they are used in a program.
Consider the following example:
Example 4.2: The marks obtained by a student in 3 different subjects are
input through the keyboard. The student gets a division as per the
following rules:
Percentage above or equal to 60 - First division
Percentage between 50 and 59 - Second division
Percentage between 40 and 49 - Third division
Percentage less than 40 - Fail
Write a program to determine the division obtained by the student.
Here is the program that implements this logic.
if ( per >= 60 )
System.out.println ( "First division" ) ;
if ( per < 40 )
System.out.println ( "Fail" ) ;
}
}
As can be seen from the second if statement, the && operator is used to
if ( per >= 60 )
System.out.println ( "First division" ) ;
else if ( per >= 50 )
System.out.println ( "Second division" ) ;
else if ( per >= 40 )
System.out.println ( "Third division" ) ;
else
System.out.println ( "Fail" ) ;
}
}
You can note that this program reduces the indentation of the
statements. In this case, every else is associated with its previous if. The
last else goes to work only if all the conditions fail. Also, if the first
condition is satisfied, other conditions are not checked. Even in else if
ladder, the last else is optional.
Since all these cases lead to the driver being insured, they can be
combined together using && and || as shown in the program below.
else
System.out.println ( "Driver is not insured" ) ;
}
}
int a = 1, b = 1, c = 5, d ;
if ( a > 3 && ( b = c + 4 ) > 1 )
d = 35 ;
System.out.println ( b ) ;
Note that we have used the operator & instead of &&. Using & ensures
that both conditions are evaluated even if the first condition turns out to
be false. Figure 4.2 summarizes the effects of using &&, ||, & and |
operators.
Chapter 4: Decision Control Instruction 69
Figure 4.2
The ! Operator
So far we have used only the logical operators && and ||. The third
logical operator is the NOT operator, written as !. This operator reverses
the result of the expression it operates on. For example, if the
expression evaluates to true, then on applying ! operator to it results
into a false. Vice versa, if the expression evaluates to false, then on
applying ! operator to it makes it true. Here is an example of the NOT
operator applied to a relational expression.
! ( y < 10 )
if ( ! flag )
if ( flag == 0 )
Does the NOT operator sound confusing? Avoid it if you want, as the
same thing can be achieved without using the NOT operator.
Figure 4.3 summarizes the working of all the three logical operators.
70 Let Us Java
Figure 4.3
Figure 4.4
A Word of Caution
A common mistake while using the if statement is to write a semicolon
(;) after the condition, as shown below.
if ( i == 5 ) ;
System.out.println ( "Reached here" ) ;
Chapter 4: Decision Control Instruction 71
The ; makes the compiler to interpret the statement as if you have
written it in following manner:
if ( i == 5 )
;
System.out.println ( "Reached here" ) ;
(a) int x, y ;
y=x>5?3+4:4+7;
(b) int y ;
y = a >= 65 && a <= 90 ? 1 : 0 ;
System.out.println ( y ) ;
This would work as the sine or cosine value returned would get
assigned to j.
Ex.: int x ;
x = act == 1 ? System.out.println ( "Amitabh" ) :
System.out.println ( "All and sundry" ) ;
Ex.: double j ;
System.out.println ( j = 3 > 4 ? 4.4 : 3.3 ) ;
System.out.println ( 3 > 4 ? 4.4 : 3.3 ) ;
g=a>b?a:b;
triangle is valid if the sum of two sides is greater than the largest of
the three sides.
(g) If the three sides of a triangle are entered through the keyboard,
write a program to check whether the triangle is isosceles,
equilateral, scalene or right angled triangle.
(h) Given the length and breadth of a rectangle, write a program to find
whether the area of the rectangle is greater than its perimeter.
(i) Given the coordinates (x, y) of a center of a circle and its radius,
write a program which will determine whether a point lies inside
the circle, on the circle or outside the circle. (Hint: Use Math.sqrt( )
and Math.pow( ) functions)
(j) Given a point (x, y), write a program to find out if it lies on the X-
axis, Y-axis or on the origin, viz. (0, 0).
(k) Any year is entered through the keyboard, write a program (using
logical operators) to determine whether the year is leap or not.
(l) What will be the result of the following expressions:
int a = 10 ;
int b = 20 ;
a > 5 && b != 5
a != 0 & b < 34
a > 45 || b > 45
a == 10 | b == 20
a > 5 && b != 3 || a + b >= 10
a > 5 || b != 3 && a + b >= 10
{ } are necessary
a = b is assignment. a == b is comparison
Hierarchy :
! * / % +- < > <= >= && & || | =
Unary operator - needs only 1 operand. Ex. !
? : can be nested
79
80 Let Us Java
Loops
The while Loop
Tips about while
The for Loop
Partial for Loops
Nesting of Loops
Multiple Initializations in the for Loop
The do-while Loop
The break Statement
The continue Statement
Common usage
Exercises
KanNotes
Chapter 5: Loop Control Instruction 81
Loops
The versatility of the computer lies in its ability to perform a set of
instructions repeatedly. This involves repeating some portion of the
program either a specified number of times or until a particular
condition is being satisfied. There are three methods by way of which
we can repeat a part of a program. They are:
The program executes all statements after the while 3 times. The logic
for calculating the simple interest is written within a pair of braces
immediately after the while keyword. These statements form what is
while loop. The parentheses after the while
contain a condition. So long as this condition remains true, all
statements within the body of the while loop keep getting executed
repeatedly. To begin with, the variable count is initialized to 1 and every
time the simple interest logic is executed, the value of count is
incremented by one. The variable count is often
Chapter 5: Loop Control Instruction 83
When the value of count reaches 4, the condition in
while fails and the loop is terminated.
is same as
while ( i <= 10 )
{
i=i+1;
}
Almost always, the while must test a condition that will eventually
become false, otherwise the loop would be executed forever,
indefinitely.
int i = 1 ;
while ( i <= 10 )
System.out.println ( i ) ;
System.out.println ( i ) ;
i=i+1;
}
Instead of incrementing a loop counter, we can even decrement it
and still manage to get the body of the loop executed repeatedly.
This is shown below.
int i = 5 ;
while ( i >= 1 )
{
System.out.println ( "Make the computer literate!" ) ;
i=i-1;
}
It is not necessary that a loop counter must only be an int. It can
even be a float.
float a = 10.0f ;
while ( a <= 10.5f )
{
System.out.println ( "Raindrops on roses..." ) ;
System.out.println ( "...and whiskers on kittens" ) ;
a = a + 0.1f ;
}
Even floating-point loop counters can be decremented. Once again,
the increment and decrement could be by any value, not necessarily
1.
What will be the output of the following code snippet?
int i = 1 ;
while ( i <= 10 ) ;
{
System.out.println ( i ) ;
i=i+1;
}
reason is, we have carelessly given a ; after the while. This would
make the loop work like this...
while ( i <= 10 )
;
Chapter 5: Loop Control Instruction 85
{
System.out.println ( i ) ;
i=i+1;
}
int i = 1 ;
while ( i++ < 10 ) // first test the condition, then increment i
while ( ++i < 10 ) // first increment i, then test the condition
j = ++i ; // first increment i, then assign to j
j = i++ ; // first assign i to j, then increment i
System.out.println ( ++i ) ; // first increment i, then print it
System.out.println ( i++ ) ; // first print i, then increment it
86 Let Us Java
Let us write down the simple interest program using for. Compare this
program with the one, which we wrote using while.
int i = 1 ;
for ( ; i <= 10 ; i++ )
System.out.println ( i ) ;
Nesting of Loops
The way if statements can be nested, similarly whiles and fors can also
be nested. To understand how nested loops work, look at the program
given below.
When you run this program, you will get the following output:
r = 1 c = 1 sum = 2
r = 1 c = 2 sum = 3
r = 2 c = 1 sum = 3
r = 2 c = 2 sum = 4
r = 3 c = 1 sum = 4
r = 3 c = 2 sum = 5
Here, for each value of r, the inner loop is cycled through twice, with the
variable c taking values from 1 to 2. The inner loop terminates when the
value of c exceeds 2, and the outer loop terminates when the value of r
exceeds 3.
do
{
this ;
and this ;
and this ;
} while ( this condition is true ) ;
i=2;
while ( i <= num - 1 )
{
if ( num % i == 0 )
{
System.out.println ( "Not a prime number" ) ;
break ;
}
i++ ;
}
if ( i == num )
System.out.println ( "Prime number" ) ;
}
}
In this program, the moment num % i turns out to be zero, (i.e., num is
exactly divisible by i),
the control breaks out of the while loop.
Why does the program require the if statement after the while loop at
all? Well, there are two ways the control could have reached outside the
while loop:
When the loop terminates in the second case, it means that there was
no number between 2 to num - 1 that could exactly divide num. That is,
num is indeed a prime. If this is true, the program should print out the
message
92 Let Us Java
The keyword break, breaks the control only from the while in which it is
placed. So in case of nested loops if we use break in the inner loop, the
inner loop would be terminated. What if we wish to break out of the
outer loop? Well, we just have to name the outer loop and use break to
take the control out of the named loop. The following program
illustrates how this can be done:
Note that we have now given a name to the out while loop first. When
the condition j == 150 is satisfied the statement break first gets
executed. As a result, control goes outside the loop named first, i.e., the
outer while loop.
12
21
Note that when the value of i equals that of j, the continue statement
takes the control to the for loop (inner) bypassing the rest of the
statements pending execution in the for loop (inner).
The way while working in nested loops we can break the control out of
the desired loop by naming a loop, likewise, we can use continue with
named loops. This is shown below.
bypassing the conditional test. A continue sends you straight to the test
at the end of the loop.
Common Usage
In principle, what can be achieved using one loop can always be
achieved using the other two loops. However, in practice people use the
three loops for following purposes:
If you also follow the same practice then while reading a program you
can recognize the purpose of the loop just by looking at the type of the
loop used.
(d) Write a program to print all prime numbers from 1 to 300. (Hint:
Use nested loops, break and continue).
(e) Write a program to generate all combinations of 1, 2 and 3 using for
loops.
(f) According to a study, the approximate level of intelligence of a
person can be calculated using the following formula:
i = 2 + ( y + 0.5 x )
2 3 4
x 1 1 x 1 1 x 1 1 x 1
....
x 2 x 2 x 2 x
What can be done using one loop can always be done using the other
two
Usual usage :
while - to repeat something an unknown number of times
for - to repeat something a fixed number of times
do - while - to repeat something at least once
Equivalent forms of 3 loops :
i = i + 5 is same as i += 5
101
102 Let Us Java
I n real life, we are often faced with situations where we are required to
make a choice between a number of alternatives rather than only one
or two. For example, which school to join, or which hotel to visit, or
which movie to see, etc. Serious Java programming is same; the choice
we are asked to make is more complicated than merely selecting
between two alternatives. Java provides a case control instruction that
allows us to handle such cases effectively; rather than using a series of if
statements. Case control instruction is, in fact, the topic of this chapter.
switch ( expression )
{
case constant 1 :
do this ;
break ;
case constant 2 :
do this ;
break ;
case constant 3 :
do this ;
break ;
default :
do this ;
break ;
}
The expression following the keyword switch is any Java expression that
will yield an integer or a character value. The keyword case is followed
by an integer or a character constant. Each constant in each case must
be different from all the others. The
switch represent any valid Java statement.
What happens when we run a program containing a switch? First, the
expression following the keyword switch is evaluated. The value it gives
is then matched, one-by-one, against the constant values that follow the
case statements. When a match is found, the program executes the
statements following that case until a break is encountered. On
104 Let Us Java
switch ( i )
{
case 1 :
System.out.println ( "I am in case 1" ) ;
break ;
case 2 :
System.out.println ( "I am in case 2" ) ;
break ;
case 3 :
System.out.println ( "I am in case 3" ) ;
break ;
default :
System.out.println ( "I am in default" ) ;
break ;
}
}
}
I am in case 2
All that we can have after the case is a byte, short, int, char or
string constant or an expression that evaluates to one of these
constants. Even a float, double, long or boolean is not allowed.
(f) From JDK 7 onwards strings can also be checked using switch.
(g) The advantage of switch over if is that it leads to a more structured
program and the level of indentation is manageable, more so, if
there are multiple statements within each case of a switch.
(h) We can check the value of any expression in a switch. Thus, the
following switch statements are legal:
switch ( i + j * k )
switch ( 23 + 45 % 4 * k )
(i) Expressions can also be used in cases provided they are constant
expressions. Thus case 3 + 7 is correct, however, case a + b is
incorrect.
(j) The break statement when used in a switch takes the control
outside the switch. However, use of continue will not take the
control to the beginning of switch as one is likely to believe. This is
because switch is not a looping statement, unlike while, for or do-
while.
106 Let Us Java
switch ( ch )
{
case 'a' :
case 'A' :
System.out.println ( "a as in ashar" ) ;
break ;
case 'b' :
case 'B' :
System.out.println ( "b as in brain" ) ;
break ;
default :
System.out.println ( "wish you knew alphabets" ) ;
break ;
}
}
}
Here, we are making use of the fact that once an empty case is
satisfied; the control simply falls through to the next case till it
Chapter 6: Case Control Instruction 107
break statement. That is why if an alphabet a is
entered, the is satisfied and since there are no statements
to be executed in this case, the control automatically reaches the
next case, i.e., case and executes all the statements in this case.
Also note that any non-empty case has to have a break statement
at the end of it.
Also observe the way we have read the character from the
keyboard. Instead of using the usual Scanner object, we are using
the System.in.read( ) and converting its return value into a char.
(a), (b) and (c) above may lead you to believe that these are obvious
disadvantages with a switch
limitations with if-else. Then why use a switch at all? For speed switch
works faster than an equivalent if-else ladder. How? Well, this is
because the compiler generates a jump table for a switch during
compilation. As a result, during execution it simply refers the jump table
to decide which case should be executed, rather than actually checking
which case is satisfied.
Note that a lookup in the jump table is faster than evaluation of a
condition, especially if the condition is complex.
108 Let Us Java
(c) case a + b :
(d) case 1.5f :
(e) case true :
(f) case 8 % 5 + c / d :
(g) case ( temp <= 20 ) :
(h) case a || b :
[B] State whether the following statements are True or False:
(a) Floats or doubles cannot be checked in a switch.
(b) If there are multiple statements in a case then they should be
enclosed within { }.
(c) A case cannot be followed by a variable expression.
(d) Nested switch statements are not allowed.
(e) If break in a switch gets executed the program execution gets
terminated.
(f) A switch statement works slower than an equivalent if-else
statement.
[C] Write a menu driven program which has following options:
1. Factorial of a number
2. Prime or not
3. Odd or even
4. Exit
Once a menu item is selected the appropriate action should be
taken and once this action is finished, the menu should reappear.
continue to work.
(Hint: Make use of an infinite while and a switch statement).
One more form of decision making can be done using switch - case -
default
switch should not be used for checking ranges or for solving a yes /
no problem
General form :
switch ( expression ) // expression can be constant / variable
{
case constant expression :
break ;
case constant expression :
break ;
default :
}
If a case fails, control jumps to the next case
continue DOES NOT take the control to the beginning of the switch
111
112 Let Us Java
What is a Function?
Why use Functions?
Passing Values between Functions
Exercises
KanNotes
Chapter 7: Functions 113
What is a Function?
A function is a self-contained block of statements that perform a
coherent task of some kind. Every Java program has one or more
functions in it. Let us now look at a simple program containing two
functions.
This is the function definition. In this definition right now we are merely
printing a string.
message( ) ;
for a call to more than one function. Consider the following program:
I am in main
I am in italy
I am in brazil
I am in argentina
This check box is by default checked. So the skeleton code for Main
class is created for us by NetBeans. This class has the same name as
the name of the project. This class is also marked as the class from
which execution of the program would begin.
function it has already called but has in the meantime left temporarily in
order to call a third function which will sometime later call the function
that has called it, if you understand what I mean. No? Well, let me
illustrate with an example.
I am in main
I am in italy
I am in brazil
I am in argentina
I am back in italy
I am finally back in main
Here, main( ) calls other functions, which in turn call still other
functions. Trace carefully the way control passes from one function to
another. Since execution always begins with main( ), every function in a
program must be called directly or indirectly by main( ). In other words,
the main( ) function drives other functions.
Let us now summarize what we have learnt so far.
called them and they did what they were designed to do. It would be
nice to have communication
functions.
Consider the following program. In this program, in main( ) we receive
the values of a, b and c through the keyboard and then output the sum
of a, b and c. However, the calculation of sum is done in a different
function called calSum( ). Since sum is to be calculated in calSum( ) and
values of a, b and c are received in main( ), we must pass on these
values to calSum( ), and once calSum( ) calculates the sum, we must
return it from calSum( ) back to main( ).
(d) In the earlier programs, the moment closing brace ( } ) of the called
function was encountered, the control returned to the calling
function. No separate return statement was necessary to send back
the control.
This approach is fine if the called function is not going to return any
meaningful value to the calling function. In the above program,
however, we want to return the sum of x, y and z. Therefore, it is
necessary to use the return statement.
The last statement can be used when we wish to return the control
to the calling function without returning a value. Note that, in this
case, the parentheses after return are dropped. In the other return
statements too, the parentheses can be dropped.
(i) A function can return only one value at a time. Thus, the following
statements are invalid:
return ( a, b ) ;
return ( x, 12 ) ;
Chapter 7: Functions 121
60
30
message( ) ;
message( ) ;
}
public void message( ) ;
{
System.out.println ( "Praise worthy effort!" ) ;
}
}
(h) A function may be called more than once from any other function.
(i) It is necessary for a function to return some value.
(j) Function definitions cannot be nested; however, function calls can
be nested.
[D] Pick up the correct alternative for each of the following questions:
(d) Which of the following statements are CORRECT about the code
snippet given below:
where, S = ( a + b + c ) / 2
Actual & Formal arguments must match in Number, Order and Type
129
130 Let Us Java
Function Overloading
Functions with Variable Number of Arguments
Recursion
Exercises
KanNotes
Chapter 8: Advanced Features of Functions 131
Function Overloading
With the facility of function overloading we can have multiple functions
in a class with the same name. For example, if we wish to write
functions that return the absolute value of a numeric argument we can
consider writing a separate function for each numeric data type one
that returns absolute value of an int, another which returns absolute
value of a long and yet another which returns absolute value of a
double.
Since all these functions basically do the same thing, it seems
unnecessary to have three different function names. Java overcomes
this situation by allowing the programmer to create three different
functions with the same name. These functions are called overloaded
functions. The following program illustrates how to implement them:
j = abs ( i ) ;
m = abs ( l ) ;
e = abs ( d ) ;
System.out.println ( "j = " + j + " m = " + m + " e = "+ e ) ;
}
static int abs ( int ii )
{
return ( ii > 0 ? ii : ii * -1 ) ;
}
static long abs ( long ll )
132 Let Us Java
{
return ( ll > 0 ? ll : ll * -1 ) ;
}
static double abs ( double dd )
{
return ( dd > 0 ? dd : dd * -1 ) ;
}
}
j = 25 m = 100000 e = 12.34
How does the Java compiler know which of the abs( )s should be called
when a call is made? It decides that from the type of the argument being
passed during the function call. For example, if an int is being passed the
integer version of abs( ) gets called, if a double is being passed then the
double version of abs( )
would agree.
Overloaded functions must at least differ in the type, number or order
of parameters they accept. Just having different return types is not
enough to differentiate them.
a bad programming idea to create overloaded functions that
perform different types of actions; functions with the same name should
have the same general purpose. For example, if we write an abs( )
function that returns the square root of a number, it would be both silly
and confusing. We must use overloaded functions judiciously. Their
purpose is to provide a common name for several similar but slightly
divergent functions. Overusing overloaded functions can make a
program unreadable.
package varargsproject ;
public class VarArgsProject
{
public static void main ( String args[ ] )
{
double d1 = 10.0 ; double d2 = 20.0 ;
double d3 = 30.0 ; double d4 = 40.0 ;
double avg ;
avg = total / numbers.length ;
return avg ;
}
}
Recursion
In Java, it is possible for the functions to call themselves. A function is
fact = rec ( a ) ;
System.out.println ( "Factorial value = " + fact ) ;
}
if ( x == 1 )
return ( 1 ) ;
else
f = x * rec ( x - 1 ) ;
return ( f ) ;
}
}
Foxed? Well, that is recursion for you in its simplest garbs. I hope you
one
136 Let Us Java
function call to another. Possibly Figure 8.1 would make things a bit
clearer.
Assume that the number entered through readLine( ) is 3. Using Figure
8.1 let us visualize what exactly happens when the recursive function
rec( ) gets called. The first time when rec( ) is called from main( ), x
collects 3. From here, since x is not equal to 1, the if block is skipped and
rec( ) is called again with the argument ( x 1 ), i.e. 2. This is a recursive
call. Since x is still not equal to 1, rec( ) is called yet another time, with
argument (2 - 1). This time as x is 1, control goes back to previous rec( )
with the value 1, and f is evaluated as 2.
Similarly, each rec( ) evaluates its f from the returned value, and finally 6
is returned to main( ). The sequence would be grasped better by
following the arrows shown in Figure 8.1. Let it be clear that while
executing the program, there do not exist so many copies of the
function rec( ). These have been shown in Figure 8.1 just to help you
keep track of how the control flows during successive recursive calls.
Figure 8.1
Recursion may seem strange and complicated at first glance, but it is
often the most direct way to code an algorithm, and once you are
familiar with recursion, the clearest way of doing so.
Chapter 8: Advanced Features of Functions 137
[A] Pick up the correct alternative for each of the following questions:
(1)
{
}
If recursive call is made in the if block, else block should contain the
end condition logic
If recursive call is made in the else block, if block should contain the
end condition logic
Fresh set of variables are born during each function call - normal call
and recursive call
141
142 Let Us Java
Structured Programming
Object-Oriented Programming
Characteristics of OOP
Objects
Classes
Inheritance
Polymorphism
Containership
Reusability
Exercises
KanNotes
Chapter 9: Introduction to OOP 143
D ata types, control instructions and functions are the basic building
blocks of any Java program. With all these topics under our belt it is
time to move on to something more complex, namely, Object Oriented
Programming.
Java is an Object Oriented Programming (OOP) language. Many
programmers tend to use object-oriented features of Java mechanically.
Though this might make the program work, the real advantages of
object-oriented programming do not accrue unless you understand the
concept of object-oriented programming and what features it offers.
Hence, in this chapter we would not write a single program. Instead we
would concentrate on understanding what is OOP and why do we need
it.
This chapter addresses these issues and provides an overview of the
features to be discussed in the rest of the book. What we say here will
everything in this chapter on the first pass; OOP is a bit complex and
understanding it takes time. We will be going over these features again
started.
The purpose of a programming language is to express the solution to a
problem with the help of an algorithm (step-by-step procedure). The
success of the solution depends on how the solution models
(represents) the problem. Different approaches have evolved over the
years to model solutions to problems. The primary amongst them are
Structured programming model (also called Procedural programming
model) and Object-oriented programming model. These models are
often called programming paradigms, i.e. principle of program
organization. To understand these models we need to begin by taking a
peek at the history of programming models.
The Beginning...
The earliest computers were programmed in binary. Mechanical
switches were used to load programs. With the advent of mass storage
devices and larger and cheaper computer memories, the first high-level
computer programming languages came into existence. With their
arrival, instead of thinking in terms of bits and bytes, programmers
could write a series of English-like instructions that a compiler could
translate into the binary language of computers.
144 Let Us Java
Structured Programming
To overcome the limitations mentioned above, a quest began to develop
new languages with new features that would help to create more
Figure 9.1
146 Let Us Java
Object-Oriented Programming
The real-world problems and their solutions are not organized into
values and procedures separate from one another. Instead, they are
perceived as objects containing values and procedures that either access
or manipulate these values. The world is full of objects and the OOP
Chapter 9: Introduction to OOP 147
methodology helps us expresses computer programs in ways that model
how people perceive the world.
The fundamental change in OOP is that a program is designed around
the data being operated upon, rather than around the operations
themselves. This is to be expected once we appreciate that the prime
purpose of the program is to access or manipulate data. The basic idea
behind object-oriented language is to combine into a single unit, both,
the data and the functions that operate on the data. Such a unit is called
an object.
provide the only way to access its data. If you want to access a data item
in an object, you call a member function in the object. It will read the
If you want to modify the data in an object, you call the member
functions in the object. No other functions can access the data. This
simplifies writing, debugging, and maintaining the program.
A Java program typically consists of a number of objects which
Figure 9.2
148 Let Us Java
Characteristics of OOP
Object-oriented programming uses a vocabulary that is unfamiliar to the
procedural programmer. Let us now briefly examine this vocabulary with
regards to the major elements of object-oriented languages.
Objects
In structured programming a problem is approached by dividing it into
functions. Unlike this, in object-oriented programming the problem is
divided into objects. Thinking in terms of objects rather than functions
makes the designing of program easier. Following are few candidates
that can be treated as objects in respective situations:
Employees in a Payroll processing system
GUI elements like windows, menus, icons, etc.
Chapter 9: Introduction to OOP 149
Elements in computer games like cannons, guns, animals, etc.
Customers, sales persons in a sales tracking system
Classes
Most languages offer primitive data types like int, long and float. Their
data representation and response to arithmetic, assignment and
relational operators are defined as part of the language. However, not
all the information about real world objects can be represented using
these limited built-in data types. The programmer often needs to create
his own data types by defining a class for it.
For example, there can be a user-defined data type to represent dates.
Programmers have to define the behavior of dates by designing a Date
class. This class expresses the format of a date and the operations that
can be performed on it. The way we can declare many variables of the
primitive type int, we can define many objects of the Date class. A class
serves as a blueprint or a plan or a template. It specifies what data and
what functions will be included in objects of that type. Defining a class
int
Inheritance
OOP permits you to create your own data types (classes) just like the
types built into the language. However, unlike the built-in data types,
the user-defined classes can use other classes as building blocks. Using a
concept called inheritance; new classes can be built on top of the old
ones. The new class referred to as a derived class, can inherit the data
and functions of the original, or the base class. The new class can add its
own data elements and functions in addition to those it inherits from its
base class.
For example, we can build a set of classes that describe a library of
publications. There are two primary types of publications periodicals
and books. We can create a general Publication class by defining data
items for the publisher name, the number of pages and the accession
number. Publications can be retrieved, stored and read. These would be
the functions of Publication class.
Next we can define two classes named Periodical and Book. Both these
classes can be derived from the base class Publication. This is natural
150 Let Us Java
Figure 9.3
Chapter 9: Introduction to OOP 151
Polymorphism
Extending the same example of the Publication, Periodical and Book, let
us now understand another important concept. Our base class,
Publication, defines methods for storing and retrieving data. A periodical
may be stored in a binder, while a book is usually placed on a shelf.
Furthermore, the way to find a specific periodical is different from
finding a book. Periodicals are located through a guide to periodical
literature, while books are found using a card catalog system. Based on
ical lit
Containership
In a typical super-market, each item on sale can be represented using a
class. These items in turn belong to different categories like cosmetics,
food, cold-drink, clothes, books, electronics, etc. Such relationships can
be represented using containership. For example objects like cold-
cream, face-wash, shampoo are contained inside a category object
called cosmetics. You will be able to observe this containership
relationship in many real-world problems.
Reusability
Object-oriented programs are built from reusable software components.
Once a class is completed and tested, it can be distributed to other
programmers for use in their own programs. This is called reusability. If
those programmers want to add new features or change the existing
152 Let Us Java
ones, new classes can be derived from existing ones. The tried and
tested capabilities of base classes do not need to be redeveloped.
Programmers can devote time to writing new code instead of wasting
time in rewriting existing code. This way software becomes easier to
test, since programming errors can be isolated within the new code of
derived classes.
For example, you might have written (or purchased from someone else)
a class that creates a menu system. You are happy with the working of
capability of displaying help for each menu item. To do this, you simply
create a new class that inherits all the capabilities of the existing one but
adds help feature. This ease with which existing software can be reused
is a major benefit of OOP.
(e) Structured programming model does not represent the real world
problem as well as the Object-oriented programming model.
(j) In polymorphism even though the function names are same, their
implementation may vary from class to class.
[C] Pick up the correct alternative for each of the following questions:
(b) Dividing a given job into multiple smaller jobs is a feature used by
154 Let Us Java
(d) Which relationship should exists between Car, maruti and bmw?
(1) All should be classes
(2) All should be objects
(3) Car should be a class and maruti and bmw should be objects
(4) Car should be an object and maruti and bmw should be classes
157
158 Let Us Java
class Rectangle
{
private int len, brd ;
brd = b ;
}
r1 = new Rectangle( ) ;
r2 = new Rectangle( ) ;
r3 = new Rectangle( ) ;
r2.setData ( 5, 8 ) ;
r2.displayData( ) ;
r2.areaPeri( ) ;
r1 = new Rectangle( ) ;
r2 = new Rectangle( ) ;
r3 = new Rectangle( ) ;
All the three objects are created in memory and each one of them
would have data members len and br in them. None of these objects
have names. The addresses at which these objects are created in
memory are stored in variables r1, r2 and r3. These variables are known
as references to objects. Figure 10.1 illustrates this.
Figure 10.1
Once the objects are created, we can use them to call member functions
of the class. For example, we can call the member function setData( )
from main( ) using the object whose address is stored in the reference
r1, through the statement:
r1.setData ( 10, 20 ) ;
r2.setData ( 5, 8 ) ;
5 and 8 get set up in len and br of object whose address is present in r2.
The dot operator ( . ) used to call setData( ) through r1 and r2 is called
Calls to other functions like
displayData( ), areaPeri( ) are similar.
Chapter 10: Classes and Objects 163
Note that the objects are created at a place in memory called heap,
whereas, references to objects are created at a place in memory called
stack.
class Number
{
private int i ;
n1 = new Number( ) ;
n1.displayData( ) ;
n1.setData ( 200 ) ; // first method to set data in object
n1.displayData( ) ;
n2 = new Number( ) ;
n2.displayData( ) ;
n2.getData( ) ; // second method to set data in object
n2.displayData( ) ;
This program shows three ways in which we can give values to data
items in an object. One is through the member function setData( ) to
whom we pass the value to be set up. Another way is by receiving values
through keyboard as shown in function getData( ). That brings us to the
(or in short,
Ctor). The constructor is a special member function that allows us to set
up values while creating an object, without the need to make a separate
call to a member function like setData( ). Thus, constructor is a member
function that is called automatically whenever an object is created.
There are some unusual aspects to constructor functions. First, it is no
accident that they have exactly the same name as the class of which
function within it must have same names. This is how the compiler
knows that the member function is a constructor.
Chapter 10: Classes and Objects 165
Secondly, no return type is used for constructors. Why not? Since the
constructor is called automatically when an object is created, returning a
value would not make sense.
In our program the statements
create three objects of the type Number and call the appropriate
constructor function. Note the use of the new operator while creating
objects. This operator allocates memory for the object and then calls
If you notice carefully, you would find that there are two constructors
with the same name Number( ). Hence we call these constructors as
overloaded constructors. Which of the two constructors gets called
when an object is created, depends on how many arguments are used in
the creation of the object.
-argument constructor, the
value of i is set to 0 for objects referred by n1 and n2. This can be
verified from the output of the program. The value of i for n1 and n2 can
later be reset, as done here through calls to setData( ) and getData( ).
If data can be set in an object through the constructor function as well
as through the setData( ) function, why should we define both in a
class? This is because the constructor function can be called only during
creation of an object, whereas the setData( ) function can be called
multiple times once an object is created. So initial data can be set in an
object through the constructor and it can be changed later (if required)
through the setData( ) function.
What would happen if we declare an object of a particular class type and
Object Destruction
When the object is created using the operator new, memory is allocated
for it. Should we not free this memory when we are done with using this
object? This is not necessary, as this is done for us by the JVM. It has a
program called Garbage Collector, which it runs periodically. When it
runs, it checks for objects that are no longer being used by an
application. It then reclaims (frees) the memory used to store all such
objects. So, as far as memory management for objects is concerned, we
do not have to worry much about it, and can safely rely on garbage
collector to do it for us. This is unlike traditional OO languages like C++
where programmers have to manage the memory explicitly.
However, in many Java programs, memory is not the only resource that
is used. Other resources like files, network connections, database
connections, are also used. When we no longer need the objects that
use these resources the objects should release these resources in a
disciplined manner. It this is not done resource leaks will happen. This is
a waste of resources. At times, if the pool of resources gets exhausted
then the program may even stop running. Java provides a mechanism
called finalize( ) function to give up the resources when the object is no
longer needed. This function is defined inside the class and is called by
the garbage collector just before reclaiming the object.
Thus the finalize( ) function is opposite of a constructor. The constructor
is called when an object is created. Similarly, when an object is
destroyed by the garbage collector the finalize( ) function is called. Note
that we have no control over when the finalize( ) function is called by
the Garbage Collector.
The following program shows finalize( ) at work:
class Example
{
private int data ;
When the object referred by e gets created, the constructor gets called.
When control goes outside main( ) this object is no longer used. When
the garbage collector finds this, it calls the finalize( ) function. finalize( )
function does not receive any parameter, nor does it return any value.
The finalize( ) method cannot be called explicitly. Also, we should not
declare it as public.
In the finalize( ) method we have simply called the base class finalize( )
method. Note that all classes in Java including the Example class are
derived from a base class called Object. We would learn more about this
derivation process in Chapter 13.
In this program we have done precious little inside the finalize( )
function. In a program in which the object uses files, network and
database connections the finalize( ) method should perform operations
like closing open files, terminating network connections, terminating
database connections and other cleanup work.
Terminology
Consider the following code snippet:
Sample s1, s2 ;
s1 = new Sample ( 1.0f, 2.0f ) ;
s2 = new Sample( ) ;
s2.Function ( s1 ) ;
168 Let Us Java
From the code it is obvious that we are creating two objects of the
Sample class and calling its zero-argument and two-argument
constructors. Can you guess what are we passing to Function( )? Simple,
object s1. Well, actually speaking no. This is because, s1 is not an object,
a name
and we always access it using its address stored in the reference s1. But
it is quite common to call the reference s1 as object s1. Though this is
slightly incorrect, it gives a lot of convenience and hence we too would
be using this terminology in the rest of the chapter and the chapters to
follow.
A Complex Class
As we know, a complex number consists of a real part and an imaginary
part. The following program puts the concept of constructor to a
practical stint by developing a class to implement complex numbers:
class Complex
{
private float real, imag ;
public Complex( )
{
}
c1 = new Complex( ) ;
c1.setData ( 2.0f, 2.0f ) ;
c2 = new Complex( ) ;
c3 = new Complex( ) ;
c3 = c1.addComplex ( c2 ) ;
System.out.println ( "Complex c3:" ) ;
c3.displayData( ) ;
c4 = new Complex( ) ;
c4.getData( ) ;
c5 = new Complex ( 2.5f, 3.0f ) ;
c6 = new Complex( ) ;
c6 = c4.mulComplex ( c5 ) ;
System.out.println ( "Complex c6:" ) ;
c6.displayData( ) ;
Complex c7 ;
c7 = new Complex( ) ;
c7 = c1.addComplex ( c2.mulComplex ( c3 ) ) ;
System.out.println ( "Complex c7:" ) ;
c7.displayData( ) ;
}
}
c3 = c1.addComplex ( c2 ) ;
Here, the complex numbers c1 and c2 are being added and the result is
being stored in c3. Out of c1 and c2, c2 is being passed explicitly,
whereas c1 becomes accessible to addComplex( ) through a mechanism
of this reference. This mechanism would be discussed in the next
section. The syntax for arguments that are objects is the same as that
for arguments that are simple data types like ints or floats. The complex
object's reference returned by addComplex( ) is collected in c3.
Chapter 10: Classes and Objects 171
The call to mulComplex( ) function works similarly:
c6 = c4.mulComplex ( c5 ) ;
c7 = c1.addComplex ( c2.mulComplex ( c3 ) ) ;
If we implement the functions in this fashion, the way they are called
would also change. The calls would now look like this:
c3.addComplex ( c1, c2 ) ;
c6.mulComplex ( c4, c5 ) ;
Here, in the first call c1 and c2 are being passed explicitly, whereas, c3
would be available to addComplex( ) through the this reference
mechanism. Similarly, in the second call, c4 and c5 are being passed
explicitly, whereas, c6 would be available through the this reference
mechanism. Can we use both the forms of addComplex( ) and
mulComplex( ) in the same class? Of course, you can. They would then
172 Let Us Java
class Example
{
private int i ;
10
10
Since the this reference contains the address of the object, using it we
can reach the data member of the Example object through statements
like:
But if we can set the value in i and display it without using this
reference, then why bother about it? There is one situation where we
cannot get by without using this reference. Suppose that we had
defined the setData( ) function as shown below.
Note that here we have collected the value passed to setData( ) in the
variable i and not in ii. This local i would now conflict with the private
int i of the Example class. So we cannot expect the private int i to get
set if we use the statement,
i=i;
In this case the only way to refer the private int i of the Example object
is to use the this reference through a statement,
this.i = i ;
them would share the static data members. Static data members are
useful when we wish to share some data between all objects. For
example, if we wish to keep track of number of objects that have been
created so far from a class, we can track it using a static data member,
as shown below.
Static Block
A static block is similar to static data members in the sense that it
belongs to a class and not to a particular object. The static block is
executed when the class is first loaded. It is typically used to initialize all
the static data of the class at one place. In this block we can also use
control instructions to validate the static data before initializing it. We
cannot access non-static variables or methods in this block. Take a look
at the following program:
class Sample
{
private static int y ;
private static int m ;
private static int d ;
// static block
static
{
Calendar cal = Calendar.getInstance( ) ;
y = cal.get ( Calendar.YEAR ) ;
m = cal.get ( Calendar.MONTH ) ;
d = cal.get ( Calendar.DAY_OF_MONTH ) ;
}
176 Let Us Java
In the static block we have called the static method getInstance( ) of the
Calendar class. This method creates a Calendar object and fills it up with
current day, month and year data. We have then extracted this data by
calling the get( ) method of Calendar class.
package passingobjectsproject
public class PassingObjectsProject
{
public static void main ( String[ ] args )
{
Ex e = new Ex( ) ;
e.setData ( 1, 2.5f ) ;
e.displayData( ) ;
fun ( e ) ;
e.displayData( ) ;
}
class Ex
{
private int i ;
private float f ;
The second call to displayData( ) prints the values 3 and 8.5, proving
that through fun( ) when we manipulate the object, the original object
gets modified.
}
}
(b) package sampleproject ;
class StudentRecord
{
private int m1, m2, m3 ;
private float percentage ;
public StudentRecord( )
{
m1 = m2 = m3 = 0 ;
percentage = 0.0f ;
}
public void calculatePercentage ( int x, int y, int z )
{
m1 = x ; m2 = y ; m3 = z ;
percentage = ( m1 + m2 + m3 ) / 3.0f ;
}
public void displayPercentage( )
{
System.out.println ( "Percentage = " + percentage ) ;
}
}
public class SampleProject
{
public static void main ( String[ ] args )
{
StudentRecord s1 ;
s1 = new StudentRecord( ) ;
s1.displayPercentage( ) ;
s1.calculatePercentage ( 35, 35, 35 ) ;
s1.displayPercentage( ) ;
}
}
static
{
Chapter 10: Classes and Objects 179
d = m = y = 10 ;
}
public Sample( )
{
y++ ; m++ ; d++ ;
}
public static void Show( )
{
System.out.println ( "y : " + y ) ;
System.out.println ( "m: " + m ) ;
System.out.println ( "d: " + d ) ;
}
}
class SampleProject
{
static void main ( string[ ] args )
{
Sample s = new Sample( ) ;
s.Show( ) ;
}
}
(j) Is it true that in a class data members are always private, whereas
member functions are always public?
(k) Is it true that a class declaration creates space in memory for the
members defined in it?
(l) Is it necessary that a constructor in a class should always be public?
(m) Is size of an object equal to sum of sizes of data members and
member functions within the class?
(n) Define a class Cartesian which stores the Cartesian co-ordinates of
a point. Define another class Polar which stores Polar co-ordinates
of a point. Make a provision to convert co-ordinates from Cartesian
to Polar and vice-versa.
(p) If a method is called using two different objects, then would this
reference contain same addresses during each call?
Classes indicate how the objects created from them would look like
Data values in objects are often called instance data or state of the
object
Ctor is a function
Static block gets executed exactly once when the class is loaded
185
186 Let Us Java
avg = sum / 30 ;
System.out.println ( "Average marks = "+ avg ) ;
}
}
int[ ] marks ;
First time through the loop, i has a value 0, so the first value read
through nextInt( ) function will be stored in marks[ 0 ]. This process will
be repeated 30 times till the last values gets stored at marks[ 29 ].
Reading Data from an Array
The balance program reads the data back out of the array and uses it to
calculate the average. The for loop is much the same, but now the body
of the loop causes each s s marks to be added to a running total
stored in a variable sum. When all the marks have been added up, the
result is divided by 30, the number of students, to get the average.
More on Arrays
Array is a very popular data type with Java programmers. This is because
of the convenience with which arrays lend themselves to programming.
The features which make arrays so convenient to program would be
discussed below, along with the possible pitfalls in using them.
Array Initialization
In the program in the previous section we have used and array that did
not have any values in it to begin with. We managed to store values in it
during program execution. Let us now see how to initialize an array
while declaring it. Following are a few examples that demonstrate this:
// method 1
for ( i = 0 ; i <= 6 ; i++ )
System.out.print ( marks[ i ] + " " ) ;
// method 2
for ( i = 0 ; i <= marks.length - 1 ; i++ )
System.out.print ( marks[ i ] + " " ) ;
// method 3
for ( int j : marks )
System.out.print ( j + " " ) ;
The first method is as usual. In the second method we have obtained the
number of elements in the array using its length property. In the third
method, we don't have to worry about the number of elements in the
array, as j takes different values present in marks[ ] array during each
iteration through the loop.
Bounds Checking
In Java while accessing array elements if we exceed the bounds of the
array an error would be reported during execution of the program. For
example, if the following code snippet is executed an error would be
reported as num[ 6 ] does not exist.
55 65 75 56 78 78 90
modify ( marks ) ;
Returning an Array
The way we can pass an array to a function, can we return an array from
a function? Certainly. Even here what we would be returning would only
be a reference to the array. The following program shows how this can
be done:
package returningarrayproject ;
Chapter 11: Arrays 193
Here the reference to the array arr is returned from func( ) and
collected in reference p in main( ). Using this reference when we iterate
through the array in the for loop we are able to access all the elements
of arr.
Arrays.sort ( arr ) ;
System.out.println ( "\nSorted array" ) ;
for ( i = 0 ; i < arr.length ; i++ )
System.out.print ( arr[ i ] + " " ) ;
Arrays.fill ( arr, 0 ) ;
System.out.println ( "\nCleared array" ) ;
for ( i = 0; i < arr.length; i++ )
System.out.print ( arr[ i ] + " " ) ;
System.out.println( );
}
}
Original array
23 45 11 54 89 32
Sorted array
11 23 32 45 54 89
Element 54 found at 4
New array contents
11 23 32 45 54 89
Cleared array
000000
Array of Objects
So far we have constructed an array of pre-defined types like integers or
floats. It is also possible to create an array of user-defined types as well.
For example, we can create an array of 3 Sample objects as shown
below.
class Sample
{
private int i ;
private float a ;
i = 10 a = 3.14
i = 20 a = 6.28
i = 30 a = 3.55
Figure 11.1
Once the array is created we can iterate through it using the for loop,
calling display( ) function for each object in turn. This is justified by the
output of the program.
There is a more compact way in which we could have initialized the
array of objects. This is shown below.
Sample[ ] arr = {
new Sample ( 10, 3.14f ),
new Sample ( 20, 6.28f ),
new Sample ( 30, 3.55f )
};
Chapter 11: Arrays 197
Multi-Dimensional Arrays
So far, we have explored arrays with only one dimension. It is also
possible for arrays to have two or more dimensions. The two-
dimensional array is also called a matrix. Here is a simple program that
stores numbers in a matrix and then reports the biggest number and its
position in the matrix.
package twodarrayproject ;
big = a[ 0 ][ 0 ] ;
r=0;
c=0;
There are two parts to the program in the first part, we define and
initialize a 2-D array of 3 rows and 4 columns of integers, whereas, in the
198 Let Us Java
second part through a set of for loops, we find out the value of the
biggest number in the 2-D array.
In a[ i ][ j ] the first subscript of the variable a, is row number. The
second subscript tells which of the four columns we are talking about.
Remember the counting of rows and columns begin with zero. Thus, 7 is
stored in a[ 0 ][ 0 ], 8 is stored in a[ 1 ][ 3 ] and so on.
Instead of initializing the 2-D array in-place we can also receive its values
from the keyboard as shown below.
package passingandreturning2darraysproject ;
public class PassingAndReturning2dArraysProject
{
public static void main ( String[ ] args )
{
int[ ][ ] a = { { 1, 2, 3 }, { 4, 5, 6 } } ;
int sum ;
sum = getSum ( a ) ;
System.out.println ( "Sum = "+ sum ) ;
int[ ][ ] d ;
d = getArray( ) ;
int i, j, prod = 0 ;
for ( i = 0 ; i < d.length ; i++ )
{
Chapter 11: Arrays 199
for ( j = 0 ; j < d[ i ].length ; j++ )
prod = prod + d[ i ][ j ] ;
}
System.out.println ( "Product = "+ prod ) ;
}
And here is th
Sum = 21
Product = 45
Here in main( ) we have defined a 2-D array and then passed it to the
function getSum( ). The getSum( ) function collects the reference to the
2-D array passed to it in b. Using this reference it then accesses all the
array elements and adds them up. This sum is then returned to main( )
which promptly prints it.
Next, main( ) calls the function getArray( ). This function defines
another 2-D array c, and returns its reference to main( ). When control
returns from getArray( ) even though the reference c dies, since the
reference d points to the 2-
the Garbage Collector. Once in main( ) it iterates through the 2-D array,
200 Let Us Java
this time calculating the product of all its elements. This product is then
printed out.
From this program it is evident that whenever we have to pass or return
an array all that we have to do is pass or return a reference to the array.
Jagged Arrays
It is not necessary that each row of a 2-D array would always have same
number of columns. For example, the arrangement of seats in an
auditorium is like a 2-D array, but number of seats in each row may not
be equal. If we are to represent these seats in a 2-D array in Java we
have to use jagged arrays. A jagged array permits unequal number of
elements in each row of a 2-D array.
Let us now construct a 2-D jagged array containing 3 rows. The number
of elements in these three rows is 4, 3 and 2, respectively. So the
arrangement of this jagged array would be as shown in Figure 11.2.
Figure 11.2
a[ 0 ][ 0 ] = 7 ; a[ 0 ][ 1 ] = 2 ; a[ 0 ][ 2 ] = 6 ; a[ 0 ][ 3 ] = 1 ;
a[ 1 ][ 0 ] = 9 ; a[ 1 ][ 1 ] = 3 ; a[ 1 ][ 2 ] = 4 ;
a[ 2 ][ 0 ] = 1 ; a[ 2 ][ 1 ] = 8 ;
System.out.println( ) ;
}
}
}
The way we can pass a normal 2-D array to a function, we can also pass
or return a jagged 2-D array to/from a function
202 Let Us Java
Resizing of Arrays
As we know, arrays are created on the heap dynamically during
execution of the program. As a result, we can do two things with an
array:
(a) We can decide the number of elements in the array at run-time and
do not have to make any commitment about it while writing the
program.
(b) Once the array is created we can increase or decrease its size during
execution.
Let us now see how this can be done programmatically.
Look at the way arr and newarr are defined in one statement:
The int[ ] applies to arr as well as newarr. Both are treated as references
to an integer array.
Both these arrays are flexible in the sense that their size is decided by
the user of this program during execution. The arrays are created based
on the values of num and newnum supplied by the user during
execution.
204 Let Us Java
To increase the size of the array we have created a new array (newarr),
copied elements of existing array (arr) into it and then filled additional
values into it after the copied elements.
Once this is done, if we so desire, we can delete the old array by settings
its reference to null. If we do so, the array would be reclaimed by the
Garbage Collector.
The way this program lets you increase the size of an existing array
during execution; if the need arises we can also shrink the size of an
existing array during execution.
209
210 Let Us Java
I n the last chapter, you learnt how to define arrays of various sizes and
dimensions, how to initialize them, how to pass them to a function,
etc. With this knowledge under your belt, you should be ready to handle
strings, which are, simply put, a special kind of array. And strings and the
ways to manipulate them are going to be the topics of discussion in this
chapter.
String s1 = "Lionel" ;
String s2 = "Messi" ;
System.out.println ( s1 ) ;
System.out.println ( s2 ) ;
String s1 = "Lionel" ;
String s2 = "Messi" ;
s1 = s1 + s2 ;
System.out.println ( s1 ) ;
This would produce the output LionelMessi. This code snippet makes it
appear as if the string s2 simply got attached at the end of s1, thereby
changing s1. However, internally something different happened. This is
212 Let Us Java
String s1 = "Hoopster" ;
String s2 = "Hoopster";
if ( s1 == s2 )
System.out.println ( "Equal" ) ;
else
System.out.println ( "Unequal" ) ;
Here, when we create the second string, a new string object is not
created. Instead s2 is made to refer to the same string "Hoopster" to
which s1 is referring. Thus s1 and s2 both are referring to the same
string. Therefore the condition in if is satisfied. As against this, in the
following code s3 and s4 are referring to two different string objects, so
the condition in if fails.
In this case to check whether the contents of the two String objects are
same we should use the following code:
if ( s3.equals ( s4 ) )
System.out.println ( "Equal" ) ;
else
System.out.println ( "Unequal" ) ;
String s3 ;
s3 = s1.concat ( s2 ) ;
System.out.println ( s3 ) ;
s3 = String.copyValueOf ( s2.toCharArray( ) ) ;
System.out.println ( s3 ) ;
int c ;
c = s2.compareTo ( s3 ) ;
if ( c < 0 )
System.out.println ( "s2 is less than s3" ) ;
else if ( c == 0 )
System.out.println ( "s2 is equal to s3" ) ;
else
System.out.println ( "s2 is greater than s3" ) ;
if ( s1 == s3 )
System.out.println ( "s1 is equal to s3" ) ;
else
System.out.println ( "s1 is not equal to s3" ) ;
s3 = s1.toUpperCase( ) ;
System.out.println ( s3 ) ;
s3 = s2.concat ( "Mumbai" ) ;
214 Let Us Java
System.out.println ( s3 ) ;
String s ;
s = s1.substring ( fin, lin + 1 ) ;
System.out.println ( "Substring: " + s ) ;
int i = 10 ;
float f = 9.8f ;
s3 = String.format ( "Value of i = %d Value of f = %f" , i, f ) ;
System.out.println ( s3 ) ;
}
}
integer if the String object that has called the compareTo( ) method
contains a string which is lesser than the string contained in the object
String s1 = "Hello" ;
s1 = s1.toUpperCase( ).substring ( 2, 5 ) ;
System.out.println ( s1 ) ;
if ( s == "" )
Splitting Strings
While processing strings in Java applications, it is required to split a
string in parts based on certain separator, thereby getting an array of
strings. The following program illustrates how this splitting of strings can
be done:
StringBuilder Class
As we noted in the previous section, when a string is manipulated it is
not changed in-place. Instead, a new object containing the manipulated
string is created and the reference which was pointing to the earlier
string is now made to point to this new manipulated string.
Instead of this, if we want that the same string should get manipulated
in-place, then Java library provides a StringBuilder class for this. When
we create a string using this class we can modify it by appending,
removing, replacing, or inserting characters. For carrying out these
operations it has methods like append( ), delete( ), replace( ) and
insert( ).
Based on what operations that we wish to perform on the string created
using StringBuilder, we can set the maximum number of characters that
the object can store. This can be done by using the EnsureCapacity( )
method.
Array of Strings
Very often we are required to deal with a set of strings rather than only
one string. In such cases we should create an array of strings. The
following program shows how to declare and process such an array. In
this program we have stored names of persons in a string array called
masterList. The program asks you to type your name. When you do so, it
218 Let Us Java
checks your name against the names in masterList to see if you are
flag = false ;
for ( i = 0 ; i <= 5 ; i++ )
{
a = masterList[ i ].compareTo ( yourName ) ;
if ( a == 0 )
{
System.out.println ( "You can enter the palace" ) ;
flag = true ;
break ;
}
}
if ( flag == false )
System.out.println ( "Sorry, you are a trespasser" ) ;
}
}
And here is the output for two sample runs of this program...
Chapter 12: Strings and Enums 219
Notice how the array of strings masterList has been created. Actually
speaking, it is an array of String references. That is, it contains base
addresses of respective names. For example, address of String object
representing masterList[ 0 ], base address of String
object representing masterList[ 1 ] and so on. This is
depicted in Figure 12.1.
Figure 12.1
Sorting Strings
Let us now create a program that stores names of persons in an array of
strings, sorts these names in alphabetical order using the Bubble Sort
package sortingstringsproject ;
To sort the strings we have used the Selection Sort logic. To compare the
alphabetical order of strings, we have used the function compareTo( )
which returns a value greater than 0 if the two strings being compared
are not in alphabetical order. In such a case we swap the positions of the
two strings in the array. Once the sorting of all strings is over, we have
printed the sorted list using a for loop.
Chapter 12: Strings and Enums 221
Enumerations
The enumerated data type gives you an opportunity to invent your own
data type and define what values the variable of this data type can take.
This can help in making the program listings more readable, which can
be an advantage when a program gets complicated or when more than
one programmer would be working on it. Using enumerated data type
can also help you reduce programming errors.
As an example, one could invent a data type called MaritalStatus which
can have four possible values Single, Married, Divorced or Widowed.
MaritalStatus data type can be implemented.
enum MaritalStatus
{
single, married, divorced, widowed
}
MaritalStatus person1, person2 ;
person1 = MaritalStatus.married ;
person2 = MaritalStatus.divorced ;
// Using Enumerations
package usingenumproject ;
enum Department
{
assembly, manufacturing, accounts, stores
}
class Employee
{
private String name ;
private int age ;
private float salary ;
private Department dept ;
Let us now dissect the program. We first defined the data type enum
Department and specified the four possible values, namely, assembly,
manufacturing, accounts and stores. Then we declared a class Employee
to manage employee data. It contains name, age, salary and dept. Of
these, the last one is special it is a variable of the type Department.
The constructor of the Employee class is invoked from main( ). In this
call to the constructor, the value of the department in which the
employee is working is passed as Department.manufacturing. This is
much more informative to anyone reading the program than simply
passing a value 1 to represent the manufacturing department. In the
constructor, the enum value passed to it is assigned to private variable
of the class through the statement,
dept = d ;
In the next part of the program, the values assigned to Employee e are
printed by calling the function displayData( ). Note that even though an
integer value is assigned to Dept, while printing it, it prints
manufacturing and not 1. Thus enum data type comes in very handy
whenever we wish to use named constants in a Java program. Here are
a few more enum declarations that would make programming quite
convenient.
You must have noticed that in the previous program when we tried to
print the department, it was printed as manufacturing though the
underlying value was an integer. At times, we may want to access and
print the integer value instead of the string. The following program
shows how this can be done. This time however we are going to use an
enum CarTypes instead of Department. Here is the pr
224 Let Us Java
enum CarTypes
{
suv, hatchBack, sedan, convertible
}
{
MaritalStatus ms = MARRIED ;
System.out.println ( ms ) ;
}
}
[B] Pick up the correct alternative for each of the following questions:
(a) Which of the following is the CORRECT way to create a String object?
(1) String s = "Hello" ;
(2) String s[ ] = "Hello" ;
(3) char s[ ] = new char [ 20 ] ; s = "Hello" ;
(4) string s = "Hello" ;
(d) What is the name of the String object in the following code snippet:
String s = "Snakes and Pythons" ; String t = s ;
(1) s
(2) t
(3) Snakes and Pythons
(4) Object is nameless
(b) Write a program to reverse the strings stored in the following array:
String s[ ] = {
"To err is human...",
"But to really mess things up...",
"One needs to know Java!!"
};
(c) Write a program to delete all vowels from a sentence.
(d) Write a program that will read a line and delete from it all
229
230 Let Us Java
Uses of Inheritance
Constructors in Inheritance
The final Keyword
Incremental Development
Other Code Reuse Mechanisms
Exercises
KanNotes
Chapter 13: Inheritance 231
// base class
class Index
{
protected int count ;
public Index( )
{
count = 0 ;
}
public void display( )
{
System.out.println ( "count = " + count ) ;
}
public void increment( )
{
count += 1 ;
}
}
// derived class
class Index1 extends Index
{
public void decrement( )
{
count -= 1 ;
}
}
specifies that the class Index1 has been derived from the base class
Index. By doing this, Index1 inherits all the features of the base class
Index. Index1 increment( ) function,
since they are already present in the base class. Figure 13.1 shows the
relationship between the base class and the derived class. Note that the
arrow in Figure 13 derived from
says that the derived class can refer to the functions and data in the
base class, while the base class has no access to the derived class data or
functions.
Figure 13.1
Since we have not declared any constructor in Index1, the compiler
would insert an empty zero-argument constructor in it. When we create
an object i, the constructor in the base class gets called followed by the
empty zero-argument constructor in the derived class. When we call the
function increment( ) it is searched in the derived class. Since the
increment( ) from base
class is used to increment count.
Note that count has been marked as protected. Had it been private it
would not have been available outside the Index class. By marking it as
protected, it is now available in the Index class as well as in the
inheritance chain, i.e. in functions of Index1 class.
234 Let Us Java
Figure 13.2
You would agree that, through the derived class Index1 (and thereby
through inheritance) we have increased (extended) the functionality of
the Index class, without modifying it.
Note that inheri
its objects have no knowledge about any classes derived from the base
class. This means, in our program, had we built an object j from the class
Index, then the decrement( ) function would have remained inaccessible
to this object.
Uses of Inheritance
Now that we have a basic idea about inheritance let us see in which
scenarios is inheritance used in Java. The four common usages of
inheritance are as follows:
(a) Use existing functionality
(b) Override existing functionality
Chapter 13: Inheritance 235
(c) Provide new functionality
(d) Combination of existing and new functionality
class Ex
{
public void fun( )
{
System.out.println ( "Inside Ex - fun( )" ) ;
}
public void save( )
{
System.out.println ( "Inside Ex - save( )" ) ;
}
public void enc( )
{
System.out.println ( "Inside Ex - enc( )" ) ;
}
public void open( )
{
System.out.println ( "Inside Ex - open( )" ) ;
}
}
class NewEx extends Ex
{
public void save( )
{
System.out.println ( "Inside NewEx - save( )" ) ;
}
public void enc( )
{
System.out.println ( "Inside NewEx - enc( )" ) ;
}
public void autoUpdate( )
{
System.out.println ( "Inside NewEx - autoUpdate( )" ) ;
236 Let Us Java
}
public void open( )
{
System.out.println ( "Inside NewEx - open( )" ) ;
super.open( ) ;
}
}
public class InheritanceFeaturesUsageProject
{
public static void main ( String[ ] args )
{
NewEx e = new NewEx( ) ;
e.fun( ) ;
e.save( ) ;
e.enc( ) ;
e.autoUpdate( ) ;
e.open( ) ;
}
}
Inside Ex - fun( )
Inside NewEx - save( )
Inside NewEx - enc( )
Inside NewEx - autoUpdate( )
Inside NewEx - open( )
Inside Ex - open( )
(a) We can even derive a class from a class which itself has been
derived from another class. Thus, multiple levels of inheritance can
exist.
(b) A class cannot have multiple base classes. That is, a derived class
cannot be derived from more than one class.
Constructors in Inheritance
As we know, the base class member functions can be called from
derived class member function using the syntax:
super.functionName( ) ;
(a) When a derived class object is created, the constructor of the base
class followed by constructor of derived class gets called.
(b) While constructing a derived class object, if we do not call the base
class constructor then, by default, the zero-argument constructor of
base class gets called.
Let us illustrate these facts with the help of a program. Here is the
class a
{
public a( )
{
System.out.println ( "a's 0-arg Ctor" ) ;
}
public a ( int xx )
{
System.out.println ( "a's 1-arg Ctor" ) ;
}
}
class b extends a
{
public b( )
{
System.out.println ( "b's 0-arg Ctor" ) ;
}
public b ( int x )
{
super ( x ) ;
System.out.println ( "b's 1-arg Ctor" ) ;
}
}
public class ConstructorsIninheritanceProject
{
public static void main ( String[ ] args )
Chapter 13: Inheritance 239
{
b y = new b( ) ;
b z = new b ( 10 ) ;
}
}
From the output we can see that when we construct the objects y and z,
firstly the constructor of base class gets called, followed by constructor
of the derived class. While constructing the object y the zero-argument
constructor of base class got called automatically. However, while
constructing z the one-argument constructor had to be called explicitly
through the syntax:
super ( x ) ;
class base1
{
protected int i ;
public base1( )
{
i=4;
}
}
class der extends base1
{
240 Let Us Java
private int j ;
public der( )
{
j=i*4;
}
}
public class OrderOfConstructionProject
{
public static void main ( String[ ] args )
{
der d = new der( ) ;
}
}
Incremental Development
One of the advantages of inheritance is that it supports incremental
development. It allows you to introduce new code without causing bugs
in existing code. By inheriting from an existing, functional class and
242 Let Us Java
even need the source code for the member functions to reuse the code.
Just the byte-code containing the compiled member functions would do.
process. Nobody ever conceived the program in its entirety at the start
of the project. The program should try to create and manipulate objects
of various types to express a model in the terms given to you by the
problem definition. Rather than constructing the program all at once it
should grow out as an organic, evolutionary creature. Of course, at some
point after things stabilize you need to take a fresh look at your class
hierarchy with an aim to collapse it into a sensible structure. Inheritance
fits this bill to perfection.
(a) We can derive a class from a base class even if the base class's
source code is not available.
(b) The way a derived class member function can access base class
protected and public members, the base class member functions
can access protected and public member functions of derived class.
(d) The size of a derived class object is equal to the sum of sizes of data
members in base class and the derived class.
(h) If a base class and a derived class each include a member function
with the same name, the member function of the derived class will
be called by an object of the derived class.
(i) A class D can be derived from a class C, which is derived from a class
B, which is derived from a class A.
(b) Suppose there is a base class B and a derived class D derived from
B. B has two public member functions b1( ) and b2( ), whereas D
has two member functions d1( ) and d2( ). Write these classes for
the following different situations:
b1( ) should be accessible in main( ), b2( ) should not be.
Neither b1( ), nor b2( ) should be accessible in main( ).
Both b1( ) and b2( ) should be accessible in main( ).
(c) If a class D that is derived from class B, then which of the following
can an object of class D located in main( ) access?
public members of D
protected members of D
private members of D
public members of B
protected members of B
private members of B
(g) If the base class has two versions of the overloaded function, and
derived class contains one version of it, then using the derived class
object can we call the other version of the base class?
(h) What will be the size of the derived class object if the base class
contains two private integers, one static integer and the derived
class contains two static integers and one private integer?
Chapter 13: Inheritance 245
Source code level reuse is done using Generic functions and Generic
classes
relationship
Derived class object may not be able to access all base class data
Inheritance facilitates :
Inheritance of existing feature : To implement this just establish
inheritance relationship
Suppressing an existing feature : Hide base class implementation by
defining same function in derived class
246 Let Us Java
247
248 Let Us Java
What is Polymorphism?
Abstract Classes and Functions
Abstract Functions Practical Example
Interfaces
Practical Uses of Interfaces
Interfaces Focused View
Interfaces Different Implementation
Interfaces Unrelated Inheritance
Exercises
KanNotes
Chapter 14: Polymorphism 249
What is Polymorphism?
Overloading of functions is one kind of polymorphism one thing
existing in several distinct forms. We have already dealt with this type of
polymorphism. The other type of polymorphism simplifies the syntax of
performing the same operation with a hierarchy of classes. Thus, you
can use polymorphism to keep the interface to the classes clean,
because you do not have to define unique function names for similar
operations on each derived class.
When polymorphism is used, a program that appears to be calling a
function of one class may in reality be calling a function of a different
class. But why on earth would we want this? Suppose we have three
different classes called Line, Circle and Rectangle. Each class contains a
draw( ) function to draw the relevant shape on the screen. If we are to
draw a picture containing numerous lines, circles and triangles we can
create an array of references which would hold addresses of all the
objects in the picture. The array definition may look like,
Shape[ ] arr ;
arr = new Shape[ 50 ] ;
When it is time to draw the picture we can simply run the loop,
arr[ i ].draw( ) ;
When arr[ i ] contains address of the Line object it would call the Line
class's draw( ) function. Similarly, when it contains the address of the
Circle object it would call the Circle class's draw( ) function. This is
amazing for two reasons:
(a) Functions from different classes are executed through the same
function call.
(b) The array arr[ ] has been defined to contain Shape references and
not Line or Circle references.
(a) The classes Line, Circle and Rectangle all must be derived from the
same base class, Shape.
(b) The Shape base class must contain a draw( ) function.
All this would be too much to digest at one shot. So let us break it into
pieces and try to understand it part by part through simple programs.
class One
{
public void display( )
{
System.out.println ( "In base class" ) ;
}
}
class OneOfOne extends One
{
public void display( )
{
System.out.println ( "In OneOfOne class" ) ;
}
Chapter 14: Polymorphism 251
}
class TwoOfOne extends One
{
public void display( )
{
System.out.println ( "In TwoOfOne class" ) ;
}
}
public class PolymorphismProject
{
public static void main ( String[ ] args )
{
One ptr ;
OneOfOne o1 = new OneOfOne( ) ;
TwoOfOne o2 = new TwoOfOne( ) ;
ptr = o1 ;
ptr.display( ) ;
ptr = o2 ;
ptr.display( ) ;
}
}
Here OneOfOne and TwoOfOne are classes derived from the base class
One. Each of these three classes has a member function display( ).
Inside main( ) having created the objects o1, o2 (from the two derived
classes) and a reference ptr to base class, we have assigned the address
of a derived class object to the base class reference through the
statement,
ptr = o1 ;
Should this not give us an error, since we are assigning an address of one
type to a reference of another? No, since in this case the compiler
relaxes the type checking. The rule is that references to objects of a
derived class are type-compatible with references to objects of the base
class. Assigning the address of a derived class object to a base class
reference is called upcasting.
When we execute the statement,
ptr.display( ) ;
252 Let Us Java
In OneOfOne class
In TwoOfOne class
As can be seen from the output, instead of the base class, the member
functions of the derived classes got executed. Thus the same function
call,
ptr.display( ) ;
Figure 14.1
254 Let Us Java
Given below is the code for the program that implements this class
hierarchy.
maruti.speed( ) ;
maruti.maintenance( ) ;
maruti.value( ) ;
bajaj.speed( ) ;
bajaj.maintenance( ) ;
bajaj.value( ) ;
jumbo.speed( ) ;
jumbo.maintenance( ) ;
jumbo.value( ) ;
}
}
In speed of FourWheeler
256 Let Us Java
In maintenance of FourWheeler
In value of FourWheeler
In speed of TwoWheeler
In maintenance of TwoWheeler
In value of TwoWheeler
In speed of Airborne
In maintenance of Airborne
In value of Airborne
Figure 14.2
Given below is the implementation of the classes shown in Figure 14.2.
}
}
>> LaserPrinter.print
>> InkjetPrinter.print
Interfaces
An interface contains only the signatures of methods (i.e., function
declarations). The implementation of these methods is done in the class
that implements the interface. For example, we can declare an interface
called Mouse, and then implement this interface in a class GeniusMouse
as shown below.
interface Mouse
Chapter 14: Polymorphism 259
{
void lBtnDown ( int x, int y ) ;
void rBtnDown ( int x, int y ) ;
}
class GeniusMouse implements Mouse
{
@Override public void lBtnDown ( int x, int y )
{
System.out.println ( "Left Button: " + x + " " + y ) ;
}
@Override public void rBtnDown ( int x, int y )
{
System.out.println ( "Right Button: " + x + " " + y ) ;
}
}
public class InterfaceExampleProject
{
public static void main ( String[ ] args )
{
GeniusMouse m = new GeniusMouse( ) ;
m.lBtnDown ( 10,20 ) ;
m.rBtnDown ( 30,40 ) ;
}
}
Left Button: 10 20
Right Button: 30 40
One similarity that abstract classes and interfaces share is, we cannot
create objects from either of them.
Now that we have created our first program that uses an interface, it is
time for some small tips about interfaces. These are given below.
(a) Interfaces are not derived from Object class.
(b) Interfaces are not derived from any base interface.
(c) A class can implement multiple interfaces.
(d) Interfaces can be inherited.
(e) Multiple interface inheritance is allowed.
(f) Class cannot implement an interface partially.
interface IEncrypt
{
void encrypt( ) ;
void decrypt( ) ;
}
interface ICompress
{
void compress( ) ;
void decompress( ) ;
}
interface IAuthenticate
{
void login( ) ;
void logout( ) ;
}
class FocusedView implements IEncrypt, ICompress, IAuthenticate
{
262 Let Us Java
IEncrypt ie = o ;
ie.encrypt( ) ;
ie.decrypt( ) ;
ICompress ic = o ;
ic.compress( ) ;
ic.decompress( ) ;
IAuthenticate ia = o ;
ia.login( ) ;
Chapter 14: Polymorphism 263
ia.logout( ) ;
}
}
>> encrypt
>> decrypt
>> compress
>> decompress
>> login
>> logout
Few things that you can notice about the program are
(a) The class FocusedView implements three interfaces IEncrypt,
ICompress and IAuthenticate. The order in which the interfaces are
mentioned in the list while defining the class does not matter.
(b) In main( ) we have created a FocusedView object, assigned its
address to an interface reference (say ICompress) and then called
the methods (Compress( ) and Decompress( )) that belongs to that
interface (ICompress).
(c) If we call the method Compress( ) using authentication interface
reference ia, it would result into a compilation error. This means
using an interface reference only methods belonging to that
interface alone can be called.
(d) While typing the program when we type "ia.", the help shows only
those methods that belong to IAuthenticate interface. This makes
the development of the FocusedView class easy, as we get to
concentrate only on that part of the class which we are developing
right now.
(e) Though the FocusedView class implements only interfaces, we may
as well derive FocusedView from another class. In that case, while
defining the class the base class name should precede the
interfaces as shown below.
class FocusedView extends BaseClass implements IEncrypt,
ICompress, IAuthenticate
{
// code
}
264 Let Us Java
interface IListMethods
{
int count( ) ;
void add ( Object o ) ;
void remove ( Object o ) ;
}
class MyArray implements IListMethods
{
@Override public int count( )
{
System.out.println ( ">> MyArray.count" ) ;
return 0 ;
}
@Override public void add ( Object o )
{
System.out.println ( ">> MyArray.add" ) ;
}
@Override public void remove ( Object o )
{
System.out.println ( ">> MyArray.remove" ) ;
}
}
class MyLL implements IListMethods
{
@Override public int count( )
Chapter 14: Polymorphism 265
{
System.out.println ( ">> MyLL.count" ) ;
return 0 ;
}
@Override public void add ( Object o )
{
System.out.println ( ">> MyLL.add" ) ;
}
@Override public void remove ( Object o )
{
System.out.println ( ">> MyLL.remove" ) ;
}
}
public class DifferentImplementationsProject
{
public static void main ( String[ ] args )
{
IListMethods i ;
i = new MyArray( ) ;
i.add ( 1 ) ;
i.remove ( 1 ) ;
i.count( ) ;
i = new MyLL( ) ;
i.add ( 1 ) ;
i.remove ( 1 ) ;
i.count( ) ;
}
}
>> MyArray.add
>> MyArray.remove
>> MyArray.count
>> MyLL.add
>> MyLL.remove
>> MyLL.count
interface ICharacter
{
void patriotism( ) ;
}
class Actor
{
protected String hairstyle = "Spikes" ;
p = new Person( ) ;
m=p;
m.style( ) ;
ICharacter i ;
i=p;
i.patriotism( ) ;
}
}
Here, we have created a base class called Actor and an Interface called
ICharacter. The base class has a method called style( ) and the interface has
a method called patriotism( ). Then we have created a class called Person.
This class inherits from Actor class and implements ICharacter interface. In
main( ) we have created an object of Person class and stored its address in
Actor reference m as well as ICharacter reference i. Then using m and i we
have called the style( ) and patriotism( ) methods. The output of the
program is given below.
268 Let Us Java
(a) Java permits calling of derived class functions using a base class
reference.
(d)
constructor is called first or the derived class constructor is called
first.
[C] Pick up the correct alternative for each of the following questions:
C++ - Does Early Binding when possible and Late Binding when Early
Binding is not possible
For Late Binding the function being called must be present in base
class as well as derived class
273
274 Let Us Java
Exercises
KanNotes
Chapter 15: Exception Handling 275
Figure 15.1
Figure 15.2
The code in the application that anticipates an exception to occur during
its execution is enclosed in a try block. When the exception occurs and
an exception object is thrown the control is transferred to another
section of code in the application called exception handler or a catch
block. Thus runtime errors generated in the try block are caught in the
catch block.
present within the try block.
The following code snippet shows the organization of try and catch
blocks. It is not a working program, but it clearly shows how and where
the various elements of the exception mechanism are placed.
package exceptionexampleproject ;
{
fun( ) ;
}
catch ( Exception e ) // exception handler or catch block
{
// do something about the error
}
}
public static void fun( )
{
// if some error occurs during execution of this function then:
// (a) Java runtime would create an exception object
// (b) Java runtime would throw the exception object
// on throwing the exception object control would reach
// the catch block
}
}
Figure 15.3
Let us now see programs for all these cases to get a real grasp of these
situations. Here is the first one
}
catch ( IOException e )
{
System.out.println ( "Error in input" ) ;
}
}
}
In our code there are two possible places where things may wrong:
As you can see, these prototypes indicate that if something goes wrong
with readLine( ), then it would throw IOException and if something goes
wrong with parseInt( ), then it would throws NumberFormatException.
This is known as advertising an exception.
For these advertised exceptions, we may adopt any of the following
three approaches:
Enter a number:
12a
Incorrect Input
Enter a number:
12
You entered: 12
From the interaction with the program shown below, you can see that
when we supply the inp
incorrect and then terminates the execution.
Enter a number:
12a
Incorrect Input
// Multiple exceptions
package multipleexceptionsproject ;
import java.io.* ;
(a)
(b) The denominator is equal to 0
(c) Some unknown error
try
{
fw = new FileWriter ( "a.txt" ) ;
fw.write ( "Hello World\n" ) ;
}
catch ( IOException ie )
{
System.out.println ( "Encountered IO Error" ) ;
}
finally
{
try
{
if ( fw != null )
fw.close( ) ;
}
catch ( IOException e )
{
System.out.println ( "Error in input" ) ;
}
}
}
}
User-defined Exceptions
In all the programs in this chapter we have been using exception objects
created from Java exception classes. It is time we explore the possibility
of user-defined exception classes. Such classes are required when an
exception condition that occurs cannot be represented using the
standard exception classes. For example, a banking application may
throw an exception when the amount being withdrawn makes the
balance in an account go below the minimum prescribed limit. Another
example could be when the amount of transaction in a credit card
application is more than the credit limit of a credit card. In such cases
we need to define our own exception class, and when an exception
occurs, we need to create an object of this exception class and throw it.
The following program illustrates this in a simple banking application:
class Customer
{
private String name ;
private int accno ;
private int balance ;
balance -= amt ;
}
}
class BankException extends Exception
{
private int acc ;
private int bal ;
288 Let Us Java
Transaction failed
Acc. No.: 2453
Balance: 900
In this program apart from the normal class that contains main( ), we
have defined two new classes Customer and BankException. From
main( ) we have constructed an object of Customer class to contain
name, account number and balance amount. Then we have called the
withdraw( ) method from the try block to withdraw an amount of Rs.
being withdrawn
makes the balance go below Rs. 500, an exception occurs. To represent
Chapter 15: Exception Handling 289
this exceptional condition, an object of BankException (derived from
Exception class) class is created. The account number and current
balance values are stored in this BankException object. This object is
then thrown using the keyword throw. This takes the control
straightway to the catch block that matches the try block, from where
withdraw( ) method was called. In the catch block we have called the
method inform( ) which promptly displays the error message
balance.
(a) When the program attempts to store more objects in the stack than
what it can accommodate.
(b) When the program tries to remove an object from the empty stack.
Here is the program that uses exceptions to handle these two errors.
data[ size ] = o ;
size++ ;
}
public Object pop( ) throws StackException
{
if ( size <= 0 )
throw new StackException ( "Stack empty" ) ;
size-- ;
return data[ size ] ;
}
public int getSize( )
{
return size ;
}
}
class StackException extends Exception
{
private String errormsg ;
try
{
while ( s.getSize( ) > 0 )
System.out.println ( s.pop( ) ) ;
}
catch ( StackException ex )
{
System.out.println ( "Problem in stack" ) ;
ex.inform( ) ;
}
}
}
Problem in stack
Stack full
25
Sanjay
Vinod
Throw an Exception
When an exception situation occurs an exception object is created and
thrown. In our first program an exception could occur in one situation
when the balance goes below 500, whereas, in the second program
there were two exceptional situations when the stack becomes full
and we try to store another object in it, or when we try to remove an
object from an empty stack. In the first program, we create and throw a
BankException object, whereas in the second we create and throw a
StackException object. On throwing an exception the control is
transferred to the exception handler, i.e., the catch block.
using exceptions. Note that all the code in the program need not be in a
try block. Just the code that anticipates occurrence of exceptional
condition during execution should be in try block..
You can appreciate how clean is this code. Just about any statement in
the try
Chapter 15: Exception Handling 293
one. The try-throw-catch arrangement handles it all for us,
automatically.
To round off all that we have learnt about exception handling, here are a
few finer points about it that you must note:
(a)
a catch block like this:
catch ( Exception e )
{
}
Programmers are tempted to write this when runtime errors occur
in their program and they wish to avoid displaying of an ugly and
elaborate stack trace of the exception.
(b)
the purpose of rectifying the exceptional situation or perform a
graceful exit.
(c) Always try to distinguish between types of exceptions by writing
multiple catch blocks wherever relevant.
(d) It is not necessary that the statement that causes an exception be
located directly in the try block. It may as well be present in a
function that is being called from the try block.
(e) A try block can be present inside another try block.
(f) If inner try catch block, then
the outer try catch handlers are inspected for a match when
an exception occurs.
(g) If we are writing a class library for somebody else to use, we should
anticipate what could cause problems to the program using it. At all
such places we should throw exceptions.
(h) If we are writing a program that uses a class library, we should
provide try and catch blocks for any exceptions that the library may
throw.
(i) Exceptions impose an overhead in terms of program size and (when
an exception occurs) in time. So we should not try to overuse it.
Make it optimally elaborate not too much, not too little.
294 Let Us Java
(d) For one try block there can be multiple catch blocks.
(e)
called.
(m) finally clause is used to perform cleanup operations like closing the
network/database connections.
(q) All values set up in the exception object are available in the catch
block.
(r) If our program does not catch an exception then the Java Runtime
catches it.
(t) All types of exceptions can be caught using the Exception class.
(u) For every try block there must be a corresponding finally block.
(a) If we do not catch the exception thrown at runtime then who will
catch it?
try block :
- Can be nested inside another try block
- If inner try doesn't have a catch, outer try's catch handlers are
inspected for a match
298 Let Us Java
catch block :
- Multiple catch blocks for one try block are OK
- At a time only one catch block goes to work
- Order of catch blocks is important - Derived to Base
finally block :
- finally clause is optional
- Code in finally always runs, no matter what! Even if a return or
break occurs first
- it is placed after catch blocks (if they exist)
- try block must have catch block and/or finally block
Exception handling tips :
-
-
types of exceptions
- Make it optimally elaborate - Not too much, not too little
No point in creating a program that tells secrets to itself. Input /
Output with the outside world is the way of life for a program...
299
300 Let Us Java
import java.io.* ;
import java.util.Date ;
File f ;
f = new File ( str ) ;
if ( f.exists( ) )
{
String dname = f.getParent( ) ;
System.out.println ( "Directory name: " + dname ) ;
String fname = f.getName( ) ;
System.out.println ( "File name: " + fname ) ;
String abspath = f.getAbsolutePath( ) ;
System.out.println ( "Full Name: " + abspath ) ;
{
String str ;
System.out.println ( indent + d.getName( ) + "/" ) ;
for ( File fi : d.listFiles( ) )
{
str = indent + " " + fi.getName( ) ;
System.out.println ( str ) ;
}
./
build
build.xml
manifest.mf
nbproject
src
build/
classes
classes/
.netbeans_automatic_build
.netbeans_update_resources
directorylisterproject
directorylisterproject/
DirectoryListerProject$1.class
DirectoryListerProject.class
nbproject/
Chapter 16: Effective Input/Output 305
build-impl.xml
genfiles.properties
private
project.properties
project.xml
private/
private.properties
src/
directorylisterproject
directorylisterproject/
DirectoryListerProject.java
Drive = C:\
Total Space = 179583315968
Free Space = 18052345856
Drive = D:\
Total Space = 59624124416
Free Space = 20760801280
Drive = E:\
Total Space = 10737414144
Free Space = 6717689856
Drive = F:\
Total Space = 0
Free Space = 0
Drive = G:\
Total Space = 0
Free Space = 0
Drive = H:\
Total Space = 0
Free Space = 0
Figure 16.1
Streams are implemented using classes in java.io package. This
abstraction of I/O operations using streams offers one important
benefit no matter from where we are reading or where we are writing,
stream behaves similarly. For example, whether we are reading from a
keyboard or a disk we call the same readLine( ) method. The
implementation of the readLine( ) method is different for different
devices. Thus because of stream-
have to worry about the specific details of the operating system and
underlying devices while performing I/O as shown in Figure 16.2. The
differences in the devices and the OS are hidden away from us into
different stream classes in the java.io package.
Figure 16.2
The two fundamental operations that can be performed on a stream are
Reading and Writing. Reading involves transfer of data from a stream
into a data structure, such as an array of bytes. Writing consists of
transfer of data from a data source into a stream.
308 Let Us Java
Every stream may not support reading and writing. Most stream classes
contain methods called canRead( ) and canWrite( ) using which we can
determine which operations that stream supports.
Stream Classes
There are two fundamental types of streams Byte streams and
Character streams. Byte streams perform I/O 1 byte at a time, whereas
Character streams perform I/O one char (2 bytes) at a time. For
example, an integer 235 when written to a byte stream would involve
transfer of 4 bytes, since an integer is 4 bytes long. The same integer
when written to character stream would need transfer of 6 bytes 2
bytes per character.
There are several classes available in the Java library to perform stream-
based input/output of bytes/characters. Figure 16.3 and Figure 16.4
show the hierarchy of these classes.
Figure 16.3
The classes InputStream and OutputStream are abstract classes. From
these classes FileInputStream and FileOutputStream are derived. As
their names suggest, these classes read/write streams of bytes from/to
file. The FilterInputStream class uses some input stream as source of
data and filters it based on some criterion. The BufferedInputStream
class provides the buffering ability. Buffering is used to improve
read/write performance of a stream. The DataInputStream class
provides ability to read Java primitives.
Chapter 16: Effective Input/Output 309
Figure 16.4 shows the hierarchy of classes used for performing character
based input/output.
Figure 16.4
The classes Reader and Writer are abstract classes. The classes
InputStreamReader and OutputStreamWriter are used to read/write
character from/to stream. The FileReader and FileWriter classes are
used to read/write from/to file. The PrintWriter class is used to carry
out formatted writing in text representation.
package byteandcharacterstreams ;
import java.io.* ;
rawWrite ( i ) ;
charWrite ( i ) ;
unicodeWrite ( i ) ;
}
catch ( IOException e )
{
System.out.println ( "IO error" ) ;
}
}
static void rawWrite ( int i ) throws IOException
{
DataOutputStream ds = new DataOutputStream (
new FileOutputStream ( "Stream.txt" ) ) ;
ds.writeInt ( i ) ;
ds.close( ) ;
System.out.println ( "Wrote 123456 as an integer" ) ;
System.out.print ( "Length of file = " ) ;
System.out.println ( new File ( "Stream.txt" ).length( ) ) ;
}
static void charWrite ( int i ) throws IOException
{
FileWriter fw = new FileWriter ( new File ( "Char.txt" ) ) ;
fw.write ( ( ( Integer ) i ).toString( ) ) ;
fw.close( ) ;
System.out.println ( "Wrote 123456 as a string" ) ;
System.out.print ( "Length of file = " ) ;
System.out.println ( new File ( "Char.txt" ).length( ) ) ;
}
static void unicodeWrite ( int i ) throws IOException
{
OutputStreamWriter ow = new OutputStreamWriter (
new FileOutputStream ( "CharU.txt" ), "UTF-16" ) ;
ow.write ( ( ( Integer ) i ).toString( ) ) ;
ow.close( ) ;
System.out.println ( "Wrote 123456 as a Unicode string" ) ;
System.out.print ( "Length of file = " ) ;
System.out.println ( new File ( "CharU.txt" ).length( ) ) ;
}
}
The program writes the same integer into 3 files in different ways. For
example, it is written as an int in the first file, as a string in the second
and as a Unicode string in the third. These writing operations are done
through three methods defined in the program rawWrite( ),
charWrite( ) and unicodeWrite( ).
Note that after writing the same integer value (123456), the sizes of the
14 bytes, respectively. This indicates that during raw write, each byte
value of the 4-byte integer is written. Unlike this, during character
writing,
written to the file character-by-character. In Unicode writing, each
character of the string was written as a 2-byte character.
Before writing to a file, the file is opened using either the
FileOutputStream or File object. While writing the integer as an int, a
DataOutputStream object is used, whereas, while writing it as a string
or a Unicode string, a FileWriter and OutputStreamWriter, respectively
are used. Objects of these writers are created before using them to call
the writeInt( ) and write( ) methods. Instead of the statement,
package displayfilecontents ;
import java.io.* ;
Record I/O
Suppose we wish to write records of employees into a file and then read
them back from the file and display them on the screen. Each record
contains
choice = br1.readLine( ) ;
}
osw.close( ) ;
User-defined Streams
Apart from using the standard streams Java permits us to define our
own streams and their behavior. For example, we can define a filter
stream called UppercaseFilterStream which would convert all
characters passed through it into uppercase characters. Such a stream
316 Let Us Java
// Converts all chars read from a file into uppercase using a filter stream
package filterstreamproject ;
import java.io.* ;
return nb ;
}
private char transform ( char ch )
{
if ( Character.isLowerCase ( ch ) )
return Character.toUpperCase ( ( char ) ch ) ;
return ch ;
}
}
public class FilterStreamProject
{
public static void main ( String[ ] args ) throws
FileNotFoundException, IOException
{
File f = new File ( "C:\\a.txt" ) ;
if ( f.exists( ) )
{
UppercaseFilterReader ufr ;
BufferedReader br ;
String line ;
while ( ( line = br.readLine( ) ) != null )
System.out.println ( line ) ;
br.close( ) ;
ufr.close( ) ;
}
}
}
Figure 16.5
Once the construction of objects is over, we have called the readLine( )
method of BufferedReader class. This method in turn calls the read( )
method of the UppercaseFilterStream class. Here firstly the characters
are read from the input stream by calling the read( ) method of
FileReader class. These characters are collected in the buffer cbuf. This
buffer's contents are then converted to uppercase by calling the
transform( ) function for each character in the buffer. Thus we are able
to change the behavior of a stream by implementing the desired
behavior through a filter stream class.
File Encryption/Decryption
Security has gained paramount importance in the digital world. Often
we wish to secure our data from others. There are various techniques
through which this can be done. One of the most common techniques is
to encrypt the data in such a fashion that even if the encrypted data falls
into other people's hands they are unable to obtain the original data
from it. At the same time we should be able to get back the original data
by decrypting the encrypted data. Many Encryption/Decryption schemes
are popularly used today to secure the data from misuse. Our intention
here is not to discuss these schemes. Instead, we wish to evolve a very
simple encryption/decryption scheme. In this scheme during encryption
we would replace every lowercase alphabet in the source stream with
another predetermined lowercase character. During decryption we
would do the reverse. This type of encryption/decryption scheme is
often called a Substitution Cipher. Given below is the program which
implements the Substitution Cipher.
Chapter 16: Effective Input/Output 319
interface ITransform
{
public char transform ( char ch ) ;
}
class Encrypt implements ITransform
{
String str = "xyfagchbimpourvnqsdewtkjzl" ;
return ch ;
}
}
class Decrypt implements ITransform
{
String str = "xyfagchbimpourvnqsdewtkjzl" ;
return ch ;
}
}
class TransformWriter extends FilterWriter
{
private ITransform trans ;
try
{
out.write ( buf, off, len ) ;
}
catch ( IOException ex )
{
System.out.println ( "IO error" ) ;
}
}
}
public class SubstitutionCipherProject
{
public static void main ( String[ ] args ) throws IOException
{
doEncDec ( "C:\\a.txt", "enc.txt", true ) ;
doEncDec ( "enc.txt", "dec.txt", false ) ;
}
static void doEncDec ( String source, String target,
boolean IsEncrypt ) throws IOException
{
ITransform trans ;
if ( IsEncrypt )
trans = new Encrypt( ) ;
else
trans = new Decrypt( ) ;
FileReader sstream ;
BufferedReader sr ;
FileWriter tstream ;
TransformWriter tw ;
BufferedWriter sw ;
Chapter 16: Effective Input/Output 321
String line ;
while ( ( line = sr.readLine( ) ) != null )
sw.write ( line + "\r\n" ) ;
sw.close( ) ;
sr.close( ) ;
}
}
Figure 16.6
Rest of the program is similar to the uppercase filter stream program
that we discussed in the last section. So I would not repeat the
explanation here.
From this program and the one that we discussed in the last section we
can make the following important observations:
(a) Stream is a very important abstraction for data modelling in a
variety of applications. Being able to manipulate stream data
effectively is immensely important in Java programming. The
streams implementation in Java enables us to do that quite
effectively. For example, the data that we are manipulating in our
stream may come from a file stored on disk, a network socket or
simply a buffer in memory.
(b) We too can create our customized stream classes. When we do so,
we need to implement the abstract methods inherited from the
base class.
(l) The streams implementation in Java is such that the stream doesn't
have to know source or destination of the data.
(b) Can we inherit new classes from File class available in the Java
library?
(c) How would you check whether a given file exists or not?
(d) Is it possible to check the number of drives, the type of each drive
and the drive format type through a Java program? If yes, how?
[C] Pick up the correct alternative for each of the following questions:
(a) A number 485000 when written to a file using byte stream will
occupy
(1) 4 bytes
(2) 8 bytes
(3) 12 bytes
(4) 2 bytes
(b) A number 485000 when written to a file using character stream will
occupy
(1) 4 bytes
(2) 8 bytes
(3) 6 bytes
(4) 2 bytes
(c) Given a File object fobj, how will you determine whether it
represents a file or a directory?
(1) System.out.println ( fobj ) ;
(2) if ( fobj.isDirectory( ) )
(3) if ( fobj.isFolder( ) )
(4) if ( fobj.FileOrDirectory( ) )
(d) Which import statement should be used to avail classes that use
character stream?
(1) import java.bytestream.*
(2) import java.io.*
(3) import java.inputoutput.*
(4) import io.*
(e) Which import statement should be used to avail classes that use
character stream?
(1) import java.bytestream.*
(2) import java.io.*
(3) import java.inputoutput.*
(4) import io.*
Byte stream perform i/o one byte at a time. They are used to i/o
binary data
character stream
- System - class
- out - PrintStream object reference
- out - public static member of System class
- println( ), print( ) Members of PrintStream class
How to decide which classes to use when :
- What is your data format - text or binary
Binary InputStream, OutputStream
Text Reader, Writer
- Do you want random access capability?
Use RandomAccessFile class
- Dealing with objects or non-objects?
ObjectInputStream, ObjectOutputStream
- What are your sources and sinks for data?
Sockets, files, strings - All can be used by Byte and Character
Streams
- Do you need to use filtering?
Ability to do multiple things simultaneously is a great asset in
life. So also in programming...
327
328 Let Us Java
Multithreading in Java
To help you appreciate the challenges of multithreading you can try a
simple experiment. Make two phone calls to your friends and try to
carry out conversation with both of them concurrently. This would
involve major challenges talking to one friend, putting him on hold,
remembering where you left off, picking up the other receiver, talking to
Chapter 17: Multithreading 331
the other friend, putting him on hold, picking up the first receiver,
carrying on the conversation from the point where you left off, and
above all making the conversation sensible for everybody involved.
Java offers features that let you run multiple threads in a program. To
create multiple threads the programmer has to specify which parts of
the program he intends to execute concurrently. Although on the face of
it this might appear simple, rest assured that often multithreaded
programs are tricky and demand a substantial effort on your part to
master all the issues involved in multithreading.
Any simple Java program has a single thread of execution. This running
thread has a name called main, a priority and a group to which it
belongs. If we wish we can change the name of the thread. This has
been demonstrated in the program given below.
package mainthread ;
public class MainThread
{
public static void main ( String args[ ] )
{
Thread t = Thread.currentThread( ) ;
System.out.println ( "Current thread: " + t ) ;
t.setName ( "mythread" ) ;
System.out.println ( "After name change: " + t ) ;
String s = t.getName( ) ;
System.out.println ( "Thread name: " + s ) ;
}
}
Once we have obtained the Thread object, we can set or get the name
of the current thread using the methods setName( ) and getName( )
respectively.
It is possible to make multiple threads to belong to one group. If this is
done, then it is possible to manipulate all those threads together, rather
than individually. For example, we can start or suspend all
the threads within a group with a single method call.
Launching Threads
There are two mechanisms to launch new threads in a Java program.
These are:
(a) By extending the Thread class
(b) By implementing the Runnable interface
We wish to learn both these ways to launch a thread and assess the
utility of each. Let us begin with a program that uses the first way.
package sample ;
public class Sample
{
public static void main ( String args[ ] )
{
Ex t = new Ex( ) ;
t.start( ) ;
for ( int i = 0 ; i < 5 ; i ++ )
System.out.println ( "Main thread" ) ;
}
}
class Ex extends Thread
{
public void run( )
{
for ( int i = 0 ; i < 5 ; i++ )
System.out.println ( "New thread" ) ;
}
}
Here we have derived the Ex class from the Thread class and defined a
run( ) method inside it. The method simply pr
main( ) we have created an object of Ex class, called
Chapter 17: Multithreading 333
the start( )
times.
The start( ) method is defined in Thread class and by inheritance is
available to Ex objects. Once we call the start( ) method, the thread gets
scheduled. This means we are informing the thread scheduler that the
new thread is ready to run. When the thread scheduler deems fit, it
would start executing this new thread by calling its run( ) method.
The output of the program is shown below.
Main thread
Main thread
Main thread
Main thread
Main thread
New thread
New thread
New thread
New thread
New thread
inter-mingled. But this did not happen because once the time slot got
allotted to the main thread, in that time slot it printed all the messages,
before the time slot could be snatched away and allotted to the new
thread. Had each loop been executed 1000 times, then during each
time-slot allocated to the two threads, each would not have been able
to print all 1000 messages. This would have resulted in inter-mingling of
messages.
Another way to get the inter-mingled messages is to put each thread to
sleep for 1000 milliseconds when they get the time slot. This would
ensure that in the first time-
entire printing. This change is shown below.
package sample ;
public class Sample
{
public static void main ( String args[ ] )
{
Ex t = new Ex( ) ;
t.start( ) ;
334 Let Us Java
try
{
for ( int i = 0 ; i < 5 ; i ++ )
{
System.out.println ( "Main thread" ) ;
Thread.sleep ( 1000 ) ;
}
catch ( Exception e )
{
}
}
}
}
class Ex extends Thread
{
public void run( )
{
try
{
for ( int i = 0 ; i < 5 ; i++ )
{
System.out.println ( "New thread" ) ;
Thread.sleep ( 1000 ) ;
}
}
catch ( Exception e )
{
}
}
}
The static sleep( ) method of the Thread class postpones the execution
of next instruction by 1000 milliseconds. As a result, now the output is
inter-mingled as shown below.
Main thread
Main thread
Main thread
New thread
New thread
Main thread
Main thread
Chapter 17: Multithreading 335
New thread
New thread
New thread
Notice that the call to sleep( ) method has to be present in the try block,
as it is likely to throw an exception. Though, not the best of the ways, for
the sake of simplicity we have used an empty catch block to catch the
exception that sleep( ) may throw.
package sample ;
public class Sample
{
public static void main ( String args[ ] )
{
Ex t1 = new Ex( ) ;
t1.start( ) ;
t1.setname ( "First" ) ;
Ex t2 = new Ex( ) ;
t2.start( ) ;
t2.setname ("Second" ) ;
Ex t3 = new Ex( ) ;
t3.start( ) ;
t3.setname ( "Third" ) ;
try
{
for ( int i = 0 ; i < 10 ; i ++ )
{
System.out.println ( "Main thread" ) ;
Thread.sleep ( 500 ) ;
}
}
catch ( Exception e )
{
}
}
336 Let Us Java
t = Thread.currentThread( ) ;
String s = t.getName( ) ;
Here, while launching the three threads we have given a name to each,
which is displayed in a loop, when those threads get a time slot. In which
situation we would want to launch multiple threads from the same
Chapter 17: Multithreading 337
class? Imagine if the thread is to display an animation from a GIF file.
Then by launching different threads we can display different animations
in different parts of the screen simultaneously.
When we launch several threads from the main thread there is a
possibility that the main thread ends whereas the launched threads
continue to execute. If we wish that main thread should be the last
thread to finish execution, then we can employ the join( ) method of the
Thread class to ensure this, as shown below.
package sample ;
public class Sample
{
public static void main ( String args[ ] )
{
Ex t1 = new Ex( ) ;
t1.start( ) ;
t1.setname ( "First" ) ;
Ex t2 = new Ex( ) ;
t2.start( ) ;
t2.setname ("Second" ) ;
Ex t3 = new Ex( ) ;
t3.start( ) ;
t3.setname ( "Third" ) ;
try
{
for ( int i = 0 ; i < 10 ; i ++ )
{
System.out.println ( "Main thread" ) ;
Thread.sleep ( 500 ) ;
}
}
catch ( Exception e )
{
}
System.out.println ( t1.isAlive( ) ) ;
System.out.println ( t2.isAlive( ) ) ;
System.out.println ( t3.isAlive( ) ) ;
t1.join( ) ;
t2.join( ) ;
t3.join( ) ;
338 Let Us Java
System.out.println ( t1.isAlive( ) ) ;
System.out.println ( t2.isAlive( ) ) ;
System.out.println ( t3.isAlive( ) ) ;
}
}
On execution, out of the calls to isAlive( ) some threads may return false
if those threads have finished execution. By calling join( ) the main
thread would wait for the alive threads to finish their execution, before
it terminates. Naturally, the second set of calls to isAlive( ) would return
false for each call.
inheritance. If we wish to keep our class open for derivation from some
other class and still be able to launch new threads, we should do so by
implementing a Runnable interface in it. This method of launching new
threads is given below.
package sample ;
public class Sample
{
public static void main ( String args[ ] )
{
Ex t = new Ex ( "One" ) ;
t.x.start( ) ;
for ( int i = 0 ; i < 10 ; i ++ )
System.out.println ( "Main thread" ) ;
}
}
class Ex implements Runnable
{
public Thread x ;
Ex ( String n )
{
x = new Thread ( this, n ) ;
}
Chapter 17: Multithreading 339
Note that here we have not derived Ex from Thread class. Instead, we
are implementing the Runnable interface in it. The Runnable interface
has only one method in it run( ).
While creating an object of the Ex class, in the constructor we have
created an object of the Thread class and stored its address in a public
reference called x. Then, from main( ) we have used this x to call the
start( ) method of the Thread class. By doing this, we are informing JVM
to schedule this thread. As a result, the run( ) method gets called. In the
run( ) method we have simply printed the name of the thread.
Here is one more program that uses Runnable interface to launch
threads. The difference is that this one launches multiple threads for
each instance of the Ex class.
package sample ;
public class Sample
{
public static void main ( String args[ ] )
{
Ex t1 = new Ex ( "First" ) ;
t1.x.start( ) ;
Ex t2 = new Ex ( "Second" ) ;
t2.x.start( ) ;
Ex t3 = new Ex ( "Third" ) ;
t3.x.start( ) ;
{
Thread x ;
Ex ( String n )
{
x = new Thread ( this, n ) ;
}
The program reads three files a.txt, b.txt and c.txt that are provided to
it as command-line arguments. To add these files to your project in
NetBeans, right click on the project folder and select New | Empty File
from the menu that pops up. Give the name of the file (say, a.txt) and
type a few lines in it. Similarly add b.txt and c.txt to your project. Once
this is done, add these filenames as command-line arguments through
Right-click project name | Properties | Run | Arguments.
In the program, we print the current time in milliseconds before we start
reading the files and after the reading is finished. This is done using the
function System.currentTimeMillis( ). The actual reading of a file is done
by using the LineNumberReader class. This class has a method
getLineNumber( ) which reports the number of lines present in the file
that it has read.
When I executed this program I got the following output:
Your output may vary as depending on the contents of the three files
their reading times may vary. A quick calculation would show the
difference in times to be 753 milliseconds.
342 Let Us Java
Now let us look at the program that follows the multithreaded approach
Synchronization
Software development is a team effort. Unless team members
cooperate with one another and synchronize their work with the rest of
the team, the team
several threads running, unless their activities are synchronized with one
another the disaster is not far away. For example, if a program
instantiates two threads and if both the threads use the same resource
and both of them change it simultaneously the situation would become
unreliable and erratic.
Let me illustrate the need for synchronization of threads using a simple
example. Consider the following method.
[India[Nagpur[KICIT]
]
]
package sample ;
public class Sample
{
static public void main ( String args[ ] ) throws Exception
{
Output c = new Output( ) ;
Ex t1 = new Ex ( c, "KICIT" ) ;
t1.start( ) ;
Ex t2 = new Ex ( c,"Nagpur" ) ;
t2.start( ) ;
Ex t3 = new Ex ( c,"India" ) ;
t3.start( ) ;
t1.join( ) ;
t2.join( ) ;
t3.join( ) ;
}
}
class Ex extends Thread
{
private Output o ;
private String message ;
[KICIT]
[NAGPUR]
[INDIA]
Inter-thread Communication
The programs in the Synchronization section unconditionally blocked
other threads from asynchronous access to certain methods. To improve
the overall performance of the program there should be a mechanism to
notify a waiting thread that it can start running. This means that one
thread should be able to communicate with the other. To achieve this
Java provides three methods wait( ), notify( ) and notifyAll( ). Given
below is the purpose of each of these methods.
Figure 17.1
Chapter 17: Multithreading 347
One of the places where usage of this method makes sense is in
implementing a classical Computer Science algorithm called Producer -
Consumer algorithm. This algorithm is described in the Exercise at the
end of this chapter.
Thread Priorities
If we wish, we can assign priorities to each running thread. This helps
the scheduler to determine the order in which these threads are
executed. Threads with higher priority are more important to a program
and are allocated processor time before lower-priority threads. A Java
thread can have three standard Priorities MIN_PRIORITY,
MAX_PRIORITY, NORM_PRIORITY. These represent numbers 1, 10 and 5.
Java provides following functions to set a new priority for a thread and
(d) If we create a class that inherits from the Thread class, we can still
inherit our class from some other class.
(i) To launch a thread we must explicitly call the run( ) method defined
in a class that extends the Thread class.
[B] Pick up the correct alternative for each of the following questions:
(a) Which are the two methods available for launching threads in a
Java program?
(b) What are the pros and cons of using two different methods of
launching threads in a Java program?
350 Let Us Java
Working :
- Consumer must wait while Producer is producing
- Once Producer has produced it would send signal to Consumer
- Producer must wait while Consumer is consuming
- Once Consumer has consumed it would send signal to Producer
Thread priorities are used to schedule thread execution
Higher priority threads get more CPU time and may preempt lower
priority threads
Standard Priorities :
- MIN_PRIORITY, MAX_PRIORITY, NORM_PRIORITY
- These are constants with values 1, 10, 5
Functions to set and get priorities :
- final void setPriority ( int level )
- final int getPriority( )
Generalizations are good. Especially so, when the Compiler
handles the specializations...
353
354 Let Us Java
Generic Functions
Multiple Argument Types
Generic Classes
Bounded Generics
Exercises
KanNotes
.
Generic Functions
Suppose you wish to print contents of an integer array. To achieve this
we can write a function as shown below:
// etc...
Have we gained anything by writing these overloaded functions? Not
much, because we still have to write a separate definition for each type.
This results into three disadvantages:
(a) Rewriting the same function body over and over for different types
is time consuming.
(b) The program consumes more disk space.
(c) If we decide to modify one such function, we need to remember to
make the modification in other overloaded functions.
e if we could write such a function just once, and make it
work for many different data types. This is exactly what function
generics do for us.
The following program shows how to write the printArr( ) function as a
generic function, so that it will work with any standard type. We have
invoked this function from main( ) for different data types.
package genericfunction ;
public class GenericFunction
{
public static <T> void printArray ( T[ ] arr )
{
for ( T i : arr )
System.out.printf ( "%s ", i ) ;
System.out.println( ) ;
}
public static void main ( String args[ ] )
{
Integer[ ] intarr = { 10, -2, 37, 42, 15 } ;
Float[ ] floatarr = { 3.14f, 6.28f, -1.5f, -3.44f, 7.234f } ;
Chapter 18: Generics 357
Character[ ] chararr = { 'Q', 'U', 'E', 'S', 'T' } ;
printArray ( intarr ) ;
printArray ( floatarr ) ;
printArray ( chararr ) ;
}
}
10 -2 37 42 15
3.14 6.28 -1.5 -3.44 7.234
QUEST
As you can see, the printArr( ) function now works with different data
types that we use as arguments.
way to reuse object code. Generics provide a way to reuse the source
code. Generics can significantly reduce source code size and increase
code flexibility.
Let us now understand what grants the generic function the flexibility to
work with different data types. Here is the definition of the printArr( )
System.out.println( ) ;
}
To help you fix your ideas about generics, here is another program that
uses a generic function. This one obtains the minimum of two quantities
using a generic minimum( ) function.
package minusinggenerics ;
public class MinUsingGenerics
{
public static <T extends Comparable <T> > T minimum ( T a, T b )
{
if ( a.compareTo ( b ) < 0 )
return a ;
else
return b ;
}
public static void main ( String[ ] args )
{
Float a = 3.14f, b = -6.28f, c ;
c = minimum ( a, b ) ;
System.out.println ( c ) ;
-6.28
A
1.1
}
Chapter 18: Generics 359
The above definition means that this function would work with all those
types which implement the Comparable interface. In our case the
classes Integer, Float and Character classes implement this interface, so
we can use the minimum( ) function with these types.
Note that this function cannot compare two Integer or two Float objects
using relational operators like >, <, etc. Hence to actually carry out the
comparison we have used the compareTo( ) function of Integer / Float /
Character class.
We can extend the same comparison logic and write a program that
sorts Integers, Floats, Characters using a generic sorting function. Here
package genericsorting ;
public class GenericSorting
{
public static void main ( String[ ] args )
{
Float num[ ] = { 5.4f, 3.23f, 2.15f, 1.09f, 34.66f } ;
Integer arr[ ] = { -12, 23, 14, 0, 245, 78 , 66, -9 } ;
int i ;
sort ( num, 5 ) ;
for ( i = 0 ; i <= 4 ; i++ )
System.out.print ( num[ i ] + " " ) ;
System.out.println( ) ;
sort ( arr, 8 ) ;
for ( i = 0 ; i <= 7 ; i++ )
System.out.print ( arr[ i ] + " " ) ;
}
public static <T extends Comparable <T> > void sort ( T[ ] n, int size )
{
int i, j ;
Tt;
t = n[ i ] ;
n[ i ] = n[ j ] ;
n[ j ] = t ;
}
}
}
}
}
I do not intend to explain the actual working of the sorting logic. This
topic has been dealt with thoroughly in all the standard books on Data
Structures. What you need to concentrate here is, how to write generic
functions that can work for variety of data types.
package mulitpletypesgenericfunction ;
public class MulitpleTypesGenericFunction
{
public static void main ( String[ ] args )
{
Integer i = 10 ;
Float j = 3.14f ;
Character ch = 'A' ;
printTypes ( i, j, ch ) ;
}
public static <T, S, Z> void printTypes ( T a, S b, Z c )
{
System.out.println ( "a = " + a ) ;
System.out.println ( "b = " + b ) ;
System.out.println ( "c = " + c ) ;
}
}
Chapter 18: Generics 361
The printTypes( ) function can receive three different types of
arguments represented by T, S and Z. It simply prints all the arguments
that it receives. Would the function work, if we pass to it arguments of
same types? Yes, it will. So the following call would be perfectly valid.
Generic Classes
The concept of generics can be extended even to classes. Generic
classes are often used for data storage. In fact Java provides a library of
container classes that implement data structures like stack, queue,
linked lists, binary tree, hash map, etc. These implementations are based
on generic classes.
Let us try implementing a Stack class as a generic class. This class should
be able to maintain a stack of Integers, Floats, Characters etc. Here is a
program with this generic stack class in action.
package genericstack ;
public class GenericStack
{
public static void main ( String[ ] args )
{
Stack <Integer> s1 ;
s1 = new Stack <Integer> ( 10 ) ;
if ( ! s1.isFull( ) )
s1.push ( 10 ) ;
if ( ! s1.isFull( ) )
s1.push ( 20 ) ;
if ( ! s1.isFull( ) )
s1.push ( 30 ) ;
int data1 ;
if ( ! s1.isEmpty( ) )
{
data1 = s1.pop( ) ;
System.out.println ( data1 ) ;
}
if ( ! s1.isEmpty( ) )
362 Let Us Java
{
data1 = s1.pop( ) ;
System.out.println ( data1 ) ;
}
Stack <Float> s2 ;
s2 = new Stack <Float> ( 10 ) ;
if ( ! s2.isFull( ) )
s2.push ( 10.5f ) ;
if ( ! s2.isFull( ) )
s2.push ( 20.5f ) ;
if ( ! s2.isFull( ) )
s2.push ( 18.5f ) ;
float data2 ;
if ( ! s2.isEmpty( ) )
{
data2 = s2.pop( ) ;
System.out.println ( data2 ) ;
}
if ( ! s2.isEmpty( ) )
{
data2 = s2.pop( ) ;
System.out.println ( data2 ) ;
}
Stack <Complex> s3 ;
s3 = new Stack <Complex> ( 10 ) ;
if ( ! s3.isFull( ) )
s3.push ( c1 ) ;
if ( ! s3.isFull( ) )
s3.push ( c2 ) ;
if ( ! s3.isFull( ) )
s3.push ( c3 ) ;
Chapter 18: Generics 363
Complex c ;
if ( ! s3.isEmpty( ) )
{
c = s3.pop( ) ;
c.printData( ) ;
}
if ( ! s3.isEmpty( ) )
{
c = s3.pop( ) ;
c.printData( ) ;
}
}
}
Stack ( int sz )
{
size = sz ;
top = -1 ;
arr = ( T[ ] ) new Object[ sz ] ;
}
boolean isFull( )
{
if ( top == size )
return true ;
else
return false ;
}
void push ( T data )
{
top++ ;
arr [ top ] = data ;
}
boolean isEmpty( )
364 Let Us Java
{
if ( top == -1 )
return true ;
else
return false ;
}
T pop( )
{
T val ;
val = arr [ top ] ;
top-- ;
return val ;
}
}
class Complex
{
float r, i ;
We have created three stacks here s1, s2 and s3 and pushed three
objects on each one. Then we have popped the values from the three
stacks and displayed them on the screen. s1 and s2 maintain a stack of
objects of ready-made classes Integer and Float. We have also declared
a class called Complex and then pushed/ popped Complex objects
to/from stack s3
30
20
18.5
20.5
Chapter 18: Generics 365
Real = 5.5 Imag = 6.6
Real = 3.3 Imag = 4.4
You can observe that the order in which the elements are popped from
the stack is exactly reverse of the order in which they were pushed on
the stack.
The way to build a generic class is similar to the one used for building a
generic function. The <T> signals that the class is going to be a generic
class. This is precisely how we have defined the Stack class. Its skeleton
is shown below.
It the Stack class, the type T is used at every place in the class where
there is a reference to the type of the array arr. There are four such
places the definition of arr, the constructor, the argument type of the
push( ) function, and the return type of the pop( ) function. Do take a
look at these four functions in our program.
To create objects of this generic class we have used the statements like,
Stack <Integer> s1 ;
s1 = new Stack <Integer> ( 10 ) ;
Here, firstly an array of Objects is created and the address of this array is
typecasted into an address of array of type T.
In the constructor, to indicate emptiness of stack we have initiated top
to a value -1. This variable is going to act as an index into the array in
which the values pushed into the stack are going to be stored. We have
also preserved the value of array size in the variable size. Later, in
functions isEmpty( ) and isFull( ) we have used these values to check
whether stack is empty or full.
366 Let Us Java
Note that it is also possible to inherit a new class from a generic class.
Bounded Generics
Let us now define and use a generic class called Statistics. This class
obtains average of Integers or Floats. This should be fairly simple.
However, the twist here is, we should not be allowed to find average of
This means that the Statistics class should not work for strings. Such
classes are known as Bounded Generics. It is very simple to accomplish
this. While defining the Statistics class we should define it through the
statement
This ensures that Statistics class can work only with those types that are
derived from Number. Incidentally, Integer and Float both are derived
from Number class, so Statistics can work with objects of these classes.
Here is the full-fledged program.
package statsdemo ;
public class StatsDemo
{
public static void main ( String[ ] args )
{
Integer iarr[ ] = { 1, 2, 3, 4, 5 } ;
Statistics <Integer> iobj ;
double avg1 ;
Statistics ( T[ ] obj )
{
arr = obj ;
}
public double getAverage( )
{
double sum = 0.0 ;
(d) Generic functions cannot work for primitives like int, float, char,
etc.
(a) Write a program that will implement a linked list through a generic
class.
(b) Write a program that has a generic class that can sort dates and
strings apart from integers and floats.
[C] Pick up the correct alternative for each of the following questions:
Once the generic function / class is ready we can use them with any
reference type
Primitives are often called value types, whereas classes are called
reference types
{
..
}
Generic function that can work with types that implements a
Comparable interface
public static <T extends Comparable <T> > T min ( T a, T b )
{
}
Generic function that can receive multiple types
public static <T, S, Z> void printTypes ( T a, S b, Z c )
{
System.out.println ( "a = " + a + " b = " + b + " c = " + c ) ;
}
Syntax for using and defining a generic class :
// using generic class
stack <Integer> s1 ;
s1 = new stack <Integer> ( 10 );
s1.push ( 10 ) ;
For example, the following class would work only for those types
that are derived from the Java API Number class :
class Statistics <T extends Number>
{
..
}
There are many standard ways of storing and accessing data.
Let Java Collections handle that, so that you can concentrate on
building something bigger using them...
371
372 Let Us Java
(a) We may not want fixed-size arrays. We may want arrays to grow in
size dynamically as we keep adding new elements to it. This
requirement cannot be met by normal arrays, at least not without
an effort of allocating space for bigger-sized array, copying existing
elements into this space, etc.
(b) There may be a need to maintain data in different ways like
Dictionary (where order is important), Key-Value maps, like cell
number (key) and name (value).
(c) There may be a need to access data in different ways Last In First
Out (as in a stack), First In First Out (as in a queue), or sorted order.
Given in Figure 19.1 is a very short list of classes and interfaces available
in the collections framework. This list is by no means exhaustive or
complete, but is given here just to give you an idea of how the
collections framework is organized.
The classes and interfaces of the collections framework are defined in
java.util package.
In summary, we can say that collections framework provides
prepackaged data structures plus the algorithms to manipulate them.
Figure 19.1
Chapter 19: Java Collections 375
package arraylistdemo ;
import java.util.* ;
public class ArrayListDemo
{
public static void main ( String[ ] args )
{
ArrayList <String> alnames ;
if ( alnames.contains ( "Aditya" ) )
System.out.println ( "Aditya is present in the array list" ) ;
int sum = 0 ;
for ( int i = 0 ; i < alnums.size( ) ; i++ )
sum = sum + alnums.get ( i ) ;
376 Let Us Java
sum = 0 ;
for ( int n : arr )
sum += n ;
Here is th
instead of names.
We have obtained the sum of all integers by retrieving each integer
using the get( ) function. Note that get( ) returns an Integer, not an int.
The size( ) function yields the current size of the array list.
Chapter 19: Java Collections 377
The array maintained by array list can be converted into the normal Java
array using the toArray( ) function. This array can then be iterated over
using the special for loop as shown in the program.
Maintaining a Stack
A Stack is a data structure in which addition of new element or deletion
of an existing element always takes place at the same end. This end is
often known as top of stack. This situation can be compared to a stack
of plates in a cafeteria where every new plate added to the stack is
added at the top. Similarly, every plate taken off the stack is also from
the top of the stack. Thus stack is a last-in-first-out (LIFO) list. When an
item is added to a stack, the operation is called push, and when an item
is removed from the stack the operation is called pop.
Given below is a program that maintains a stack of city names using the
collection class called Stack. Note that before calling the pop( ) function
we need to ascertain whether the stack has any element left in it. This is
done by calling the isEmpty( ) function.
package stackdemo ;
import java.util.* ;
public class StackDemo
{
public static void main ( String args[ ] )
{
Stack < String > s ;
s = new Stack <> ( ) ;
s.push ( "Delhi" ) ;
s.push ( "Nagpur" ) ;
s.push ( "Indore" ) ;
s.push ( "Raipur" ) ;
s.push ( "Mysore" ) ;
s.push ( "Mumbai" ) ;
String str ;
if ( ! s.isEmpty( ) )
{
str = s.pop( ) ;
System.out.println ( str ) ;
}
378 Let Us Java
if ( ! s.isEmpty( ) )
{
str = s.pop( ) ;
System.out.println ( str ) ;
}
}
}
Figure 19.2
Observe that the linked list is a collection of elements called nodes, each
of which stores two items of information an element of the list and a
link. A link is a reference or an address that indicates explicitly the
location of the node containing the successor of the list element. In
Figure 19.2, the arrows represent the links. The data part of each node
consists of the marks obtained by a student, and the link part is a
pointer to the next node. The NULL in the last node indicates that this is
the last node in the list.
Instead of marks, we can maintain a linked list of names. If we want, we
can maintain both in each node. The program given below uses the
collection class LinkedList to maintain a linked list of names of students.
Most of the operations in the program are self-explanatory. Go through
the program carefully, a step at a time.
Chapter 19: Java Collections 379
package linkedlistdemo ;
import java.util.* ;
public class LinkedListDemo
{
public static void main ( String[ ] args )
{
LinkedList <String> ll ;
ll = new LinkedList <> ( ) ;
ll.add ( "Subhash" ) ;
ll.add ( "Rahul" ) ;
ll.add ( "Joe" ) ;
ll.add ( "Vineeta" ) ;
for ( String s : ll )
System.out.println ( s ) ;
ll.set ( 2, "Neha" ) ;
System.out.println ( ll ) ;
Subhash
Rahul
Joe
Vineeta
[Subhash, Rahul, Neha, Vineeta]
String at position 2 = Neha
[Subhash, Rahul, Neha, Sanjay, Vineeta]
[Subhash, Neha, Sanjay, Vineeta]
Maintaining a Tree
The data structures such as linked lists, stacks and queues are linear
data structures. As against this, trees are non-linear data structures. In a
380 Let Us Java
linked list each node has a link which points to another node. In a tree
structure, however, each node may point to several other nodes (which
may then point to several other nodes, etc.). Thus a tree is a very flexible
and powerful data structure that can be used for a wide variety of
applications. For example, suppose we wish to use a data structure to
represent a person and all of his or her descendants. Assume that the
person's name is Rahul and that he has 3 children, Sanjay, Sameer and
Nisha. Also suppose that Sameer has 3 children, Abha, Ram and Madhu
and Nisha has one child Neha. We can represent Rahul and his
descendants with the tree structure shown in Figure 19.3.
Figure 19.4
Notice that each tree node contains a name for data and one or more
pointers to the other tree nodes.
Although the nodes in a general tree may contain any number of
pointers to the other tree nodes, a large number of data structures have
at the most two pointers to the other tree nodes. This type of a tree is
called a Binary Tree.
Many algorithms that use binary trees proceed in two phases. The first
phase builds a binary tree, and the second traverses the tree. Suppose
we wish that while traversing the binary tree we should be able to
access the elements in it in ascending order. To ensure this we need to
arrange the elements properly during insertion. A simple logic to do so
would be to compare the element to be inserted with the element in the
root node and then take the left branch if the element is smaller than
the element in the node, and a right branch if it is greater or equal to
the element in the node. Thus if the input list is
3, 9, 1, 4, 7, 11
Chapter 19: Java Collections 381
then using this insertion method the binary tree shown in Figure 19.4
would be produced.
Figure 19.4
Such a binary tree has the property that all the elements in the left sub-
tree of any node n are less than the contents of n. And all the elements
in the right sub-tree of n are greater than or equal to the contents of n.
A binary tree that has these properties is called a Binary Search Tree.
If a binary search tree is traversed in in-order, i.e., in the order left child,
root, and right child and the contents of each node are printed as each
node is visited, the numbers are printed in ascending order. This is
demonstrated in the program given below.
package treesetdemo ;
import java.util.* ;
public class TreeSetDemo
{
public static void main ( String args[ ] )
{
TreeSet <Integer> ts ;
ts = new TreeSet <> ( ) ;
ts.add ( 3 ) ;
ts.add ( 9 ) ;
382 Let Us Java
ts.add ( 1 ) ;
ts.add ( 4 ) ;
ts.add ( 7 ) ;
ts.add ( 11 ) ;
System.out.println ( ts ) ;
System.out.println ( ts.subSet ( 4, 11 ) ) ;
ts.clear( ) ;
System.out.println ( ts ) ;
}
}
[1, 3, 4, 7, 9, 11]
[4, 7, 9, 11]
[]
From the output you can see that the subSet( ) function gives all those
nodes that lie between the nodes passed to it. Also, to delete all the
nodes in the tree at one shot, the clear( ) function can be used.
Maintaining a HashMap
The HashMap class lets us maintain a set of key - value pairs. For
example, we can maintain key - value pairs of cell numbers and names,
or key - value pairs of day names in English and Hindi. Against each key
multiple values may also be maintained. For example, against cell
number we can store the name, address and photograph. The key -
value pairs may not be stored in the same order as the order of
insertion. We can get the order in which they are being maintained by
printing out the hash map. This is shown in the following program.
package hashmapdemo ;
import java.util.* ;
System.out.println ( hm ) ;
String str ;
str = hm.get ( "Wed" ) ;
System.out.println ( "Wed in hindi is " + str ) ;
}
}
The output of the program is shown below. Note that the get( ) function
can be used to obtain the value stored against the key passed to it.
package arraysdemo ;
import java.util.* ;
public class ArraysDemo
{
public static void main ( String[ ] args )
{
int arr[ ] = new int[ 5 ] ;
Random r = new Random( ) ;
{
arr[ i ] = r.nextInt ( 25 ) ;
System.out.println ( arr[ i ] ) ;
}
Arrays.sort ( arr ) ;
System.out.println ( "After sorting: " ) ;
for ( int i = 0 ; i < arr.length ; i++ )
System.out.println ( arr[ i ] ) ;
Arrays.fill ( arr, 2, 4, -3 ) ;
System.out.println ( "After filling: " ) ;
for ( int i = 0 ; i < arr.length ; i++ )
System.out.println ( arr[ i ] ) ;
int pos ;
pos = Arrays.binarySearch ( arr, -3 ) ;
System.out.println ( "pos = " + pos ) ;
}
}
15
13
13
2
18
After sorting:
2
13
13
15
18
After filling:
2
13
-3
-3
18
pos = 2
Chapter 19: Java Collections 385
The program generates random numbers using nextInt( ) function of
Random class and then populates the array arr with these randomly
generated integers. Next, it calls the sort( ) function to sort these
numbers.
The call to fill( ) function fills the array with -3 starting from 2 nd position
up to and excluding the 4th position. Then the program uses the
binarySearch( ) function to search the position of first occurrence of -3
in the array.
I hope now you have got a fair idea of how to use the Java collections
framework. You can explore the other collection classes, interfaces and
algorithms of the framework on your own.
(h) All binary trees are maintained by TreeSet class as binary search
trees.
[B] Pick up the correct alternative for each of the following questions:
(a) Which of the following is the CORRECT import statement for using
classes in Java collection framework?
(1) import java.io.*
(2) import java.util.*
(3) import java.*
(4) import java.collections.*
ArrayList < Integer > num = new ArrayList < Integer > ( ) ;
num.add ( 10 ) ;
num.add ( 20 ) ;
num.add ( 30 ) ;
num.add ( 40 ) ;
Integer arr [ ] = new Integer[ num.size( ) ] ;
// add statement here
for ( int n: arr )
System.out.println ( n ) ;
Chapter 19: Java Collections 387
Which statement will you add for the code to work?
Vector class and ArrayList class both can maintain arrays that grow
dynamically
The order in which we insert entries into a HashMap and the order in
which they are stored may be different
Text is gone! Graphics is the way forward. Learn how to build
Graphical User Interfaces in Java...
389
390 Let Us Java
Text Field
Label
Button Panel
Figure 20.1
Given below are the steps that we should carry out to create this
application using NetBeans.
Step I Create a Java Application, give Project Name as GUIApp. Choose a
suitable location on your disk for creating the files of this application.
Uncheck the check box.
392 Let Us Java
Step II To create the window for the application, add new JFrame form to
the application. For this right click on the GUIApp project in the project
On doing so, it
will ask you to supply the name of the class to represent the window. Type
ConvertTemp
Step III At the end of step II a window would appear in NetBeans. Now
we need to insert Container (Panel) and Controls (Labels, Text fields,
Button) in this window. Drag and drop them from the Swing Containers
and Controls window that appears besides the frame window.
Step IV roperty of the two label controls and
str = txtTempC.getText( ) ;
c = Float.parseFloat ( str ) ;
f = c * 9 / 5 + 32 ;
str = Float.toString ( f ) ;
txtTempF.setText ( str ) ;
}
Step VIII Compile and execute the program using F6. On execution the
window with the container and controls we had inserted would appear.
Chapter 20: User Interfaces 393
On providing the temperature in Centigrade and clicking the Convert
button the temperature in Fahrenheit would get displayed.
So much about creating our first GUI application using Swing library. Let
us now understand what we did in this application. Given below is the
source code that the wizard has created for us as we were creating the
application.
package converttemp ;
public class ConvertTemp extends javax.swing.JFrame
{
private javax.swing.JPanel jPanel1 ;
private javax.swing.JLabel jLabel1 ;
private javax.swing.JLabel jLabel2 ;
private javax.swing.JTextField txtTempC ;
private javax.swing.JTextField txtTempF ;
private javax.swing.JButton btnConvert ;
public ConvertTemp( )
{
initComponents( ) ;
}
But somewhere the objects of JPanel, JButton, JTextField, etc. also need
to be created. Well, that is what is done in the initComponents( )
function that has been called from the ConvertTemp
fact if you take a look at this function you would see apart from creation
of these objects, properties of these objects being setup. These include
position, size, color, etc. You can also observe statements to add all
these controls to the window. You are best advised not to edit the code
in initComponents( ) directly.
The wizard would add the code to create and display the window in
main( ). When the window is created, an object of ConvertTemp would
be created. This would result into call to its constructor and in turn to
initComponents( ).
One question that must be troubling you what is the difference
between a container and a control? A control is something that the user
interacts with, like a push button, a check box or a combo box. As the
name suggests, a container is something that would hold these visual
controls.
Now that we have understood the code to create the window, container
and controls, let us now turn our attention to a phenomenon called
event handling.
Event Handling
In simplest words an event is a thing that takes place. Programmatically
it means change in the state of an object. Events occur all the time when
we are interacting with a GUI application. For example, when we enter a
character from keyboard, or move the mouse, or click the left mouse
button, events occur. These events are generated as a consequence of
interaction with the graphical components in the GUI. Such events are
known as Foreground events.
Apart from these, events also occur without any user interaction. For
example, expiry of a timer, completion of some ongoing task,
occurrence of an interrupt, etc. Such events are known as Background
Events.
When an event occurs, the program is supposed to react to that event.
That reaction is known as event handling. Programmatically, a function
known as event handler gets executed when an event occurs. To ensure
that all events are handled in a standard manner, Java uses a
Chapter 20: User Interfaces 395
mechanism called Event Delegation Model to handle the events. This
model involves two key players:
(a) We dragged and dropped the button in our window and gave it a
name btnConvert.
(b) For the Convert button, for the actionPerformed event, we added
an event handler function. We called this function
btnConvertActionPerformed( ).
Figure 20.2
As you can see in Figure 20.2, there are several labels, 4 text fields (for
Name, Age, Salary and Address), 1 list box (for Grade of employee), 2
radio buttons (for Sex of employee), 3 check boxes (for Hobbies of
employee) and 1 button (Show button) in the window. The user would
interact with different controls and either type or select the data values
for an employee. Once the Show button is clicked the typed or selected
values should be displayed in a message box as shown in Figure 20.3.
Figure 20.3
398 Let Us Java
To create this application, you should follow exactly the same steps that
were discussed while creating the first GUI application in the previous
section.
Regarding GUI in this application, one additional thing that you need to
do is manage the mutual exclusivity of the radio buttons for Male /
Female. To do this, first insert two radio buttons and then insert a
Button Group control. Change ButtonGroup property of both radio
buttons to have a value same as name of the Button Group control.
Once this is done, you can choose only one out of the two radio buttons
at a time.
Also, for Combo Box by default model property would have some
default values. Edit this property to add values Grade I, Grade II, Grade
III and Grade IV to it.
Once again add an event handler for the button for actionPerformed
event. Once created, add the following code in the event handler to
display the typed / selected values in a message box.
strName = txtName.getText ( ) ;
strAge = txtAge.getText( ) ;
strSalary = txtSalary.getText( ) ;
strAddress = txtAddress.getText( ) ;
strGrade = cbGrade.getName( ) ;
if ( rbMale.isSelected( ) )
strSex = rbMale.getText( ) ;
if ( rbFemale.isSelected( ) )
Chapter 20: User Interfaces 399
strSex = rbFemale.getText( ) ;
if ( ckbxSports.isSelected( ) )
strSports = ckbxSports.getText( ) ;
if ( ckbxReading.isSelected( ) )
strReading = ckbxReading.getText( ) ;
if ( ckbxTravelling.isSelected( ) )
strTravelling = ckbxTravelling.getText( ) ;
In this event handler we have extracted the values from the text fields
using calls to the getText( ) function. Which of the radio buttons and
check boxes have been selected is checked using the isSelected( )
function. The actual selections are again obtained using the getText( )
function. The grade selected from Combobox is collected using the
function getName( ).
All the strings extracted from these functions are concatenated, with
\
str is displayed using the static method ShowMessage( ) of the
JOptionPane class. The information icon is displayed using the enum
value INFORMATION_MESSAGE.
Adapter Classes
Suppose we wish to interact with mouse in our GUI application. For this
we need to add the MouseListener interface. If we do this then all the
methods in the MouseListener interface need to be implemented in our
class. Problem is that MouseListener has five methods in it. These are as
follows:
What Next?
There are many controls and interfaces in Java swing API. The intention
of this chapter was to introduce you to some of them and discuss the
basic philosophy behind creating modern GUI and handling events. The
Swing library is very exhaustive and covering all classes in it would need
a separate book. Nevertheless, through this chapter you got introduced
to the Swing API and its working. Rest you are free to explore on your
own.
Chapter 20: User Interfaces 401
(f) For every event related interface available in Swing library there is
one equivalent adapter class.
(i) The Even Delegation model ensures that the code that creates
controls and events remains separate from the code that reacts to
events.
it.
(c) Write a program that draws a line, rectangle and ellipse of suitable
402 Let Us Java
For every window and control there are Swing classes available
403
404 Let Us Java
Data Organization
Common Database Operations
Database Operations through Java
JDBC Architecture
JDBC Driver Types
MySQL Database Installation
Common JDBC API Components
Putting it to Work
Exercises
KanNotes
Chapter 21: JDBC 405
D being generated and exchanged. All this data finally gets stored in a
database. As a Java programmer one must know how to handle this data
programmatically. To help us do this Java provides an API called JDBC. It
stands for Java Database Connectivity. This API lets us write Java
programs that can interact with a wide range of databases. How this can
be done is discussed in this chapter.
Data Organization
Modern way of organizing data is storing it in a Relational Database
Management System or RDBMS. Different vendors provide this RDBMS
software. These include Oracle, Microsoft, IBM, etc. There are several
open source implementations available as well, the most popular
amongst which is MySQL. All these RDBMSs are accessible through the
JDBC API.
Each of these RDBMS organizes the data in the form of different tables.
One database may contain multiple tables. Each table contains data
organized in the form of records (rows). Each record may contain
multiple fields (columns).
For example, a company may have a database containing Employees
table containing records about employees working in an organization.
Each record may contain fields like Name, Age, Salary, etc.
Likewise a University database may consist of tables for students,
professors, courses, examinations, payments, etc. It is also possible to
establish relationships between tables. For example, if a student pays
fees, then the record of fees paid can be linked to his record in the
student table. This would be a one-to-one relationship. If the same
student pays fees multiple times then it would become a one-to-many
relationship.
(a) Create Table - Create a table by specifying its name and the fields
that it would contain along with the type of each field.
(b) Modify Table - Modify the specifications of different fields, or add /
delete certain fields.
406 Let Us Java
(c) Drop Table - Delete the table from the database including all the
records present in it.
// Create a table called Persons containing two fields EmpID of the type
// int and Name of the type variable length string of 255 characters
CREATE TABLE Persons ( EmpID int, Name varchar ( 255 ) )
SQL also provides statements that let you work with the records of each
table. Common operations on a table include Create new record(s),
Read existing record(s), Update existing record(s), Delete existing
record(s). In short these are known as CRUD operations. The SQL
statements that carry out these operations are often known as SQL
queries. Given below are some sample SQL statements for carrying out
CRUD operations.
// Insert a new record in Persons table, with values 101 and Sunil in the
// EmpID and Name respectively
INSERT INTO Persons ( EmpID, Name) VALUES ( 1001
// Delete that record from the Persons table whose Employee ID is 1244
DELETE FROM Persons WHERE EmpID = 1244
JDBC Architecture
To help programmers communicate with the database, vendors provide
vendor-specific JDBC driver software. For example, Oracle provides a
JDBC driver to help programmers communicate with databases
maintained by it. Likewise, Microsoft provides a JDBC driver to help
programmers communicate with databases maintained by MS SQL.
Java programmers must have a standard and uniform way to
communicate with any third-party JDBC driver. To facilitate this, an
408 Let Us Java
Figure 21.1
In a later part of the chapter we would see how to use the MySQL
Workbench to create a database and its tables(s). We would also see
how to use the JDBC driver to work with the database programmatically.
Putting it to Work
We have now understood the data organization, SQL statements for
database operations, JDBC architecture and JDBC API components. We
have also seen how to install MySQL and MySQL Workbench. So it is
now time to write a Java program that accomplishes the following:
Out of these, steps (a) and (b) are to be performed using MySQL
workbench, whereas the rest are to be performed through the Java
program.
So let us now create a schema, add table to it and then add 4 records to
it. Carry out the following steps to achieve this:
(d) Click on the Columns tab at the bottom of the page and create
three columns with following properties.
(e)
Name and Balance would be shown. Add 4 records with values
mentioned in the problem statement above.
Now finally we have reached a stage where we can write a Java program
to carry out steps (c), (d), (e) and (f) given in the problem statement.
package myjdbccrud ;
import java.sql.* ;
try
{
Class.forName ( jdbcDriver ) ;
conn = DriverManager.getConnection ( dbURL,
"root", "admin" ) ;
stmt = conn.createStatement( ) ;
412 Let Us Java
String sql ;
sql = "INSERT INTO Accounts VALUES ( 1001, 'Joe',
5000.0 )" ;
stmt.executeUpdate ( sql ) ;
int id ;
String name ;
float balance ;
while ( rs.next( ) )
{
id = rs.getInt ( "ID" ) ;
name = rs.getString ( "Name" ) ;
balance = rs.getFloat ( "Balance" ) ;
rs.close( ) ;
stmt.close( ) ;
}
finally
{
if ( conn != null )
conn.close( ) ;
}
}
}
Let us now to try to understand the program. The project name given
was MyJdbcCrud, hence the classes in this program would belong to the
Chapter 21: JDBC 413
package myjdbccrud, as indicated in the package statement at the
beginning of the program.
The import statement ensures that the classes declared in java.sql
package for database access are available to the program.
Now we need to open a communication channel with the database. For
this we need to load and register the JDBC driver. This registration
needs to be done only once in the program. We have done this
registration through the call
Class.forName ( jdbcDriver ) ;
stmt = conn.createStatement( ) ;
String sql ;
sql = "INSERT INTO Accounts VALUES ( 1001, 'Joe', 5000.0 )";
stmt.executeUpdate ( sql ) ;
Create, update and delete operations are similar in the sense that to
perform all of them the executeUpdate( ) method has to be called. The
Read operation is a bit different. For it we need to call the
executeQuery( ) method on the Statement object. When we do this, the
query is fired on the database and all the records that qualify the query
are returned in the form of a ResultSet object. For example, when we
ELECT Accounts
table would qualify this query and hence would be returned together in
a ResultSet object.
We can iterate through all the records in the ResultSet object through a
while loop. Each time through the loop we can extract the individual
field values in the record by calling the ResultSet methods as shown
below.
id = getInt ( "ID" ) ;
name = rs.getString ( "Name" ) ;
balance = rs.getFloat ( "Balance" ) ;
We have extracted the values and displayed them on the screen. Once
all the records have been iterated, rs.next( ) returns a false, whereupon
the loop is terminated.
That brings us to the final stage of the program where we need to do
the cleanup operations. We do this by calling the close( ) methods on
Statement, ResultSet and Connection objects.
Chapter 21: JDBC 415
One small thing needs to be done before you can execute the program.
We need to add the JDBC library. Carry out the following steps to do
this:
(a) ibrarie
project window.
(b)
(c) Navigate to the suitable directory where you have downloaded the
mysql-connector-java-5.1.40-bin
(d) Click Open followed by OK.
Once the library has been added we can now use F6 to build and
execute the program.
(d) Advantage of JDBC is that the same driver can be used to connect
multiple RDBMSs.
(h) Driver
416 Let Us Java
(a) Write a program which lets you carry out the CRUD operations
through a GUI shown in Figure 21.2. Use the same database and
table discussed in t
Note that all the records added to the table should get displayed in
the list box. Before carrying out Delete or Update operations the
record should be searched using the ID. A new record should be
added or the existing record should be modified on clicking the
Commit button.
Figure 21.2
[C] Pick up the correct alternative for each of the following questions:
Terminology :
- Field - Individual item of information
- Record - Collection of fields
- Table - Collection of records
- Database - Collection of tables
Different vendors provide RDBMS. Ex. : Oracle, MS SQL, MySQL
418 Let Us Java
SQL statements are often called Queries and are English like
statements
All RDBMS are accessible through Java API - JDBC. To these API
functions SQL queries have to be passed
419
420 Let Us Java
Networking Concepts
Networking Model
Protocols
Packets
IP Addresses
Sockets
Port Numbers
that
is where Network Programming is heading for. Hence learning network
programming has become more relevant today than ever before.
Often we need our application to write some data into a file stored on a
remote machine connected through the network, or exchange messages
across the machines connected to the network. Using the Java
networking API it has become as simple to carry out such jobs.
Networking Concepts
It is important to understand several concepts and terms before we can
actually start writing networking programs. Let us begin with a typical
computer network. Figure 22.1 shows a typical computer network.
We can make the following observations from Figure 22.1.
(a) PCs, Laptops, Mobile phones are client machines (also known as
nodes/hosts). They are connected to Hub/Switch through network
cables or wirelessly.
(b) Database Server, File Server, Print Server, Web Server, Application
Server are also connected to Hub/Switch.
(c) Clients and Servers form the Local Area Network (LAN). All clients
can get services from the servers.
(d) Gateway machine is connected to Hub/Switch and also to Router.
The router would be connected to other routers of other LANs or to
Internet. The routers route the data from one machine to another
along the least congested path.
(e) Gateway machine is so called because clients and servers in the LAN
can communicate with devices in other LANs or Internet through it.
(f) A Hub sends the incoming data packet to every node connected to
it. As against this, a Switch sends the incoming data packet only to
specified node.
(g) Access Point lets wireless devices to connect to LAN.
(h) Since all devices in Figure 22.1 are connected to a centralized
Hub/Switch the arrangement is known as Star topology. There are
422 Let Us Java
other topologies like Bus, Ring, Tree, Mesh each with its set of pros
and cons.
Figure 22.1
(i) All devices are connected to the network using a network adapter.
Most desktops and older laptops contain a Network Interface Card
(NIC) that acts as wired network adapter.
(j) Modern laptops, tablets and cell phones contain wireless network
adapters.
(k) Wireless networking capability can be added to a PC or old laptop by
attaching a wireless network adapter in its USB port.
(l) Some network adapters are actually just software packages that
Chapter 22: Network & Internet Programming 423
(m) Network adapters serve the purpose of transmitting and receiving
data on both, a wired and a wireless network.
Networking Model
In early days of networking PCs from same manufacturer could
communicate with one another. As networking became popular, a need
was felt to help vendors create interoperable network devices and
software. Typical issues involved in networking include how to create
packets, how to detect and correct errors, how to route packets from
one host to another, how to support multiple OS, how to deal with
heterogeneous network cabling, etc.
Towards this end in late 70's IOS defined a standard called Open
Systems Interconnection (OSI) reference model. OSI uses a 7-layered
network model, with each layer responsible for different aspects
(mentioned above) of network communication.
For Internet, the computing industry has combined some of the layers of
OSI model into a single layer. As a result, a 4-layer model called TCP/IP
model has emerged. This model is shown in Figure 22.2.
Figure 22.2
The purpose of each layer in brief is as follows:
(a) Application Layer - This layer provides services for user applications
for sending/receiving emails, web browsing, file transfer, audio
and/or video streaming, etc.
(b) Transport Layer (TCP layer) - This layer ensures that there is a
reliable channel for the application layer. This means that it ensures
424 Let Us Java
that whatever is sent from one end of the connection arrives at the
other end, without errors or omissions, and in the same order as
sent.
The transport layer may have to split the data into packets to give to
the IP layer. The TCP layer has to include sequence information in
the packets to allow it to re-assemble them in the correct order at
the far end, and to detect if a packet has gone missing. It also needs
to be able to resend a packet when this happens.
(c) Internet Layer (IP layer) - This
of data provided by the transport layer from source address to
destination address on the Internet.
(d) Network Interface Layer - This layer carries out actual transmission
of bits with implementations for a wide range of networks -
Ethernet, WiFi, optic fiber, etc.
Each layer receives services from layer below it and provides services to
the layer above it.
layer below it works. It merely uses the service provided to it. This
allows any layer to be swapped out for an alternative, and the layers
t care as long as the service provided by each
underlying layer is the same.
For example, one implementation of network interface layer may send
data over Ethernet, and another implementation handles sending the
data over a phone line. The Internet layer t need to know
anything about the network interface layer. The standard service
interface provided by the network interface layer, regardless of whether
Ethernet or a phone line is carrying the data, allows the Internet layer to
work in exactly the same way regardless. Thus, the layered model makes
the complexity of network communication more manageable.
Note that each layer feels that it is directly communicating with same
layer in another machine. As the data passes down a layer, a header and
trailer specific to that layer gets added to it. Similarly, when the data
reaches the other end and travels up the layers the headers and trailers
are stripped.
Protocols
Protocols are a set of rules that the network, computers and
applications agree upon to carry out communication between devices.
Chapter 22: Network & Internet Programming 425
The commonly used protocols in each layer of TCP/IP model are shown
in Figure 22.3.
Figure 22.3
There are many more protocols available in each layer than the ones
shown in Figure 22.3. All these protocols are implemented as a protocol
stack by the OS (Operating System) and its components.
The Ethernet protocol indicates how bits will be transmitted through
different physical media (network cables like CAT5, Fiber Optic, etc.) at
different speeds.
The IP protocol provides for transmitting blocks of data from source
machine to destination machine. Hosts are identified by a unique
address known as an IP address.
IP is a best effort protocol. It doesn't guarantee the correctness of the
delivered data. The packets may be lost, may get duplicated or may be
delivered out-of-order. These aspects of packet delivery are addressed
by transport layer protocols like TCP and UDP.
The protocols used in the Application layer are chosen based on what
the application intends to do. For example, for browsing Internet, the
HTTP protocol is used, for email SMTP and POP3 protocols are used.
Most of our programs in this chapter will use the protocols in
Application layer.
Packets
Traditional telecommunications links transmit data as a series of bytes,
characters, or bits alone. Unlike this, in a computer network data is
transmitted in the form of Packets. That's the reason why Internet is
often referred to as packet switched network. Once the data is
426 Let Us Java
IP Addresses
To be able to identify the devices in a network and carry out
communication between them using Internet Protocol (IP), each of them
has to be assigned a unique address. This address is called IP address.
There are two IP addressing schemes IPv4 and IPv6. An IPv4 address is 4
byte long, whereas an IPv6 address is 16 bytes long. The IPv4 addresses
are commonly written using 4 numbers (one number per byte) in a
dotted-decimal notation. In this notation, each byte in IP address is
separated using dot. One example of this notation is IP address
192.168.100.10.
To avoid miscommunication between machines in a network, their IP
addresses must be unique. This uniqueness can be achieved in case of
small networks, especially when these networks are not connected to
the outside world. However, to guarantee uniqueness of IP addresses in
big networks spanning cities and continents, the IP addresses are
created and managed by a central authority called Internet Assigned
Numbers Authority (IANA). IANA allocates super-blocks of addresses to
Regional Internet Registrars. These in turn allocate smaller blocks to
Internet Service Providers and enterprises, who in turn further allocate
the addresses to individual organizations who in turn assign them to
individual devices.
Sockets
Another term that is commonly used in network communication is
Socket. Socket is a software construct that identifies an end-point in a
communication channel. A socket is used by applications as an interface
to the underlying network and protocols. Applications that
communicate with one another in a network carry out the
communication using sockets. Java networking API provides classes for
creating sockets and sending / receiving packets through them.
Chapter 22: Network & Internet Programming 427
Port Numbers
It is common to send emails through your email application at the same
time as you download a file from a web site. Here, the email application
is communicating with an email server program residing on another
machine, whereas the web browser is communicating with a web server
program running on yet another machine. The IP address of the machine
on which the email application and the web browser is running is same.
In such a case, the data sent by the web server should not go to the
email application; similarly the data sent by the email server should not
go to the web browser. To avoid such situations the email application
and the web browser use sockets with different port numbers on the
same machine (IP address) for carrying out communication. These ports
are logical ports and not physical ports. They should not be confused
with HDMI or USB ports.
Email program and Web Browser are standard applications. Hence they
use standard port numbers. After all, you should not be required to
make a phone call to the place where the Web Server is present and ask
which port number it is using so that you can send a request to it for
downloading a file. IANA is responsible for assigning standard port
numbers. Port numbers used by applications that use some common
protocols are as follows:
HTTP - 80
SMTP - 25
POP3 - 110
FTP - 20, 21
Time - 37
Telnet - 23
Whois - 43
There is another important reason why the idea of port numbers was
created. The network link speed is usually so high that one application
would not be able to use the entire capacity of the connection by itself.
For example, when you visit a website and a page is downloaded in your
browser, unless you click a link and make a request for another page,
the network link is idle. Hence, to meaningfully utilize the capacity of the
network link, it becomes important to be able to share the same link for
multiple applications. This means multiple applications running on the
machine will use the same IP address of the machine, but different port
numbers.
428 Let Us Java
Figure 22.4
package addresses ;
import java.net.InetAddress ;
ia = InetAddress.getByName ( "www.google.com" ) ;
System.out.println ( "Name: " + ia.getHostName ( ) ) ;
System.out.println ( "Address: " + ia.getHostAddress ( ) ) ;
System.out.println ( "Reachable: " + ia.isReachable(3000));
}
catch ( UnknownHostException ex )
{
ex.printStackTrace ( ) ;
}
}
}
?
There are several time servers on Internet that maintain an accurate
measure of current time. We can write a client program to connect to
one of these servers and obtain the current date and time.
package javatimeclient ;
import java.net.* ;
import java.io.* ;
import java.util.* ;
InputStream is = null ;
s = new Socket ( hostname, port ) ;
is = s.getInputStream( ) ;
int i, ch ;
secSince1900 = 0 ;
for ( i = 0 ; i < 4 ; i++ )
{
ch = is.read( ) ;
secSince1900 = ( secSince1900 << 8 ) | ch ;
}
secSince1970 = secSince1900 - diffBetEpochs ;
msSince1970 = secSince1970 * 1000 ;
time = new Date ( msSince1970 ) ;
System.out.println ( "It is " + time + " at " + hostname ) ;
s.close( ) ;
}
}
Date class we converted the milliseconds since 1970 into date time
format and printed it.
package javawhoisclient ;
import java.io.* ;
import java.net.Socket ;
int c ;
while ( ( c = is.read( ) ) != -1 )
System.out.print( ( char ) c ) ;
s.close( ) ;
}
}
package javhttpclient ;
import java.net.* ;
import java.io.* ;
import java.util.* ;
{
URL url = new URL ( "http://www.kicit.com" ) ;
URLConnection urlConnection = url.openConnection( ) ;
InputStream is = urlConnection.getInputStream( ) ;
int c ;
while ( ( c = is.read( ) ) != -1 )
System.out.print ( ( char ) c ) ;
is.close( ) ;
}
}
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"
/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"
/>
Rather than using the raw Socket class to create socket and then send a
GET request, we have used the more specialized class
HttpURLConnection. Using this class we can conveniently connect to a
site on the HTTP web server, make a request, and access the response
headers and the response message.
Two-Way Communication
So far we have written only client programs which communicated with
already existing server programs. The sockets that we created in all
these client programs were stream sockets. Through these sockets the
client could establish a connection with the server and then carry out
communication. While the connection is in place, data flows between
the processes in continuous streams. Hence such sockets are known as
stream sockets. These sockets are said to provide a connection-oriented
service. The protocol used for transmission is Transmission Control
Protocol (TCP).
Chapter 22: Network & Internet Programming 435
There is one more type of socket. It is known as datagram socket. It is
used to transmit individual packets of information. The protocol used is
User Datagram Protocol (UDP). Datagram sockets offer a connectionless
service. Hence the packets sent using these sockets may arrive in any
sequence or may even be lost or duplicated.
UDP is appropriate for network applications that do not require the
error checking and reliability in packet transmission. Stream sockets and
the TCP protocol is more commonly used for majority of Java
networking applications.
Let us now try to create a single user chat application. This application
would have two programs a server and a client. Once created, they
would be able to carry out two-way communication between them.
Let us begin with the server first. Here is the program for it.
DataInputStream dis ;
dis = new DataInputStream ( comSock.getInputStream( ) ) ;
DataOutputStream dos ;
dos = new DataOutputStream ( comSock.getOutputStream( ) ) ;
In all the client programs in this chapter we used to create one socket
and then use it to connect to a specific server at a specific port. Once
connected, through the same socket we used to communicate with the
server. In the server program there is a major change. In this program
we have to create two sockets a listening socket and a communication
socket. Using the listening socket the server would wait for a connection
request from the client. Once this request is received, the
communication is carried out with the client using the communication
socket.
The listening socket is created using ServerSocket class. The constructor
of this class uses server's IP address and the port number passed to it to
create a socket. We have chosen the port number as 6001. There is
nothing special about this number. You are free to choose any other
suitable number.
Using the listening socket accept( ) function is called. This function is a
blocking function. This means that the control would not return from
this function unless a connection request comes from the client. As
soon as the client connection request arrives, the accept( ) function
accepts the connection request, creates a new socket object for
communication and returns it into comSock. Once this communication
socket is created, communication is carried out with the client using the
input and output streams associated with the socket.
Chapter 22: Network & Internet Programming 437
The idea behind using a different socket for communication is to let the
server wait for other clients' connection request in a multithreaded
server. This concept is demonstrated in the next section.
References to communication socket's input/output streams are
obtained by calling methods getOutputStream( ) and getInputStream( ).
Using these references DataOutputStream and DataInputStream
objects are created. These objects are then used to send or receive
individual messages by calling writeUTF( ) and readUTF( ) methods.
When server receives a message "quit" message the socket and the
associated streams are closed.
Now that the server program is ready, let us take a look at the client
program. This is shown below.
DataInputStream dis ;
dis = new DataInputStream ( comSock.getInputStream( ) ) ;
DataOutputStream dos ;
dos = new DataOutputStream ( comSock.getOutputStream( ) ) ;
while ( true )
{
System.out.print ( "Enter text: " ) ;
msgToSend = scanner.nextLine( ) ;
438 Let Us Java
dos.writeUTF ( msgToSend ) ;
if ( msgToSend.equalsIgnoreCase ( "quit" ) )
{
dis.close( ) ;
dos.close( ) ;
cliSocket.close( ) ;
break ;
}
msgRecd = dis.readUTF( ) ;
System.out.println ( "Server response: " + msgRecd ) ;
}
}
}
For sake of convenience we plan to run server and client on the same
machine. Hence while creating the client socket we are using the local
machine's IP address as the IP address of the server. The client program
makes a connection request to server at port number 6001.
Once the connection is established, it just sends a message to the
server. When the server responds to this message, the client collects it
and displays it on the screen.
(a) Server maintains a list of its active clients in a vector. When a new
client connects to the server, this vector would be updated.
(b) Communication between any pair of clients happens in a separate
thread.
(c) All messages sent by a client are prepended with the client id for
whom it is meant. So if client 2 wishes to communicate with client 7
then he should send messages in the following format:
client 7 # Hello, how are you doing?
client 7 # Can we meet sometime next week?
Chapter 22: Network & Internet Programming 439
package javamultiuserchatserver ;
import java.io.* ;
import java.net.* ;
import java.util.* ;
while ( true )
{
comSock = serSock.accept( ) ;
System.out.println ( "New client req recd: " + comSock ) ;
DataInputStream dis ;
dis = new DataInputStream ( comSock.getInputStream( ) ) ;
DataOutputStream dos ;
dos = new DataOutputStream (
comSock.getOutputStream( ) ) ;
System.out.println ( "Starting new clien thread..." ) ;
ClientThread t ;
t = new ClientThread ( comSock, "Client" + i, dis, dos ) ;
v.add ( t ) ;
t.start( ) ;
i++ ;
}
}
}
Note that when a new client request comes a new thread is launched for
by calling the start( ) method of Thread class. The actual communication
between two clients happens in the run( ) method. Since we don't call
the run( ) method explicitly, all variables that it needs are passed and
preserved in private variables through the constructor of ClientThread
class.
Let us now turn our attention to the client program. I would first present
the code.
package javamultiuserchatclient ;
import java.io.* ;
import java.net.* ;
import java.util.Scanner ;
InetAddress ip = InetAddress.getLocalHost( ) ;
Socket s = new Socket ( ip, 1234 ) ;
DataInputStream dis ;
dis = new DataInputStream ( s.getInputStream( ) ) ;
DataOutputStream dos ;
dos = new DataOutputStream ( s.getOutputStream( ) ) ;
SendThread ( DataOutputStream d )
{
dos = d ;
}
public void run( )
{
Scanner scn = new Scanner ( System.in ) ;
while ( true )
{
String msgToSend = scn.nextLine( ) ;
try
{
dos.writeUTF ( msgToSend ) ;
}
catch ( IOException e )
{
e.printStackTrace( ) ;
}
}
}
}
Chapter 22: Network & Internet Programming 443
class RecvThread extends Thread
{
private DataInputStream dis ;
RecvThread ( DataInputStream d )
{
dis = d ;
}
public void run( )
{
Scanner scn = new Scanner ( System.in ) ;
while ( true )
{
try
{
String msgRecd = dis.readUTF( ) ;
System.out.println ( msgRecd ) ;
}
catch ( IOException e )
{
e.printStackTrace( ) ;
}
}
}
}
The server program creates a datagram socket and calls the receive( )
function to receive a filename from the client. Once the filename is
received in a datagram packet the file by this name is created on the
server. Next the chunks received from client are written to this file. The
process ends when the string "END" is received from the client. Here is
the server progr
File f ;
f = new File ( fname ) ;
FileInputStream fis = new FileInputStream ( f ) ;
The client program first receives the filename as input and sends it to
the server. It then sends chunks of this file to the server until its end is
reached. On reaching the end of file, it sends a string "END" to server as
a signal to stop the communication.
(d) HTTP protocol is used for accessing web pages from a site.
(f) Every working site on the Internet has a corresponding entry in the
whois database.
(g) IP protocol is responsible for reliable delivery of packets, detecting
errors in transmission and flow control.
CLASSPATH Variable
Strictfp Modifier
Packages
Creating and Using a Package
Split Packages
Different Packages, Same Type
Nested Packages
Package FAQs
Packages and Access Mechanism
Bitwise Operators
KanNotes
Chapter 23: Miscellany 451
T he topics discussed in this chapter were either too large or far too
removed from the mainstream Java programming for inclusion in the
earlier chapters. These topics provide certain useful programming
features, and could prove to be of immense help in certain programming
strategies. These include CLASSPATH variable, strictfp modifier,
packages and bitwise operators. Let us understand them one by one.
CLASSPATH Variable
Our Java program may use types stored in other .class files. CLASSPATH
is a mechanism that helps Java runtime environment locate the other
.class files. It is an environment variable and it contains a list of
directories that contain third-party and user-defined types.
CLASSPATH variable is different than the PATH environment variable.
PATH is used by Operating System to locate executable files, whereas
CLASSPATH is used to locate the .class files. The default value of
CLASSPATH is . . This means by default the search for .class files is
carried out only in current directory or its sub-directories.
In Windows the CLASSPATH variable can be set permanently through
Control Panel or at command prompt. Given below is an example of
setting it at command prompt.
set CLASSPATH=%CLASSPATH%;.;C:\ProgramFiles\Java\mylib
Here %CLASSPATH% gives the existing value of CLASSPATH variable. We
have two directories in the list, . and C:\ProgramFiles\Java\mylib
separated by a semicolon (;). The .class files would be first searched in
current directory. If not, found they would be searched in mylib
directory.
If we wish to set the CLASSPATH value temporarily, we can do so as
shown in the following example:
strictfp Modifier
Floating-point calculations are platform-dependent. So, the same
floating-point operation may give different results when the same class
file is executed on different platforms. This happens because floating
452 Let Us Java
Packages
A reasonably big Java software would contain many classes,
interfaces, enumerations and annotations. Java helps you organize
them properly by storing related classes, interfaces, enumerations
and annotations in a logical container called Package. This
organization is helpful in three ways:
(a) Packages makes it easy to locate and use types (i.e., classes,
interfaces, enumerations and annotations).
(b) Two different packages may contain types with same names. So, if a
library package contains a type called class Student and we also
define a type called class Student, so long as they belong to two
different packages, we can use both. Thus, packages help avoid
naming conflict.
(a) A new folder is created for every new package. Moreover, package
name and folder name are always same.
(b) A .java file can contain only one public type. Its name is same as the
name of the .java file.
(a) Create a new project by name Client. This will create a file called
Client.java containing a package client, which contains a public
class, Client.
(b) Add a new package called sample. For this right-click Source
Packages. A menu will pop up. From this menu select New | Java
Package sample. This action will create a package sample.
Let us now examine the directory structure. The source code would
get created in the following files:
~\Client\src\client\Client.java
~\Client\src\sample\Sample.java
~\Client\build\classes\client\Client.class
~\Client\build\classes\sample\Sample.class
Split Packages
It is possible to split a package across multiple files. This means a
package can contain multiple public types stored in different files.
This suits software development teams as different developers can
develop different types and store them in different files. All these
types can belong to the same package. Following program
demonstrates split packages.
// File: Sample1.java
package sample ;
public class Sample1
{
public void show( )
{
System.out.println ( "Bye" ) ;
}
}
Chapter 23: Miscellany 455
// File: Sample2.java
package sample ;
public class Sample2
{
public void display( )
{
System.out.println ( "Hi" ) ;
}
}
// File: Client.java
package client ;
import sample.Sample1 ;
import sample.Sample2 ;
class Client
{
public static void main ( String args[ ] )
{
Sample1 s1 = new Sample1( ) ;
s1.show( ) ;
Sample2 s2 = new Sample2( ) ;
s2.display( ) ;
}
}
(a) Create a new project by name Client. This will create a file called
Client.java containing a package client, which contains a public
class, Client.
(b) Add a new package called sample. For this right-click Source
Packages. A menu will pop up. From this menu select New | Java
Package sample. This action will create a package sample.
(d) Again right-click sample package. A menu would pop up. From this
menu select New | Java class Sample2. This action will create a
public class Sample2 in a file Sample2.java in the package sample.
~\Client\src\client\Client.java
~\Client\src\sample\Sample1.java
~\Client\src\sample\Sample2.java
~\Client\build\classes\client\Client.class
~\Client\build\classes\sample\Sample1.class
~\Client\build\classes\sample\Sample2.class
Let us now look at client code that uses these Sample class from two
different packages.
~\Client\src\client\Client.java
~\Client\src\sample1\Sample.java
~\Client\src\sample2\Sample.java
~\Client\build\classes\client\Client.class
~\Client\build\classes\sample1\Sample.class
~\Client\build\classes\sample1\Sample.class
Note that while creating objects of Sample class from two different
packages, we should use the fully qualified name to help understand
which Sample class are we planning to use for object creation, as
shown below:
Nested Packages
It is also possible to create nested packages. This is especially helpful
while creating big libraries containing numerous types. For example, the
classes in Java library are organized in many nested packages like
java.io, java.awt, java.lang, etc. Note that all packages in Java API
begin with java or javax. Let us now create nested packages for user-
defined classes.
Let us now look at client code that uses the Sample class present in
sample package and the Trial class present in the nested package
sample.trial.
package client ;
import sample.Sample ;
import sample.trial.Trial ;
class Client
{
public static void main ( String args[ ] )
{
Sample s = new Sample( ) ;
s.show( ) ;
Chapter 23: Miscellany 459
Trial e = new Trial( ) ;
e.display( ) ;
}
}
~\Client\src\client\Client.java
~\Client\src\sample\Sample.java
~\Client\src\sample\trial\Trial.java
~\Client\build\classes\client\Client.class
~\Client\build\classes\sample\Sample.class
~\Client\build\classes\sample\trial\Trial.class
Package FAQs
Often there are questions in programmer s mind about packages and
import statements. I have compiled below these FAQs.
All types in the file belong to a package called default package. This
practice should however be discouraged.
None. It does not import all packages that begin with letter A. It
would result into compilation error.
No. * can be used to signify all types in a package, and not all
packages nested in a package.
package p1 ;
class Myclass
{
int num = 40 ;
void fun( )
{
}
}
Figure 23.1
Bitwise Operators
Bitwise operators permit us to work with individual bits of a byte. There
are many bitwise operators available in Java. These include:
~ - complement operator
<< - left shift
>> - right shift
>>> - unsigned right shift
& - and
462 Let Us Java
| - or
^ - xor
Note that except ~ all other bitwise operators are binary operators.
Remember the following tips while using bitwise operators:
(a) Any bit value ANDed with 0 is 0.
(b) Any bit value ORed with 1 is 1.
(c) 1 XORed with 1 is 0.
(d) << - As bits are shifted from left, zeros are pushed from right.
(e) >> - As bits are shifted from right, left-most bit is copied from left.
(f) >>> - As bits are shifted from right, zeros are pushed from left.
A new folder is created for every new package with the same name as
name of the package
A .java file can contain only one public type. Its name is same as the
name of the .java file
Bitwise Operations :
Set a bit to a value 0/1 Write operation
Check whether bit is 1 (on) or 0 (off) Read operation
Bitwise operators available in Java are ~, <<, >>, >>>, &, |, ^, <<=,
>>=, &=, |=, ^=.
You should never take a test when you are not prepared. You
should never give up an opportunity to get tested when you are
fully prepared and confident. This chapter would help you check
your strengths and weaknesses, once you are prepared and
confident...
465
466 Let Us Java
Periodic Test I
(Based on Chapters 1 to 6)
(4) Write Java statements to sum odd integers between 1 and 99, using
a for statement.
switch ( choice )
{
case 1 :
case 2 :
System.out.println ( "Right choice" ) ;
}
(8) Would the following program run? If yes, what would be the output
and if no, what would be the error? Assume that the value of choice
is 3?
switch ( choice )
{
case 1 - 5 :
System.out.println ( "Right choice" ) ;
default :
System.out.println ( "Wrong choice" ) ;
}
(9) Point out the error, if any, in the following code snippet:
int i = 5, j = 10 ;
boolean flag ;
468 Let Us Java
(1) According to a survey a popular social networking site has hit one
billion users in Jan 2019. If its user base grows at a rate of 8% per
month, write a program to show how many months will it take for
the site to grow its user base to 1.5 billion users? Print user base
figures at the beginning and end of each month.
(2) Write a program that receives a 4-digit number as input and prints
an equivalent encrypted number. The encryption should be done as
follows:
Replace each digit with ( digit + 7 ) mod 10
Interchange first digit with third digit
Interchange second digit with fourth digit
Also write the decryption logic to obtain the original number from
the encrypted number.
Periodic Tests 469
Periodic Test II
(Based on Chapters 7 to 8)
(1) A fresh set of local variables gets created every time a function is
called normally or recursively.
(2) A function can return only one value at a time.
(3) A function cannot be defined inside another function.
(4) Any function can be made a recursive function.
(5) It is possible to define a function that receives different number of
arguments in different function calls.
(1) Write a code snippet to print the name and ordinal value for each
element of the following enum.
enum maritalstatus { single, married, divorced } ;
(2) Illustrate with a code snippet the difference between a String and
StringBuilder class.
(3) What is the purpose of static block? When does it get invoked?
(4) Which of the following statements are true about an object?
472 Let Us Java
(1) The Java collection class that can be used for maintaining key-value
pairs is _______.
(2) Common algorithms like searching, sorting, etc. can be applied on
collection using functions present in ________ class.
(3) A thread can be created in a Java program by extending the
_______ class or implementing the _______ interface.
(4) If two methods running in two different threads wish to access the
same resource, then to ensure at a time only one thread accesses
the resource the methods should be marked as _______.
(5) All Swing classes are defined in _______ package.
(1) Generic functions cannot work for primitives like int, float, char, etc.
(2) A generic function can receive multiple argument types.
(3) Bounded generics can work only with the objects of specified class.
(4) Protected members are inaccessible in the inheritance chain.
(5) In Swing API there is one adapter class for each listener.
Class.forName ( jdbcDriver ) ;
conn = DriverManager.getConnection ( dbURL, "root", "admin" ) ;
stmt = conn.createStatement( ) ;
stmt.executeUpdate ( sql ) ;
(4) What are CRUD operations?
(5) What are bounded generics? When are they used?
477
478 Let Us Java
for, 86
E loop, 86, 87, 88
multiple initializations, 89
Event, 394 nesting, 88
ActionEvent, 396, 396 partial, 88
MouseEvent, 395, 399, 400 formal arguments, 119, 121
actionPerformed, 392, 395, 396 format( ), 51
Event handling, 394 format specifiers, 52
else, 61 function overloading, 131
else if clause, 66 functions, 113
enum, 221 functions
enumerations, 221 called function, 114
use of, 222 calling function, 114, 118
exceptions passing values between, 118
user-defined, 287 function overloading, 131
exception handling, 276 readLine( ) function, 50
execution, 28 println( ) function, 51
executeUpdate( ), 412
executeQuery( ), 412, 414
exists( ), 312, 316
explicit conversion, 47
G
exponential form, 37
garbage collector, 166, 167
generic classes, 361
generic functions, 353
F generics, 353
bounded, 366
Float, 39 getAbsolutePath( ), 302
File, 301, 303, 305 getCanonicalPath( ), 216, 217
FileInputStream, 308, 314 getConnection( ) , 413, 414
File Operations, 301 getFloat( ), 412, 414
FileOutputStream, 311, 313, 315 getInt( ), 412, 414
FilterOutputStream, 326 getName( ), 302, 304, 314
FileReader, 309, 312 getParent( ), 302
FileWriter, 309, 310, 311 getString( ), 412, 414
false, 37 getText( ), 392, 398, 399
file decryption, 318, 321
file encryption, 318, 321
file transfer, 446
fill( ), 195
H
final, 50, 240
finalize method, 166, 167 HTTP, 433
finally block, 285, 286, 312 HashMap, 382
float, 36 HashMap class, 382
hierarchy, 48
Index 481
isEmpty( ), 216
I isSelected( ), 398, 399
I/O System
Expectations, 301
IDE, 11
J
INSERT, 412
Input/Output, 301 J2SE, 14
InputStreamReader, 302. 309 JButton, 393, 394
Integer, 35, 39 JDBC, 405, 407
Integrated Development Architecture, 407
Environment, 11 adding library, 413
Interfaces, driver, 408
ActionListener, 396 driver manager, 408
MouseListener, 400 driver type, 408
practical uses of, 260 JLabel, 393
Interfaces - different JPanel, 393, 394
implementations, 264 JTextField, 393, 394
Interfaces - focused view, 261 James Gosling, 5
Interfaces - unrelated inheritance, Java, 5, 6
266 bytcode, 7, 8, 10
identifier, 21 data types, 19, 35
if-else statement, 61 instructions, 41
nested if-elses, 65 keywords, 23
incremental development, 241 library, 14
indexOf( ), 214, 215 streams, 306
inheritance, 231 JavaFX library, 391
constructors, 237 JDK, 10, 11
uses, 234 JIT compiler, 10
insert( ), 217 JRE, 10, 11
instruction types, 41 JVM, 7, 8, 9
instructions, 41 jagged array, 200, 201
instructions java.sql package, 408
arithmetic instruction, 41, 43 joining and splitting strings, 216
control instruction, 41, 53
exception handling, 41
type declaration instruction, 41 K
int, 35
integer, 35 keywords, 23
integer constant, 35
interface,
practical uses, 260 L
inter-thread communication, 346
isDirectory( ), 304
482 Let Us Java
U
UDP, 444
UPDATE, 406, 407, 410, 412
user-defined exceptions, 287
user-defined streams, 315
V
value type, 19
variable names,
rules for constructing, 22
variables, 20, 22, 23