This is featured post 1 title
Replace these every slider sentences with your featured post descriptions.Go to Blogger edit html and find these sentences.Now replace these with your own descriptions.This theme is Bloggerized by Lasantha - Premiumbloggertemplates.com.

This is featured post 2 title
Replace these every slider sentences with your featured post descriptions.Go to Blogger edit html and find these sentences.Now replace these with your own descriptions.This theme is Bloggerized by Lasantha - Premiumbloggertemplates.com.

This is featured post 3 title
Replace these every slider sentences with your featured post descriptions.Go to Blogger edit html and find these sentences.Now replace these with your own descriptions.This theme is Bloggerized by Lasantha - Premiumbloggertemplates.com.

Showing posts with label learn java language online. Show all posts
Showing posts with label learn java language online. Show all posts
Friday, 1 July 2011
lesson 9-Streams and Exceptions
9. Streams and
Exceptions
To be able to read and
write files
To understand the
concepts of text and
binary streams
To learn how to throw
and catch exceptions
To be able to process
the command line
To be able to
read and write
objects using
serialization
Streams, Readers, and
Writers
There are two
different ways to store
data: text or binary
formats.
In text format, data
items are represented
in human-readable
form, as a sequence of
characters. For
example, the interger
12345 is stored as the
sequence of five
characters:
‘1’ ‘2’ ‘3’ ‘4’ ‘5’
In binary form, data
items are represented
in bytes. For example,
the integer 12345 is
stored as a sequence
of four bytes:
0 0 48 57 (because
12345 = 48*256 + 57)
We use the Reader and
Writer classes and
their subclasses to
process text files:
FileReader reader =
new FileReader
(“input.txt”);
int next = reader.read()
; // read() will return int
char c;
if (next != -1) // returns
-1 if the end of input
c = (char) next;
FileWriter writer =
new FileWriter
(“output.txt”);
Text input and output
are more convenient
for humans.
To read text data from
a disk file, we create a
FileReader object:
FileReader reader =
new FileReader
(“input.txt”);
To write text data to a
disk file, we use
FileWriter:
FileWriter writer =
new FileWriter
(“Output.txt”);
The Reader class has a
method read to read a
single character at a
time. However, the
read method returns an
int, thus we have to
cast it to a char.
Reader reader = …;
int next = reader.read()
;
char c;
if (next != -1)
c = (char)next;
We use the
InputStream and
OutputStream classes
and their subclasses to
process binary files:
FileInputStream in = new
FileInputStream
(“input.dat”);
int next = in.read();
byte b;
if (next != -1) // returns -1 if
the end of input
b = (byte) next;
FileOutputStream
output = new
FileOutputStream
(“output.dat”);
Binary storage is more
compact and more
efficient.
To read binary data
from a disk file, we
create a
FileInputStream
object:
FileInputStream
inputStream = new
FileInputStream
(“input.dat”);
To write binary data to
a disk file, we use
FileOutputStream:
FileOutputStream
outputStream = new
FileOutputStream
(“output.dat”);
The InputStream class
has a method read to
read a single byte at a
time. The method also
returns an int, thus we
have to cast it to a
byte.
InputStream in = …;
int next = in.read();
byte b;
if (next != -1)
b = (byte)next;
The Writer and
FileOutputStream
classes have a write
method to write a
single character or
byte.
Reading and Writing
Text Files
We construct a
FileWriter object from
the file name:
FileWriter writer =
new FileWriter
(“output.txt”);
We then send a
character at a time to
the file by calling the
write method.
However, the output
we have is in the form
of numbers and strings
so we need another
class whose task is to
break up numbers and
strings into individual
characters and send
them to a writer.
This class is called
PrintWriter.
PrintWriter out = new
PrintWriter(writer);
Next, we can use the
print and println
methods to print
numbers, objects, and
strings:
out.print(29.95);
out.println(new
Rectangle(5, 10, 15, 25)
);
out.println(“Hello.”);
Reading text files is
less convenient, we
have to use the
BufferedReader class,
which has a readLine
method .
After reading a line of
input, we can convert
the strings to integer
or double as needed.
Reading and Writing
Text Files
To write:
FileWriter writer =
new FileWriter
(“output.txt”);
PrintWriter out = new
PrintWriter(writer);
out.print(29.95);
out.println(new
Rectangle(5, 10, 15, 25)
);
out.println(“Hello,
World!”);
To read:
FileReader reader =
new FileReader
(“input.txt”);
BufferedReader in =
new BufferedReader
(reader);
String inputLine =
in.readLine();
double x =
Double.parseDouble
(inputLine);
When we are done
reading/writing from a
file, we should close
the files:
reader.close();
writer.close();
Before we can put
these input/output
classes to work, we
need to know about
exception handling.
All classes for reading
and writing are defined
in the java.io package.
Exception Handling
An exception is an
indication that a
problem occurred
during the program’s
execution.
When a method
detects a problematic
situation, what should
it do?
The methods should
return an indicator
whether it succeeded
or failed.
The exception-
handling mechanism
has been designed to
solve these problems.
When we detect an
error, we only need to
throw an appropriate
exception object.
The programmer
encloses in a try block
the code that may
generate an exception.
The try block is
followed by zero or
more catch blocks.
Each catch block
specifies the type of
exception it can catch
and contains an
exception handler.
An optional finally
block provides code
that always executes
regardless of whether
or not an exception
occurs.
If there is no catch
blocks following a try
block, the finally block
is required.
When an exception is
thrown, program
control leaves the try
block and the catch
blocks are searched.
If the type of the
thrown exception
matches the
parameter type in one
of the catch blocks,
the code for that catch
block is executed.
If a finally block
appears after the last
catch block, it is
executed regardless
of whether or not an
exception is thrown.
An exception can be
thrown from
statements in the
method, or from a
method called directly
or indirectly from the
try block.
The point at which the
throw is executed is
called the throw point.
The throw statement
is executed to indicate
that an exception has
occurred (i.e., a
method could not
complete
successfully).
This is called throwing
an exception.
Throwing an exception
is simple, the Java
library provides many
classes to signal all
sorts of exceptional
conditions.
For example, to signal
an unexpected end of
file, you can use the
EOFException, a
subclass of the
IOException class,
which denotes input/
output errors.
We make an object of
the exception class,
and then throw it.
For example, suppose
we write a method
that reads a product in
from a reader:
public class Product
{ private String name;
private double price;
private int score;
public void read
(BufferedReader in)
{ // read product name
name = in.readLine();
// read product price
String inputLine =
in.readLine();
price =
Double.parseDouble
(inputLine);
// read product score
inputLine = in.readLine
();
score =
Integer.parseInt
(inputLine);
}
}
What happens if we
reach the end of input?
String inputLine =
in.readLine();
if (inputline == null)
// do something
We can throw an
exception as follows:
String inputLine =
in.readLine();
if (inputline == null)
{ EOFException
exception = new
EOFException(“End of
file when reading
price.”);
throw exception;
}
Or we can just throw
the object that the
new operator returns:
String inputLine =
in.readLine();
if (inputline == null)
throw new
EOFException(“End of
file when reading
price.”);
Throwing a new
exception like an
example above is used
when you want to
generate an exception
under your control
rather than letting Java
generate it for you.
Throwing Exceptions
Throwing Exceptions
throw
exceptionObject;
throw new
IllegalArgumentExcept
ion();
...
String input =
in.readLine();
if (input == null)
throw new
EOFException(“EOF
when reading”);
When we throw an
exception, the method
exits immediately.
Execution does not
continue with the
method’s caller but
with an exception
handler.
Catching Exceptions
When an exception is
thrown, an exception
handler needs to catch
it.
We must install
exception handler for
all exceptions that our
program might throw.
We install an
exception handler with
the try statement.
Each try block contains
one or more method
calls that may cause an
exception, and catch
clauses for all
exception types.
Catching Exceptions
try
{ statement
}
catch(ExceptionClass
exceptionObject)
{ statement
}
catch(ExceptionClass
exceptionObject)
{ statement
}
finally
{ ….
}
if use "try block" you
must have whichever
"catch" or "finally"
If a try block has a
corresponding finally
block, the finally block
will be executed even
if the try block is
exited with return,
break or continue; then
the effect of the
return, break or
continue will occur.
When the catch
(IOException e) block
is executed, then
some method in the
try block has failed
with an IOException,
and that exception
object is stored in the
variable e.
try
{ BufferedReader in =
new BufferedReader(
new
InputStreamReader
(System.in));
System.out.println
(“How old are you?”);
String input =
in.readLine();
int age =
Integer.parseInt(input)
;
age++;
System.out.println
(“Next year, you’ll be ”
+ age);
}
catch(IOException e)
{ System.out.println
(“Input/Output error ” +
e);
}
catch(
NumberFormatExcepti
on e)
{ System.out.println
(“Input was not a
number”);
}
The try block contains
six statements with
two potential
exceptions that can be
thrown in this code.
The readLine method
can throw an
IOException, and
Integer.parseInt can
throw a
NumberFormatExcepti
on.
If either of these
exceptions is thrown,
the rest of the
instructions in the try
block are skipped.
Finally
Suppose a method
open a file, calls one
or more methods, and
then close the file:
BufferedReader in;
in = new
BufferedReader(new
FileReader(“input.txt”))
;
readProducts(in);
in.close();
Next, suppose that one
of the methods throws
an exception, then the
call to close is never
executed.
We solve this problem
by placing the call to
close inside a finally
clause:
BufferedReader in =
null;
try
{ in = new
BufferedReader(
new FileReader
(“input.txt”));
readProducts(in);
}
// optional catch
clauses here
finally
{ if (in != null)
in.close();
}
Checked Exception
Exceptions fall into
two categories, called
checked and
unchecked exceptions.
When you call a
method that throws a
checked exception,
you must tell the
compiler what we are
going to do about the
exception if it is
thrown.
We do not need to
keep track of
unchecked exceptions.
Exceptions that belong
to subclasses of
RuntimeException are
unchecked.
Checked Exceptions:
For example: All
subclasses of
IOException;
InterruptedException;
invent yourself by
subclassing Exception.
Unchecked Exceptions:
For example:
RuntimeException and
all subclasses;
ArithmeticException;
IllegalArgumentExcept
ion;
NumberFormatExcetpi
on;
IndexOutOfBoundsExce
ption;
NullPointerException;
invent yourself by
subclassing
RuntimeException
Checked exceptions
must be listed in
method's throws
clause, if we do not
provide catch clause.
Exception Class
Hierarchy
Suppose we want to
throw an IOException
in a method that call
BufferedReader.
readLine.
The IOException is a
checked exception, so
we need to tell the
compiler what we are
going to do about it.
We have two choices:
We can place the call
to the readLine inside a
try block and supply a
catch clause.
We can simply tell the
compiler by tagging
the method with a
throws clause.
Throws Clauses
If we want the
exception to go
uncaught and rely on
the propagation
mechanisms to catch
the exception
elsewhere, we use a
throws clause in the
method header.
Use a throws clause
only if a method will
throw one of the
checked exceptions.
public class Product
{ public void read
(BufferedReader in)
throws IOException
{ ... }
}
The throws clause
signals the caller that
it may encounter an
IOException.
If a method can throw
multiple checked
exceptions, we
separate them by
commas:
public void read
(BufferedReader in)
throws IOException,
FileNotFoundException
Error do not need to be
listed, nor do
RuntimeException
Examples: an out-of-
bounds array subscript,
arithmetic overflow,
division by zero,
invalid method
parameters, memory
exhaustion.
Example
In the next example,
we pass along all
exceptions of type
IOException that
readLine may throw.
When we encounter an
unexpected end of file,
we report it as an
EOFException.
For an expected end of
file, if the end of file
has been reached
before the start of a
record, the method
simply returns false.
If the file ends in the
middle of a record (an
unexpected end of file)
, then we throw an
exception.
public class Product
{ public boolean read
(BufferedReader in)
throws IOException
{ // read product name
name = in.readLine();
if (name == null) return
false;
String inputLine =
in.readLine();
if (inputLine == null)
throw new
EOFException (“EOF
when reading price.”);
price =
Double.parseDouble
(inputLine);
inputLIne = in.readLine
();
if (inputLine == null)
throw new
EOFException (“EOF
when reading score.”);
score =
Integer.parseInt
(inputLine);
return;
}
private String name;
private double price;
private int score;
}
Example
The following method
reads product records
and puts them into a
panel.
It is unconcerned with
any exceptions.
If there is a problem
with the input file, it
simply passes the
exception to its caller.
public void
readProducts
(BufferedReader in)
throws IOException
{ boolean done = false;
while (!done)
{
Product p = new
Product();
if (p.read(in))
panel.addProduct(p);
else
done = true;
}
}
Next, we implement
the user interaction.
We ask the user to
enter a file name and
read the product file.
If there is a problem,
we report the problem.
The program is not
terminated, and the
user has a chance to
open a different file.
public void openFile()
{ BufferedReader in =
null;
try
{ // select file name
. . .
in = new
BufferedReader(new
FileReader(selectFile))
;
readProducts(in);
}
catch(
FileNotFoundException
e)
{ JOptionPane.
showMessageDialog
(null, “Bad filename.
Try again.”);
}
catch(IOException e)
{ JOptionPane.
showMessageDialog
(null, “Corrupted file.
Try again.”);
}
finally
{ if (in != null)
try
{ in.close();
}
catch(IOException e)
{ JOptionPane.
showMessageDialog
(null, “Error closing
file.”);
}
}
}
Example
import java.io.*;
public class StringEx2 {
public static void main
(String args[]) throws
IOException {
FileWriter writer = null;
try {
String input2;
InputStreamReader
reader = new
InputStreamReader
(System.in);
BufferedReader
console = new
BufferedReader
(reader);
System.out.print("Enter
filename ");
input2 =
console.readLine();
writer = new
FileWriter(input2);
PrintWriter out = new
PrintWriter(writer);
double gpa;
System.out.print("Enter
Your GPA ");
input2 =
console.readLine();
gpa =
Double.parseDouble
(input2);
System.out.println
("Your GPA is " + gpa);
out.println(gpa);
}
catch(
NumberFormatExcepti
on e)
{ System.out.println
("Input was not a
number.");
}
catch(
FileNotFoundException
e)
{ System.out.println
("Bad filename. Try
again.");
}
finally {
writer.close();
}
}
}
Example: Reading Input
Data in a Graphical
Program
Recall the BestProduct
program that reads
product data from the
keyboard and then
marks the best buys.
In this example, we
will use a text editor
to write the data
values into a file and
then specify the name
of the file when the
data are to be used.
This program will
display its result in a
graph.
Prices grow in the x-
direction, performance
grows in the y-
direction.
// PlotProducts.java
/**
Reads products and
adds them to the
product panel.
@param in the buffered
reader to read from
*/
public void
readProducts
(BufferedReader in)
throws IOException
{ ...
boolean done = false;
while (!done)
{ Product p = new
Product();
if (p.read(in))
panel.addProduct(p);
else // last product
read
done = true;
}
}
// PlotProducts.java
/**
Handles the open file
menu. Prompts user
for file
name and reads
products from file.
*/
public void openFile()
{ BufferedReader in =
null;
try
{ // show file chooser
dialog
JFileChooser chooser
= new JFileChooser();
if (chooser.
showOpenDialog(null)
==
JFileChooser.APPROVE_
OPTION)
{ // construct reader
and read products
File selectedFile =
chooser.
getSelectedFile();
in = new
BufferedReader(new
FileReader
(selectedFile));
readProducts(in);
}
}
catch(
FileNotFoundException
e)
{ JOptionPane.
showMessageDialog
(null, "Bad filename.
Try again.");
}
catch(IOException e)
{ JOptionPane.
showMessageDialog
(null, "Corrupted file.
Try again.");
}
finally
{ if (in != null)
try
{ in.close();
}
catch(IOException e)
{ JOptionPane.
showMessageDialog
(null, "Error closing
file.");
}
}
}
Command Line
Arguments
There are several
methods of starting a
program:
By selecting “Run” in
the compilation
environment
By clicking on an icon
By typing the name of
the program at a
prompt in a terminal or
shell window
The latter method is
called “invoking the
program from the
command line”.
For the latter method,
we can also type in
additional information
that the program can
use.
These additional
strings are called
command line
arguments.
For example, if we
start a program with
the command line
java Prog –v input.txt
then the program
receives two
command line
arguments: the strings
“-v” and “input.txt”.
Java usually interprets
strings with a – as
options and other
strings as file names.
Command line
arguments are placed
in the args parameter
of the main method:
public static void main
(String[] args)
{ …
}
Thus args contains
two strings
args[0] = “-v”
args[1] = “input.txt”
Encryption
Encryption is a method
used for scrambling a
file so that it is
unreadable except to
those who know the
decryption method and
the secret keyword.
The person performing
any encryption
chooses an encryption
key; here we use a
number between 1 and
25 as the key.
This key indicates the
shift to be used in
encrypting each letter.
For example, if the key
is 3, replace A with a
D, B with an E, and so
on.
Encryption
To encrypt/decrpt data
java Crypt input.txt
encrypt.txt
java Crypt -d -k11
encrypt.txt output.txt
The program takes to
following command
line arguments:
- An optional -d flag to
indicate decryption
- An optional
encryption key, -k flag
- input file name
- output file name
If no key is specified,
then 3 is used.
import java.io.*;
public class Crypt
{ public static void
main(String[] args)
{ boolean decrypt =
false;
int key = DEFAULT_KEY;
FileReader infile = null;
FileWriter outfile =
null;
if (args.length < 2 ||
args.length > 4) usage
();
// gather command line
arguments and open
files
try
{ for (int i = 0; i <
args.length; i++)
{ if (args[i].substring(0,
1).equals("-"))
// it is a command line
option
{ String option = args[i]
.substring(1, 2);
if (option.equals("d"))
decrypt = true;
else if (option.equals
("k"))
{ key =
Integer.parseInt
(args[i].substring(2));
if (key < 1 || key >=
NLETTERS)
usage();
}
}
else
{ if (infile == null)
infile = new FileReader
(args[i]);
else if (outfile == null)
outfile = new
FileWriter(args[i]);
}
}
}
catch(IOException e)
{ System.out.println
("Error opening file");
System.exit(0);
}
if(infile == null || outfile
== null) usage();
// encrypt or decrypt
the input
if (decrypt) key =
NLETTERS - key;
try
{ encryptFile(infile,
outfile, key);
infile.close();
outfile.close();
}
catch(IOException e)
{ System.out.println
("Error processing
file");
System.exit(0);
}
}
public static void
usage()
{ System.out.println
("Usage: java Crypt [-
d] [-kn] infile outfile");
System.exit(1);
}
public static char
encrypt(char c, int k)
{ if ('a' <= c && c <= 'z')
return (char)('a' + (c - 'a'
+ k) % NLETTERS);
if ('A' <= c && c <= 'Z')
return (char)('A' + (c -
'A' + k) % NLETTERS);
return c;
}
public static void
encryptFile(FileReader
in, FileWriter out, int k)
throws IOException
{ while (true)
{ int next = in.read();
if (next == -1) return; //
end of file
char c = (char)next;
out.write(encrypt(c, k))
;
}
}
public static final int
DEFAULT_KEY = 3;
public static final int
NLETTERS = 'z' - 'a' + 1;
}
Random Access
Suppose we want to
change the prices of
some products that we
stored in a file, the
simplest technique is:
read data into an array
Update data
Save data back to a file
What happens if the
data is very large?
There will be a lot of
much reading and
writing.
Up til now, we read to
a file and write to a file
one item at a time, this
access pattern is
called sequential
access.
Next, we will learn
how to access specific
locations in a file and
change just those
locations. This access
pattern is called
random access.
Only disk files support
random access. Each
disk file has a special
file pointer position.
Normally, the file
pointer is at the end of
the file, and any output
is appended to the end.
If we move the file
pointer to the middle
of the file, the output
overwrite what is
already there.
Then the next read
command starts
reading at the file point
location.
In Java, we use a
RandomAccessFile
object a access a file
and move a file
pointer.
We can open a file
either for reading only
(“r”) or for reading and
writing (“rw”).
To open a random-
access file, we supply
a file name and a string
to specify the open
mode.
Random Access File
To open the file
product “product.dat”
for both reading and
writing, we write:
RandomAccessFile f =
new
RandomAccessFile
(“product.dat”, "rw");
To move file pointer
to byte n from the
beginning of the file:
f.seek(n);
To find out the current
position of the file
pointer (counted from
the beginning of the
file):
n = f.getFilePointer();
To find out the number
of bytes in a file:
long filelength =
f.length();
When the new data is
longer than the current
data, the update will
overwrite the old data.
For example, if the
price is increased by
50, the new price has
more digits so it
overwrites the
newline symbol that
separates the fields.
We must give each
field a fixed size that is
sufficiently large.
This also makes it
easy to access the nth
data in a file, without
having to read in the
first n-1 data.
When storing numbers
in a file with fixed
record sizes, it is
easier to store them in
binary format, not in
text format.
The readInt and
writeInt methods read
and write integers as
four-byte quantities.
The readDouble and
writeDouble methods
process double-
precision floating-point
numbers as eight-byte
quantities.
To read:
int n = f.readInt();
double x =
f.readDouble();
char c = f.readChar();
To write:
f.writeInt(n);
f.writeDouble(x);
f.writeChar(c);
Example: See
Database.java
- Name: 30 characters
at two bytes each (60
bytes)
- Price: one double (8
bytes)
- Score: one int (4
bytes)
import
java.io.IOException;
import java.io.
RandomAccessFile;
public class Database
{ public static void
main(String[] args)
{ ConsoleReader
console = new
ConsoleReader
(System.in);
System.out.println
("Please enter the data
file name:");
String filename =
console.readLine();
try
{ RandomAccessFile
file
= new
RandomAccessFile
(filename, "rw");
long nrecord =
file.length() / RECORD_
SIZE;
boolean done = false;
while (!done)
{ System.out.println
("Please enter the
record to update (1 - "
+ nrecord + "), new
record (0), quit (-1)");
int pos =
console.readInt();
if (1 <= pos && pos <=
nrecord) // update
record
{ file.seek((pos - 1) *
RECORD_SIZE);
Product p =
readProduct(file);
System.out.println
("Found " + p.getName
()
+ " " + p.getPrice() + " "
+ p.getScore());
System.out.println
("Enter the new
price:");
double newPrice =
console.readDouble();
p.setPrice(newPrice);
file.seek((pos - 1) *
RECORD_SIZE);
writeProduct(file, p);
}
else if (pos == 0) // add
record
{ System.out.println
("Enter new product:");
String name =
console.readLine();
System.out.println
("Enter price:");
double price =
console.readDouble();
System.out.println
("Enter score:");
int score =
console.readInt();
Product p = new
Product(name, price,
score);
file.seek(nrecord *
RECORD_SIZE);
writeProduct(file, p);
nrecord++;
}
else if (pos == -1)
done = true;
}
file.close();
}
catch(IOException e)
{ System.out.println
("Input/Output
Exception " + e); }
}
public static String
readFixedString
(RandomAccessFile f,
int size) throws
IOException
{ String b = "";
for (int i = 0; i < size; i+
+)
b += f.readChar();
return b.trim();
}
public static void
writeFixedString
(RandomAccessFile f,
String s, int size)
throws IOException
{ if (s.length() <= size)
{ f.writeChars(s);
for (int i = s.length(); i <
size; i++)
f.writeChar(' ');
}
else
f.writeChars
(s.substring(0, size));
}
public static Product
readProduct
(RandomAccessFile f)
throws IOException
{ String name =
readFixedString(f,
NAME_SIZE);
double price =
f.readDouble();
int score = f.readInt();
return new Product
(name, price, score);
}
public static void
writeProduct
(RandomAccessFile f,
Product p) throws
IOException
{ writeFixedString(f,
p.getName(), NAME_
SIZE);
f.writeDouble
(p.getPrice());
f.writeInt(p.getScore()
);
}
public static final int
NAME_SIZE = 30;
public static final int
CHAR_SIZE = 2;
public static final int
INT_SIZE = 4;
public static final int
DOUBLE_SIZE = 8;
public static final int
RECORD_SIZE
= CHAR_SIZE * NAME_
SIZE + DOUBLE_SIZE +
INT_SIZE;
}
Object Streams
We can read and write
complete objects with
the ObjectInputStream
and
ObjectOutputStream
Objects are saved in
binary format; hence
we use streams, not
writers.
For example, we write
a Product object to a
file as follows:
Product p = … ;
ObjectOutputStream out
= new
ObjectOutputStream
(new FileOutputStream
(“products.dat”));
out.writeObject(p);
To read objects:
ObjectInputStream in =
new ObjectInputStream
(new FileInputStream
(“products.dat”));
Product p = (Product)
in.readObject( );
We can store a whole
bunch of objects in an
array or vector, or
inside another object,
and then save that
object:
Vector v = new Vector
();
// now add many
Product objects into v
out.writeObject(v);
We can read all of
objects:
Vector v = (Vector)
in.readObject( );
The objects must
belong to classes that
implement Serializable
interface:
class Product
implements
Serializable
{ … }
To save data to disk, it
is best to put them all
into one object and
save that object.
class Catalog
implements
Serializable
{ public void
addProduct(Product p)
{… }
private Vector
products;
}
class ProductEditor
{ public void saveFile()
{ ObjectOutputStream
out = …;
out.writeObject
(productCatalog);
out.close();
}
public void loadFile()
{ ObjectInputStream in
= … ;
productCatalog =
(Catalog)in.readObject
();
in.close();
}
private Catalog
productCatalog;
}
Exceptions
To be able to read and
write files
To understand the
concepts of text and
binary streams
To learn how to throw
and catch exceptions
To be able to process
the command line
To be able to
read and write
objects using
serialization
Streams, Readers, and
Writers
There are two
different ways to store
data: text or binary
formats.
In text format, data
items are represented
in human-readable
form, as a sequence of
characters. For
example, the interger
12345 is stored as the
sequence of five
characters:
‘1’ ‘2’ ‘3’ ‘4’ ‘5’
In binary form, data
items are represented
in bytes. For example,
the integer 12345 is
stored as a sequence
of four bytes:
0 0 48 57 (because
12345 = 48*256 + 57)
We use the Reader and
Writer classes and
their subclasses to
process text files:
FileReader reader =
new FileReader
(“input.txt”);
int next = reader.read()
; // read() will return int
char c;
if (next != -1) // returns
-1 if the end of input
c = (char) next;
FileWriter writer =
new FileWriter
(“output.txt”);
Text input and output
are more convenient
for humans.
To read text data from
a disk file, we create a
FileReader object:
FileReader reader =
new FileReader
(“input.txt”);
To write text data to a
disk file, we use
FileWriter:
FileWriter writer =
new FileWriter
(“Output.txt”);
The Reader class has a
method read to read a
single character at a
time. However, the
read method returns an
int, thus we have to
cast it to a char.
Reader reader = …;
int next = reader.read()
;
char c;
if (next != -1)
c = (char)next;
We use the
InputStream and
OutputStream classes
and their subclasses to
process binary files:
FileInputStream in = new
FileInputStream
(“input.dat”);
int next = in.read();
byte b;
if (next != -1) // returns -1 if
the end of input
b = (byte) next;
FileOutputStream
output = new
FileOutputStream
(“output.dat”);
Binary storage is more
compact and more
efficient.
To read binary data
from a disk file, we
create a
FileInputStream
object:
FileInputStream
inputStream = new
FileInputStream
(“input.dat”);
To write binary data to
a disk file, we use
FileOutputStream:
FileOutputStream
outputStream = new
FileOutputStream
(“output.dat”);
The InputStream class
has a method read to
read a single byte at a
time. The method also
returns an int, thus we
have to cast it to a
byte.
InputStream in = …;
int next = in.read();
byte b;
if (next != -1)
b = (byte)next;
The Writer and
FileOutputStream
classes have a write
method to write a
single character or
byte.
Reading and Writing
Text Files
We construct a
FileWriter object from
the file name:
FileWriter writer =
new FileWriter
(“output.txt”);
We then send a
character at a time to
the file by calling the
write method.
However, the output
we have is in the form
of numbers and strings
so we need another
class whose task is to
break up numbers and
strings into individual
characters and send
them to a writer.
This class is called
PrintWriter.
PrintWriter out = new
PrintWriter(writer);
Next, we can use the
print and println
methods to print
numbers, objects, and
strings:
out.print(29.95);
out.println(new
Rectangle(5, 10, 15, 25)
);
out.println(“Hello.”);
Reading text files is
less convenient, we
have to use the
BufferedReader class,
which has a readLine
method .
After reading a line of
input, we can convert
the strings to integer
or double as needed.
Reading and Writing
Text Files
To write:
FileWriter writer =
new FileWriter
(“output.txt”);
PrintWriter out = new
PrintWriter(writer);
out.print(29.95);
out.println(new
Rectangle(5, 10, 15, 25)
);
out.println(“Hello,
World!”);
To read:
FileReader reader =
new FileReader
(“input.txt”);
BufferedReader in =
new BufferedReader
(reader);
String inputLine =
in.readLine();
double x =
Double.parseDouble
(inputLine);
When we are done
reading/writing from a
file, we should close
the files:
reader.close();
writer.close();
Before we can put
these input/output
classes to work, we
need to know about
exception handling.
All classes for reading
and writing are defined
in the java.io package.
Exception Handling
An exception is an
indication that a
problem occurred
during the program’s
execution.
When a method
detects a problematic
situation, what should
it do?
The methods should
return an indicator
whether it succeeded
or failed.
The exception-
handling mechanism
has been designed to
solve these problems.
When we detect an
error, we only need to
throw an appropriate
exception object.
The programmer
encloses in a try block
the code that may
generate an exception.
The try block is
followed by zero or
more catch blocks.
Each catch block
specifies the type of
exception it can catch
and contains an
exception handler.
An optional finally
block provides code
that always executes
regardless of whether
or not an exception
occurs.
If there is no catch
blocks following a try
block, the finally block
is required.
When an exception is
thrown, program
control leaves the try
block and the catch
blocks are searched.
If the type of the
thrown exception
matches the
parameter type in one
of the catch blocks,
the code for that catch
block is executed.
If a finally block
appears after the last
catch block, it is
executed regardless
of whether or not an
exception is thrown.
An exception can be
thrown from
statements in the
method, or from a
method called directly
or indirectly from the
try block.
The point at which the
throw is executed is
called the throw point.
The throw statement
is executed to indicate
that an exception has
occurred (i.e., a
method could not
complete
successfully).
This is called throwing
an exception.
Throwing an exception
is simple, the Java
library provides many
classes to signal all
sorts of exceptional
conditions.
For example, to signal
an unexpected end of
file, you can use the
EOFException, a
subclass of the
IOException class,
which denotes input/
output errors.
We make an object of
the exception class,
and then throw it.
For example, suppose
we write a method
that reads a product in
from a reader:
public class Product
{ private String name;
private double price;
private int score;
public void read
(BufferedReader in)
{ // read product name
name = in.readLine();
// read product price
String inputLine =
in.readLine();
price =
Double.parseDouble
(inputLine);
// read product score
inputLine = in.readLine
();
score =
Integer.parseInt
(inputLine);
}
}
What happens if we
reach the end of input?
String inputLine =
in.readLine();
if (inputline == null)
// do something
We can throw an
exception as follows:
String inputLine =
in.readLine();
if (inputline == null)
{ EOFException
exception = new
EOFException(“End of
file when reading
price.”);
throw exception;
}
Or we can just throw
the object that the
new operator returns:
String inputLine =
in.readLine();
if (inputline == null)
throw new
EOFException(“End of
file when reading
price.”);
Throwing a new
exception like an
example above is used
when you want to
generate an exception
under your control
rather than letting Java
generate it for you.
Throwing Exceptions
Throwing Exceptions
throw
exceptionObject;
throw new
IllegalArgumentExcept
ion();
...
String input =
in.readLine();
if (input == null)
throw new
EOFException(“EOF
when reading”);
When we throw an
exception, the method
exits immediately.
Execution does not
continue with the
method’s caller but
with an exception
handler.
Catching Exceptions
When an exception is
thrown, an exception
handler needs to catch
it.
We must install
exception handler for
all exceptions that our
program might throw.
We install an
exception handler with
the try statement.
Each try block contains
one or more method
calls that may cause an
exception, and catch
clauses for all
exception types.
Catching Exceptions
try
{ statement
}
catch(ExceptionClass
exceptionObject)
{ statement
}
catch(ExceptionClass
exceptionObject)
{ statement
}
finally
{ ….
}
if use "try block" you
must have whichever
"catch" or "finally"
If a try block has a
corresponding finally
block, the finally block
will be executed even
if the try block is
exited with return,
break or continue; then
the effect of the
return, break or
continue will occur.
When the catch
(IOException e) block
is executed, then
some method in the
try block has failed
with an IOException,
and that exception
object is stored in the
variable e.
try
{ BufferedReader in =
new BufferedReader(
new
InputStreamReader
(System.in));
System.out.println
(“How old are you?”);
String input =
in.readLine();
int age =
Integer.parseInt(input)
;
age++;
System.out.println
(“Next year, you’ll be ”
+ age);
}
catch(IOException e)
{ System.out.println
(“Input/Output error ” +
e);
}
catch(
NumberFormatExcepti
on e)
{ System.out.println
(“Input was not a
number”);
}
The try block contains
six statements with
two potential
exceptions that can be
thrown in this code.
The readLine method
can throw an
IOException, and
Integer.parseInt can
throw a
NumberFormatExcepti
on.
If either of these
exceptions is thrown,
the rest of the
instructions in the try
block are skipped.
Finally
Suppose a method
open a file, calls one
or more methods, and
then close the file:
BufferedReader in;
in = new
BufferedReader(new
FileReader(“input.txt”))
;
readProducts(in);
in.close();
Next, suppose that one
of the methods throws
an exception, then the
call to close is never
executed.
We solve this problem
by placing the call to
close inside a finally
clause:
BufferedReader in =
null;
try
{ in = new
BufferedReader(
new FileReader
(“input.txt”));
readProducts(in);
}
// optional catch
clauses here
finally
{ if (in != null)
in.close();
}
Checked Exception
Exceptions fall into
two categories, called
checked and
unchecked exceptions.
When you call a
method that throws a
checked exception,
you must tell the
compiler what we are
going to do about the
exception if it is
thrown.
We do not need to
keep track of
unchecked exceptions.
Exceptions that belong
to subclasses of
RuntimeException are
unchecked.
Checked Exceptions:
For example: All
subclasses of
IOException;
InterruptedException;
invent yourself by
subclassing Exception.
Unchecked Exceptions:
For example:
RuntimeException and
all subclasses;
ArithmeticException;
IllegalArgumentExcept
ion;
NumberFormatExcetpi
on;
IndexOutOfBoundsExce
ption;
NullPointerException;
invent yourself by
subclassing
RuntimeException
Checked exceptions
must be listed in
method's throws
clause, if we do not
provide catch clause.
Exception Class
Hierarchy
Suppose we want to
throw an IOException
in a method that call
BufferedReader.
readLine.
The IOException is a
checked exception, so
we need to tell the
compiler what we are
going to do about it.
We have two choices:
We can place the call
to the readLine inside a
try block and supply a
catch clause.
We can simply tell the
compiler by tagging
the method with a
throws clause.
Throws Clauses
If we want the
exception to go
uncaught and rely on
the propagation
mechanisms to catch
the exception
elsewhere, we use a
throws clause in the
method header.
Use a throws clause
only if a method will
throw one of the
checked exceptions.
public class Product
{ public void read
(BufferedReader in)
throws IOException
{ ... }
}
The throws clause
signals the caller that
it may encounter an
IOException.
If a method can throw
multiple checked
exceptions, we
separate them by
commas:
public void read
(BufferedReader in)
throws IOException,
FileNotFoundException
Error do not need to be
listed, nor do
RuntimeException
Examples: an out-of-
bounds array subscript,
arithmetic overflow,
division by zero,
invalid method
parameters, memory
exhaustion.
Example
In the next example,
we pass along all
exceptions of type
IOException that
readLine may throw.
When we encounter an
unexpected end of file,
we report it as an
EOFException.
For an expected end of
file, if the end of file
has been reached
before the start of a
record, the method
simply returns false.
If the file ends in the
middle of a record (an
unexpected end of file)
, then we throw an
exception.
public class Product
{ public boolean read
(BufferedReader in)
throws IOException
{ // read product name
name = in.readLine();
if (name == null) return
false;
String inputLine =
in.readLine();
if (inputLine == null)
throw new
EOFException (“EOF
when reading price.”);
price =
Double.parseDouble
(inputLine);
inputLIne = in.readLine
();
if (inputLine == null)
throw new
EOFException (“EOF
when reading score.”);
score =
Integer.parseInt
(inputLine);
return;
}
private String name;
private double price;
private int score;
}
Example
The following method
reads product records
and puts them into a
panel.
It is unconcerned with
any exceptions.
If there is a problem
with the input file, it
simply passes the
exception to its caller.
public void
readProducts
(BufferedReader in)
throws IOException
{ boolean done = false;
while (!done)
{
Product p = new
Product();
if (p.read(in))
panel.addProduct(p);
else
done = true;
}
}
Next, we implement
the user interaction.
We ask the user to
enter a file name and
read the product file.
If there is a problem,
we report the problem.
The program is not
terminated, and the
user has a chance to
open a different file.
public void openFile()
{ BufferedReader in =
null;
try
{ // select file name
. . .
in = new
BufferedReader(new
FileReader(selectFile))
;
readProducts(in);
}
catch(
FileNotFoundException
e)
{ JOptionPane.
showMessageDialog
(null, “Bad filename.
Try again.”);
}
catch(IOException e)
{ JOptionPane.
showMessageDialog
(null, “Corrupted file.
Try again.”);
}
finally
{ if (in != null)
try
{ in.close();
}
catch(IOException e)
{ JOptionPane.
showMessageDialog
(null, “Error closing
file.”);
}
}
}
Example
import java.io.*;
public class StringEx2 {
public static void main
(String args[]) throws
IOException {
FileWriter writer = null;
try {
String input2;
InputStreamReader
reader = new
InputStreamReader
(System.in);
BufferedReader
console = new
BufferedReader
(reader);
System.out.print("Enter
filename ");
input2 =
console.readLine();
writer = new
FileWriter(input2);
PrintWriter out = new
PrintWriter(writer);
double gpa;
System.out.print("Enter
Your GPA ");
input2 =
console.readLine();
gpa =
Double.parseDouble
(input2);
System.out.println
("Your GPA is " + gpa);
out.println(gpa);
}
catch(
NumberFormatExcepti
on e)
{ System.out.println
("Input was not a
number.");
}
catch(
FileNotFoundException
e)
{ System.out.println
("Bad filename. Try
again.");
}
finally {
writer.close();
}
}
}
Example: Reading Input
Data in a Graphical
Program
Recall the BestProduct
program that reads
product data from the
keyboard and then
marks the best buys.
In this example, we
will use a text editor
to write the data
values into a file and
then specify the name
of the file when the
data are to be used.
This program will
display its result in a
graph.
Prices grow in the x-
direction, performance
grows in the y-
direction.
// PlotProducts.java
/**
Reads products and
adds them to the
product panel.
@param in the buffered
reader to read from
*/
public void
readProducts
(BufferedReader in)
throws IOException
{ ...
boolean done = false;
while (!done)
{ Product p = new
Product();
if (p.read(in))
panel.addProduct(p);
else // last product
read
done = true;
}
}
// PlotProducts.java
/**
Handles the open file
menu. Prompts user
for file
name and reads
products from file.
*/
public void openFile()
{ BufferedReader in =
null;
try
{ // show file chooser
dialog
JFileChooser chooser
= new JFileChooser();
if (chooser.
showOpenDialog(null)
==
JFileChooser.APPROVE_
OPTION)
{ // construct reader
and read products
File selectedFile =
chooser.
getSelectedFile();
in = new
BufferedReader(new
FileReader
(selectedFile));
readProducts(in);
}
}
catch(
FileNotFoundException
e)
{ JOptionPane.
showMessageDialog
(null, "Bad filename.
Try again.");
}
catch(IOException e)
{ JOptionPane.
showMessageDialog
(null, "Corrupted file.
Try again.");
}
finally
{ if (in != null)
try
{ in.close();
}
catch(IOException e)
{ JOptionPane.
showMessageDialog
(null, "Error closing
file.");
}
}
}
Command Line
Arguments
There are several
methods of starting a
program:
By selecting “Run” in
the compilation
environment
By clicking on an icon
By typing the name of
the program at a
prompt in a terminal or
shell window
The latter method is
called “invoking the
program from the
command line”.
For the latter method,
we can also type in
additional information
that the program can
use.
These additional
strings are called
command line
arguments.
For example, if we
start a program with
the command line
java Prog –v input.txt
then the program
receives two
command line
arguments: the strings
“-v” and “input.txt”.
Java usually interprets
strings with a – as
options and other
strings as file names.
Command line
arguments are placed
in the args parameter
of the main method:
public static void main
(String[] args)
{ …
}
Thus args contains
two strings
args[0] = “-v”
args[1] = “input.txt”
Encryption
Encryption is a method
used for scrambling a
file so that it is
unreadable except to
those who know the
decryption method and
the secret keyword.
The person performing
any encryption
chooses an encryption
key; here we use a
number between 1 and
25 as the key.
This key indicates the
shift to be used in
encrypting each letter.
For example, if the key
is 3, replace A with a
D, B with an E, and so
on.
Encryption
To encrypt/decrpt data
java Crypt input.txt
encrypt.txt
java Crypt -d -k11
encrypt.txt output.txt
The program takes to
following command
line arguments:
- An optional -d flag to
indicate decryption
- An optional
encryption key, -k flag
- input file name
- output file name
If no key is specified,
then 3 is used.
import java.io.*;
public class Crypt
{ public static void
main(String[] args)
{ boolean decrypt =
false;
int key = DEFAULT_KEY;
FileReader infile = null;
FileWriter outfile =
null;
if (args.length < 2 ||
args.length > 4) usage
();
// gather command line
arguments and open
files
try
{ for (int i = 0; i <
args.length; i++)
{ if (args[i].substring(0,
1).equals("-"))
// it is a command line
option
{ String option = args[i]
.substring(1, 2);
if (option.equals("d"))
decrypt = true;
else if (option.equals
("k"))
{ key =
Integer.parseInt
(args[i].substring(2));
if (key < 1 || key >=
NLETTERS)
usage();
}
}
else
{ if (infile == null)
infile = new FileReader
(args[i]);
else if (outfile == null)
outfile = new
FileWriter(args[i]);
}
}
}
catch(IOException e)
{ System.out.println
("Error opening file");
System.exit(0);
}
if(infile == null || outfile
== null) usage();
// encrypt or decrypt
the input
if (decrypt) key =
NLETTERS - key;
try
{ encryptFile(infile,
outfile, key);
infile.close();
outfile.close();
}
catch(IOException e)
{ System.out.println
("Error processing
file");
System.exit(0);
}
}
public static void
usage()
{ System.out.println
("Usage: java Crypt [-
d] [-kn] infile outfile");
System.exit(1);
}
public static char
encrypt(char c, int k)
{ if ('a' <= c && c <= 'z')
return (char)('a' + (c - 'a'
+ k) % NLETTERS);
if ('A' <= c && c <= 'Z')
return (char)('A' + (c -
'A' + k) % NLETTERS);
return c;
}
public static void
encryptFile(FileReader
in, FileWriter out, int k)
throws IOException
{ while (true)
{ int next = in.read();
if (next == -1) return; //
end of file
char c = (char)next;
out.write(encrypt(c, k))
;
}
}
public static final int
DEFAULT_KEY = 3;
public static final int
NLETTERS = 'z' - 'a' + 1;
}
Random Access
Suppose we want to
change the prices of
some products that we
stored in a file, the
simplest technique is:
read data into an array
Update data
Save data back to a file
What happens if the
data is very large?
There will be a lot of
much reading and
writing.
Up til now, we read to
a file and write to a file
one item at a time, this
access pattern is
called sequential
access.
Next, we will learn
how to access specific
locations in a file and
change just those
locations. This access
pattern is called
random access.
Only disk files support
random access. Each
disk file has a special
file pointer position.
Normally, the file
pointer is at the end of
the file, and any output
is appended to the end.
If we move the file
pointer to the middle
of the file, the output
overwrite what is
already there.
Then the next read
command starts
reading at the file point
location.
In Java, we use a
RandomAccessFile
object a access a file
and move a file
pointer.
We can open a file
either for reading only
(“r”) or for reading and
writing (“rw”).
To open a random-
access file, we supply
a file name and a string
to specify the open
mode.
Random Access File
To open the file
product “product.dat”
for both reading and
writing, we write:
RandomAccessFile f =
new
RandomAccessFile
(“product.dat”, "rw");
To move file pointer
to byte n from the
beginning of the file:
f.seek(n);
To find out the current
position of the file
pointer (counted from
the beginning of the
file):
n = f.getFilePointer();
To find out the number
of bytes in a file:
long filelength =
f.length();
When the new data is
longer than the current
data, the update will
overwrite the old data.
For example, if the
price is increased by
50, the new price has
more digits so it
overwrites the
newline symbol that
separates the fields.
We must give each
field a fixed size that is
sufficiently large.
This also makes it
easy to access the nth
data in a file, without
having to read in the
first n-1 data.
When storing numbers
in a file with fixed
record sizes, it is
easier to store them in
binary format, not in
text format.
The readInt and
writeInt methods read
and write integers as
four-byte quantities.
The readDouble and
writeDouble methods
process double-
precision floating-point
numbers as eight-byte
quantities.
To read:
int n = f.readInt();
double x =
f.readDouble();
char c = f.readChar();
To write:
f.writeInt(n);
f.writeDouble(x);
f.writeChar(c);
Example: See
Database.java
- Name: 30 characters
at two bytes each (60
bytes)
- Price: one double (8
bytes)
- Score: one int (4
bytes)
import
java.io.IOException;
import java.io.
RandomAccessFile;
public class Database
{ public static void
main(String[] args)
{ ConsoleReader
console = new
ConsoleReader
(System.in);
System.out.println
("Please enter the data
file name:");
String filename =
console.readLine();
try
{ RandomAccessFile
file
= new
RandomAccessFile
(filename, "rw");
long nrecord =
file.length() / RECORD_
SIZE;
boolean done = false;
while (!done)
{ System.out.println
("Please enter the
record to update (1 - "
+ nrecord + "), new
record (0), quit (-1)");
int pos =
console.readInt();
if (1 <= pos && pos <=
nrecord) // update
record
{ file.seek((pos - 1) *
RECORD_SIZE);
Product p =
readProduct(file);
System.out.println
("Found " + p.getName
()
+ " " + p.getPrice() + " "
+ p.getScore());
System.out.println
("Enter the new
price:");
double newPrice =
console.readDouble();
p.setPrice(newPrice);
file.seek((pos - 1) *
RECORD_SIZE);
writeProduct(file, p);
}
else if (pos == 0) // add
record
{ System.out.println
("Enter new product:");
String name =
console.readLine();
System.out.println
("Enter price:");
double price =
console.readDouble();
System.out.println
("Enter score:");
int score =
console.readInt();
Product p = new
Product(name, price,
score);
file.seek(nrecord *
RECORD_SIZE);
writeProduct(file, p);
nrecord++;
}
else if (pos == -1)
done = true;
}
file.close();
}
catch(IOException e)
{ System.out.println
("Input/Output
Exception " + e); }
}
public static String
readFixedString
(RandomAccessFile f,
int size) throws
IOException
{ String b = "";
for (int i = 0; i < size; i+
+)
b += f.readChar();
return b.trim();
}
public static void
writeFixedString
(RandomAccessFile f,
String s, int size)
throws IOException
{ if (s.length() <= size)
{ f.writeChars(s);
for (int i = s.length(); i <
size; i++)
f.writeChar(' ');
}
else
f.writeChars
(s.substring(0, size));
}
public static Product
readProduct
(RandomAccessFile f)
throws IOException
{ String name =
readFixedString(f,
NAME_SIZE);
double price =
f.readDouble();
int score = f.readInt();
return new Product
(name, price, score);
}
public static void
writeProduct
(RandomAccessFile f,
Product p) throws
IOException
{ writeFixedString(f,
p.getName(), NAME_
SIZE);
f.writeDouble
(p.getPrice());
f.writeInt(p.getScore()
);
}
public static final int
NAME_SIZE = 30;
public static final int
CHAR_SIZE = 2;
public static final int
INT_SIZE = 4;
public static final int
DOUBLE_SIZE = 8;
public static final int
RECORD_SIZE
= CHAR_SIZE * NAME_
SIZE + DOUBLE_SIZE +
INT_SIZE;
}
Object Streams
We can read and write
complete objects with
the ObjectInputStream
and
ObjectOutputStream
Objects are saved in
binary format; hence
we use streams, not
writers.
For example, we write
a Product object to a
file as follows:
Product p = … ;
ObjectOutputStream out
= new
ObjectOutputStream
(new FileOutputStream
(“products.dat”));
out.writeObject(p);
To read objects:
ObjectInputStream in =
new ObjectInputStream
(new FileInputStream
(“products.dat”));
Product p = (Product)
in.readObject( );
We can store a whole
bunch of objects in an
array or vector, or
inside another object,
and then save that
object:
Vector v = new Vector
();
// now add many
Product objects into v
out.writeObject(v);
We can read all of
objects:
Vector v = (Vector)
in.readObject( );
The objects must
belong to classes that
implement Serializable
interface:
class Product
implements
Serializable
{ … }
To save data to disk, it
is best to put them all
into one object and
save that object.
class Catalog
implements
Serializable
{ public void
addProduct(Product p)
{… }
private Vector
products;
}
class ProductEditor
{ public void saveFile()
{ ObjectOutputStream
out = …;
out.writeObject
(productCatalog);
out.close();
}
public void loadFile()
{ ObjectInputStream in
= … ;
productCatalog =
(Catalog)in.readObject
();
in.close();
}
private Catalog
productCatalog;
}
Labels:
learn java language online
lesson 8
8. Inheritance and
Interfaces
To learn about
inheritances
To be able to convert
between supertype
and subtype
references
To understand how to
override superclass
methods
To understand the
concept of
polymorphism
To design and use
abstract classes and
interfaces
To learn about
packages and Java
access control
Introduction to
Inheritance
Inheritance is a
mechanism for
enhancing existing,
working class.
For example, suppose
we need to define a
class SavingsAccount
to model an account
that pays a fixed
interest rate on
deposits.
class SavingsAccount
extends BankAccount
{ new instance variables
new methods
}
class SubclassName
extends SuperclassName
{ new instance variables
new methods
}
Inheritance
The more general
class that forms the
basis for inheritance is
called the superclass.
The more specialized
class that inherits from
the superclass is
called the subclass.
One important reason
for inhertance is code
reuse.
Inheritance Diagram
Object <---
BankAccount <----
SavingsAccount
Inheritance Diagram
Which class is a
superclass and which
class is a subclass?
BankAccount is the
superclass and
SavingsAccount is the
subclass.
Suppose we want to
set an interest rate in
the constructor for
SavingsAccount class
and also a method to
apply that interest rate
periodically.
public class
SavingsAccount extends
BankAccount
{ private double
interestRate;
public SavingsAccont
(double rate)
{ constructor
implementation }
public void addInterest
( )
{ method
implementation }
}
public class
BankAccount
{ private double
balance;
public BankAccount( )
{ balance = 0; }
public BankAccount
(double init )
{ balance = init; }
public void deposit
(double amount)
{ balance = balance +
amount; }
public void withdraw
(double amount)
{ balance = balance -
amount; }
public double
getBalance( )
{ return balance; }
}
public class
SavingsAccount
extends BankAccount
{ private double
interestRate; //
instance variable
public SavingsAccont
(double rate) //
constructor
{ interestRate = rate; }
public void addInterest
( ) // instance method
{ double interest =
getBalance() *
interestRate / 100;
deposit(interest);
}
}
// Example Code
SavingsAccount
collegeFund = new
SavingsAccount(10);
collegeFund.deposit
(500); // OK to use
superclass’s method
collegeFund.
addInterest( );
Converting Between
Class Types
A SavingsAccount
object is a special
case of a BankAccount
object. So, you can
store a reference to a
SavingsAccount object
into a BankAccount
object.
SavingsAccount
collegeFund = new
SavingsAccount(10);
BankAccount anAccount =
collegeFund;
Object anObject =
collegeFund;
The three object
references stored in
collegeFund,
anAccount, and
anObject refer to the
same object of type
SavingsAccount.
However, the
anAccount knows less
than the full story
about the object to
which it refers
anAccount.deposit(1000)
; // OK
anAccount.addInterest( )
; // Not OK
Because anAccount is
an object of type
BankAccount, we
cannot use the
addInterest method.
anObject.deposit(1000); //
Not OK
The variable anObject
knows even less,
because it is an object
of type Object and
deposit is not a
method of the object
class.
Why would anyone
want to know less
about an object and
store a reference in an
object variable of a
superclass? Reuse.
Consider the transfer
method
void transfer(BankAccount
other, double amount)
{ withdraw(amount);
other.deposit(amount);
}
We can use this
method to transfer
money from one bank
account to another:
BankAccount
momsChecking = … ;
BankAccount
harrysChecking = … ;
momsChecking.transfer
(harrysChecking, 1000);
We can also use this
method to transfer
money into a
SavingAccount:
SavingsAccount
CollegeFund = … ;
momsChecking.
transfer(collegeFund,
1000);
However, we cannot
convert between
unrelated classes:
Rectangle r =
CollegeFund; // Error
Subclass reference can
be assigned to
superclass reference
Object anObject =
collegeFund;
But, superclass
reference must be
casted to subclass
reference.
SavingsAccount x =
(SavingsAcount)anObject;
as long as we
absolutely sure that
anObject really refers
to a SavingsAccount
object.
To play it safe, we can
use the instanceof
operator to test
whether an object
belongs to a particular
class (or one of its
subclasses).
Object instanceof
ClasssName
Rectangle r;
if (x instanceof
Rectangle)
r = (Rectangle) x;
SavingsAccount x;
if (anObject instanceof
SavingsAccount)
x = (SavingsAccount)
anObject;
else
x = null;
Hierarchies
Hierarchies are
frequently represented
as trees, with the
most general concepts
at the root.
In Java, it is common
to group classes in
inheritance
hierarchies.
Inheritance
Hierarchies
The classes
representing the most
general concepts are
near the root, more
specialized classes
towards the branches.
When designing a
hierarchy of classes,
those common
properties are
collected in a
superclass. More
specialized properties
can be found in
subclasses.
Consider a bank that
offers its customers
three kinds of
accounts:
CheckingAccount
SavingsAccount
TimeDepositAccount
The checking account
has no interest, gives
you a small number of
free transactions per
month, and charges a
transaction fee for
each additional
transactions.
The savings account
compounds interest
monthly.
The time deposit
account has a penalty
for early withdrawal.
The inheritance
hierarchy for these
account classes is:
protected Members
A superclass’s public
members are
accessible anywhere
the program has a
reference to that
superclass type or one
of its subclasses
types.
A superclass’s private
members are
accessible only in
methods of that
superclasses.
A superclass’s
protected members
serve as an
intermediate level of
protection between
public and private
access.
protected Members
A superclass’s
protected members
can be accessed by
methods of the
superclass, by
methods of subclasses
and by methods of
other classes in the
same package
(protected members
have package access).
Subclass methods can
normally refer to public
and protected
members of the
superclass simply by
using the member
names.
Inheritance
// Definition of a class
Point
public class Point {
protected int x, y;
public Point()
{ setPoint(0,0); }
public Point(int a, int b)
{ setPoint(a, b); }
public void setPoint(int
a, int b)
{ x = a;
y = b;
}
public int getX()
{ return x; }
public int getY()
{ return y; }
public String toString()
{ return “[“ + x + “, “ + y
+ “]”; } }
// Definition of class
circle
public class Circle
extends Point {
protected double
radius;
public Circle()
{ // implicit call to
superclass constructor
setRadius(0); }
public Circle(double r,
int a, int b)
{ super(a, b);
setRadius(r);
}
public void setRadius
(double r)
{ radius = (r >= 0.0 ? r :
0.0); }
pubic double getRadius
()
{ return radius; }
public double area()
{ return Math.PI *
radius * radius; }
public String toString()
{ return “Center = “ +
“[“ + x + “,” + y + “]” +
“; Radius = “ + radius;
}
}
// Main program
import java.text.
DecimalFormat;
import javax.swing.
JOptionPane;
public class
InheritanceTest {
public static void main
(String[] args)
{ Point pointRef, p;
Circle circleRef, c;
String output;
p = new Point(30,50);
c = new Circle(2.7, 120,
89);
output = “Point p: “+
p.toString() +
“\nCircle c: “ +
c.toString() ;
pointRef = c;
output += “\n\nCircle c
(via pointRef): “ +
pointRef.toString() ;
circleRef = (Circle)
pointRef;
output += “\n\nCircle c
(via circleRef): “ +
circleRef.toString() ;
DecimalFormat
precision2 = new
DecimalFormat(“0.00”)
;
output += “\n\nArea of
c (via circleRef): “ +
precision2.format
(circleRef.area());
if (p instanceof Circle)
{
circleRef = (Circle) p;
output += “\n\ncast
successful”;
}
else
output += “\n\n p does
not refer to a circle”;
JOptionPane.
showMessageDialog
(null, output,
“Inheritance”,
JOptionPane.
INFORMATION_
MESSAGE);
System.exit(0);
}
}
Inheriting Instance
Variables and Methods
When defining
additional methods for
a subclass, there are 3
options:
1. We can override
methods from the
superclass by
specifying a method
with the same
signature (i.e., the
same name and the
same parameters).
2. We can inherit
methods from the
superclass. If we do
not explicitly override
a superclass method,
we automatically
inherit it.
3. We can define new
methods that does not
exist in the superclass.
When defining
additional instance
variables for a
subclass, there are 2
options:
1. We can inherit
variables from the
superclass.
2. We can define new
variables. These new
variables are only
presented in subclass
objects.
Remember! We can
never override
instance variables.
Shadowing
The newly defined
subclass variable
shadows the
superclass variable.
The superclass
variable is still
present, but it cannot
be accessed.
When you refer to
balance in a
SavingAccount
method, you access
the new instance
variable.
Shadowing instance
variables is harmless.
Shadowing Instance
Variables
A subclass has no
access to the private
instance of the
superclass
public class
CheckingAccount
extends BankAccount
{ public void deposit
(double amount)
{ transactionCount++;
balance = balance +
amount; // ERROR
}
}
To solve the problem
public class
CheckingAccount
extends BankAccount
{ private double
balance; // shadow
variable
public void deposit
(double amount)
{ transactionCount++;
balance = balance +
amount; // OK but
different balance
}
}
Example
public class
BankAccount
{ private double
balance;
public double
getBalance() {…}
public void deposit
(double amount) {…}
public void withdraw
(double amount) {…}
}
public class
CheckingAccount
extends BankAccount
{ private int
transactionCount;
public void deposit
(double amount) {…}
public void withdraw
(double amount) {…}
public void deductFees
() {…}
}
Subclass methods
have no access rights
to the private data of
the superclass.
If we want to modify a
private superclass
variable, we must use
a public method of the
superclass.
Can we write the
deposit method as
public void deposit
(double amount)
{ transactionCount++;
deposit(amount); // Is
it OK?
}
If we define the
method like that, it will
call itself infinitely.
We need to be more
specific that we want
to invoke the
superclass’s deposit
method by using a
special keyword super
for this purpose:
public void deposit
(double amount)
{ transactionCount++;
super.deposit(amount);
}
Calling a Superclass
Methods
public class
CheckingAccount
extends BankAccount
{ private static final int
FREE_TRANSANCTIONS
= 3;
private static final
double TRANSACTION_
FEE = 2.0;
public void withdraw
(double amount) //
override method
{ transactionCount++;
super.withdraw
(amount); // calling a
superclass method.
// what if we call
withdraw(amount);
}
public void deductFees
( )
{ if (transactionCount >
FREE_TRANSACTIONS)
{ double fees =
TRANSACTION_FEE
* (transactionCount -
FREE_TRANSACTIONS);
super.withdraw(fees);
}
}
Now, let consider the
TimeDepositAccount
class, a subclass of the
SavingsAccount:
public class
TimeDepositAccount
extends
SavingsAccount
{ private int
periodsToMaturity;
private static final
double EARLY_
PENALTY= 20.0;
public void addInterest
( )
{ periodsToMaturity- - ;
super.addInterest( );
}
public void withdraw
(double amount)
{ if (periodsToMaturity
> 0)
super.withdraw(EARLY_
PENALTY);
super.withdraw
(amount);
}
}
The
TimeDepositAccount
is two levels away
from the BankAccount.
The BankAccount is a
superclass, but not the
immediate superclass.
The
TimeDepositAccount
inherits two methods
from the BankAccount
class, namely
getBalance, and
deposit.
Methods can be
inherited from an
indirect superclass as
long as none of the
intermediate
superclasses
overrides them.
The method call
super.addInterest in
addInterest method of
the
TimeDepositAccount
refers to the
addInterest method in
SavingsAccount class.
The super.withdraw in
the withdraw method
of the
TimeDepositAccount
refers to the withdraw
method in
BankAccount.
Subclass Construction
Calling a Superclass
Constructor
ClassName
(parameters)
{ super(parameters);
…
}
Call the superclass
constructor to set the
balance to the initial
balance
public class
CheckingAccount
extends BankAccount
{ public
CheckingAccount
(double init)
{ super(init);
transactionCount = 0;
}
}
The constructor call
must be the first
statement of the
subclass constructor.
Subclass Constructor
When the keyword
super is followed by a
parenthesis, it
indicates a call to the
superclass
constructor.
If super is followed by
a period and a method
name, it indicates a
call to a superclass
method.
Or, we can implement
the CheckingAccount
constructor without
calling the superclass
constructor as can be
seen in the following
example:
public class
CheckingAccount
extends BankAccount
{ public
CheckingAccount
(double init)
{ // use superclass’s
default constructor
super.deposit(init);
transactionCount = 0;
}
}
public class
TimeDepositAccount
extends
SavingsAccount
{ public
TimeDepositAccount
(double rate, int
maturity )
{ super(rate);
periodsToMaturity =
maturity ;
}
}
Subclass Construction
If a subclass
constructor does not
call the superclass
constructor, the
superclass is
constructed with its
default constructor.
If a superclass does
not have a default
constructor, then the
complier reports an
error.
For example,
CheckingAccount can
be implemented
without calling the
superclass
constructor.
The BankAccount class
is constructed with its
default constructor,
which set the balance
to zero.
Then the
CheckingAccount
constructor must
explicitly deposit the
initial balance.
public
CheckingAccount
(double init)
{ // use superclass’s
default constructor
super.deposit(init);
transactionCount = 0;
However, in the case
of
TimeDepositAccount,
the superclass,
SavingsAccount, has
no default constructor.
Thus, we must call the
superclass constructor
explicitly.
public
TimeDepositAccount
(double rate, int
maturity )
{ super(rate);
periodsToMaturity =
maturity ;
}
Polymorphism
“is-a” relationship:
Every object of the
subclass is also a
superclass object, but
with special
properties.
For example, every
CheckingAccount is a
BankAccount.
It is possible to use a
subclass object in
place of a superclass
object.
What is polymorphism?
Let’s consider the
transfer method that
can be used to transfer
money from another
account:
public void transfer
(BankAccount other,
double amount)
{ withdraw(amount);
other.deposit(amount);
}
BankAccount
collegeFund = … ;
CheckingAccount
harrysChecking = … ;
collegeFund.transfer
(harrysChecking, 1000)
;
Which deposit
method?
BankAccount.deposit( )
or CheckingAccount.
deposit( )
Consider the call to the
deposit method, the
other parameter has
type BankAccount.
On the other hand, the
CheckingAccount class
provides its own
deposit method that
updates the
transaction count.
Since the other
variable actually refers
to an object of the
subclass
CheckingAccount, it
would be appropriate if
the CheckingAccount.
deposit method was
called instead.
The principle that the
actual type of the
object determines the
method to be called is
called polymorphism.
In Java, method calls
are always determined
by the type of the
actual object, not the
type of the object
reference.
In Java, all instance
methods are
polymophic.
The term
“polymorphism”
comes from the Greek
words for “many
shapes”.
Program
AccountTest.java
public class AccountTest
{ public static void main
(String[] args)
{ SavingsAccount
momsSavings = new
SavingsAccount(0.5);
TimeDepositAccount
collegeFund =
new TimeDepositAccount
(1, 3);
CheckingAccount
harrysChekcing = new
CheckingAccount(0);
momsSavings.deposit
(10000);
collegeFund.deposit
(10000);
momsSavings.transfer
(harryschecking, 2000);
collegeFund.transfer
(harryschecking, 980);
harrysChecking.withdraw
(500);
harrysChecking.withdraw
(80);
harrysChecking.withdraw
(400);
endOfMonth
(momsSavings);
endOfMonth
(collegeFund);
endOfMonth
(harrysChecking);
printBalance(“mom’s
savings”,
momsSavings);
printBalance(“the
college Fund”,
collegeFund);
printBalance(“Harry’s
checking”,
harrysChecking);
}
public static void
endOfMonth
(SavingsAccount savings)
{ savings.addInterest
( ); }
public static void
endOfMonth
(CheckingAccount
checking)
{ checking.deductFees
( ); }
public static void
printBalance(String
name, BankAccount
account)
{ System.out.println
(“The balance of “ +
name + “ account is $”
+
account.getBalance( ) )
;
}
}
Polymorphism vs.
Overload
Let’s take a look at a
polymorphic method
call such as
other.deposit(amount),
there are several
deposit methods that
can be called.
Both polymorphic
deposit and withdraw
methods are called.
What happens when
the method name is
overloaded;i.e., when
a single class has
several methods with
the same name but
different parameter
types?
For example, we can
have two constructors
BankAccount() and
BankAccount(double).
The compiler selects
the appropriate
method when
compiling a program.
Another example of
overloading, the static
endOfMonth methods
in the AccountTest
class. The compiler
uses the type of the
explicit parameter to
pick the appropriate
method.
The main difference
between
polymorphism and
overloading is the
binding method.
Polymorphism uses a
selection method
called late binding, i.e.,
the compiler doesn’t
make any decision
when translating the
method, it is the virtual
machine that selects
the appropriate
method.
Overloading uses early
binding technique
which compiler picks
an overloaded method
when translating the
program.
Interfaces
Many object-oriented
programming
languages have
multiple inheritance,
i.e.,more than one
superclass.
In Java, a class cannot
have two direct
superclasses.
To support this
feature, we use
interfaces instead of
superclasses.
The purpose of
interfaces is to support
the concept of writing
a single method for
any data type.
Interfaces
An interface is similar
to a class, but
An interface does not
have instance
variables
All methods in an
interface are abstract;
they have a name,
parameters, and a
return type, but they
don’t have an
implementation.
All methods in an
interface are
automatically public.
The interface
declaration lists all
methods that the
interface requires.
For example, the
java.lang package
defines a Comparable
interface as:
public interface
Comparable
{ int compareTo(Object
other);
// no implementation
}
We don’t extend
interfaces; we
implement them, using
the special
implements keyword:
public class
SavingAccount extends
BankAccount
implements
Comparable
{ …
}
Any class that
implements the
Comparable interface
must supply the
compareTo method
public class
SavingsAccount
extends BankAccount
implements
Comparable
{ …
public int compareTo
(Object other)
{ // supply
implementation …
SavingsAccount
otherAcc =
(SavingsAccount)
other;
if (interestRate <
otherAcc.interestRate)
return -1;
if (interestRate >
otherAcc.interestRate)
return 1;
return 0;
} ...
}
Note that the class
must declare the
method as public,
whereas the interface
does not -- all methods
in an interface are
public.
Once a class
implements an
interface, you can
convert a class
reference to an
interface reference:
SavingsAccount
harrysSavings = new
SavingAccount();
Comparable first =
harrysSavings;
However, we can
never construct an
interface:
Comparable second; // OK
second = new
Comparable( ); // ERROR
All interface
references refer to
objects of other
classes-- classes that
implement the
interface.
first.compareTo(second);
A class can have only
one superclass, but it
can implement any
number of interfaces
public class
SavingsAccount
extends BankAccount
implements
Comparable, Cloneable
Constants in
Interfaces
public interface
SwingConstants
{ int NORTH = 1;
int NORTH_EAST = 2;
int EAST = 3;
…
}
// SwingConstants.
NORTH;
all variables in an
interface are
automatically public
static final
Abstract Classes
Abstract Classes : A
class which we cannot
create objects
public abstract class
BankAccount
{ publicabstract void
deductFees( );
…
}
The reason for using
abstract classes is to
force programmers to
create subclasses.
BankAccount
anAccount; // OK
anAccount = new
BankAccount(); // Error
anAccount = new
SavingsAccount(); //
OK
anAccount = null; // OK
Note that we can still
have an object
reference whose type
is an abstract class.
Abstarct classes differ
from interfaces -- they
can have instance
variables and concrete
methods.
Final Methods and
Classes: To prevent
others from creating
subclasses or from
overriding certain
methods
public final class String
{ …. }
public class MyApplet
extends Applet
{ publicfinal boolean
checkPasswd(String
passwd)
{ … }
}
Access Control
Java has four levels of
controlling access to
variables, methods,
and classes:
public access
private access
protected access
Package access (the
default, when no
access modifier is
given)
Proteced data can be
accessed by the
methods of a class and
all its subclasses.
public class
BankAccount
{ protected double
balance; }
If a superclass
declares a method to
be publicly accesible,
we cannot override it
to be more private.
public class BankAccount
{ public void withdraw
(double amount) { … } }
public class
TimeDepositAccount
extends BankAccount
{ private void withdraw
(double amount) { … } //
ERROR
}
Overriding Object
Superclass Methods
The methods of the
Object class are very
general:
- String toString( ) //
Returns a string
representation of the
object
- boolean equals
(Object other) // Test
whether the object
equals
// another object
- Object clone( ) //
Makes a full copy of an
object
Overriding the toString
Method
The toString method
returns a string
representation for
each object.
Rectangle x = new
Rectangle(5, 10, 20, 30);
String s = x.toString( ); //
set s to
“java.awt.Rectangle[x=5,
// y=10,
width=20,height=30]”
The toString method is
called whenever we
concatenate a string
with an object.
System.out.println(“x
means ” + x); // will print
“x means
// java.awt.Rectangle
[x=5,y=10,width=20,height
=30]”
Let’s try the toString
method for the
BankAccount class:
BankAccount x = new
BankAccount(5000);
String s = x.toString( ); //
sets s to “BankAccount@
d246bf”
// print the name of the
class and the memory
address of the object
To work better,
overriding toString
method:
public class BankAccount
{ public String toString()
{ return “BankAccount
[balance=” + balance + “]
”; }
}
BankAccount x = new
BankAccount(5000);
String s = x.toString( );
// sets s to
“BankAccount
[balance=5000]”
Overriding the equals
Method
The equals method is
called whenever we
want to compare two
objects:
if (account1.equals
(account2)) …
// contents are the same
Compare to:
if (account1 = = account2)
…
// test two references are
the same object
public class
BankAccount
{ public boolean equals
(Object otherObj)
{ if (otherObj
instanceof
BankAccount)
{ BankAccount other =
(BankAccount)
otherObj;
return balance = =
other.balance;
}
else
return false;
}
}
Overriding the clone
method
To make a copy of an
object:
public class BankAccount
{ public Object clone( )
{ BankAccount clonedAcc
= new BankAccount
(balance);
return clonedAcc;
}
}
When we call the
method:
BankAccount acc1 = new
BankAccount(1000);
BankAccount acc2 =
(BankAccount)acc1.clone();
Compared to:
BankAccount acc1 =
new BankAccount
(1000);
BankAccount acc2 =
acc1;
Clone Mutable
Instance Variables
Consider the following
class:
public class Customer
{ private String name;
private BankAccount
account;
public Customer(String
aName)
{ name = aName;
account = new
BankAccount();
}
public String getName()
{ return name; }
public BankAccount
getAccount()
{ return account; }
getAccount method
breaks encapsulation
because anyone can
modify the object
state without going
through the public
interface:
Customer harry = new
Customer(“Harry
Handsome”);
BankAccount account =
harry.getAccount();
// anyone can
withdraw money!
account.withdraw
(100000);
So, we should clone
the object reference:
public BankAccount
getAccount()
{ return (BankAccount)
account.clone(); }
The rule of thumb is
that a class should
clone all references to
mutable objects that it
gives out.
The converse is true
as well-- a class
should clone
references that it
receives:
public Customer(String
aName, BankAccount
anAccount)
{ name = aName;
account = (BankAccount)
anAccount.clone();
}
Using Object.clone()
If we have an object
with many instance
variables, it can be
tedious to copy them
all into a new object.
The Object.clone()
simply creates a new
object whose instance
variables are copies of
the original.
public class
BankAccount
{ public Object clone()
{ // not complete
Object clonedAccount
= super.clone();
return clonedAccount;
}
}
Object.clone() checks
that the object being
cloned implements the
Cloneable interface
and do exception
handling.
public class BankAccount
implements Cloneable
{ public Object clone()
{try
{ // clones all instance
variables
Object clonedAccount =
super.clone();
return clonedAccount;
}
catch (
CloneNotSupportedEx
ception e)
{ return null; }
}
}
If an object contains a
reference to another
mutable object, then
we need to call clone
for that reference, in
order to prevent
shallow copy problem
(see Figure 11).
public class Customer
implements Cloneable
{ private String name;
private BankAcount
account;
public Object clone()
{ try
{ Object
clonedCustomer =
(Customer)super.clone
();
clonedCustomer.
account =
(BankAccount)
account.clone();
return
clonedCustomer;
}
catch (
CloneNotSupportedEx
ception e)
{ return null; }
}
}
Packages
A Java package is a
set of related classes.
Table1: Important
Packages in the Java
Library
java.lang Langauge
supoort Math
java.util Utilites
Random
java.io Input/Output
PrintStream
java.awt Abstract
Windowing Color
Toolkit
java.applet Applets
Applet
java.net Networking
Socket
java.sql Database
access ResultSet
javax.swing Swing UI
JButton
To put classes in a
package:
package
packagename;
package
com.horstmann.ccj;
import
java.io.InputStream;
import java.io.
InputStreamReader;
public class
ConsoleReader { … }
….
Importing Packages
import
com.horstmann.ccj.
ConsoleReader;
Interfaces
To learn about
inheritances
To be able to convert
between supertype
and subtype
references
To understand how to
override superclass
methods
To understand the
concept of
polymorphism
To design and use
abstract classes and
interfaces
To learn about
packages and Java
access control
Introduction to
Inheritance
Inheritance is a
mechanism for
enhancing existing,
working class.
For example, suppose
we need to define a
class SavingsAccount
to model an account
that pays a fixed
interest rate on
deposits.
class SavingsAccount
extends BankAccount
{ new instance variables
new methods
}
class SubclassName
extends SuperclassName
{ new instance variables
new methods
}
Inheritance
The more general
class that forms the
basis for inheritance is
called the superclass.
The more specialized
class that inherits from
the superclass is
called the subclass.
One important reason
for inhertance is code
reuse.
Inheritance Diagram
Object <---
BankAccount <----
SavingsAccount
Inheritance Diagram
Which class is a
superclass and which
class is a subclass?
BankAccount is the
superclass and
SavingsAccount is the
subclass.
Suppose we want to
set an interest rate in
the constructor for
SavingsAccount class
and also a method to
apply that interest rate
periodically.
public class
SavingsAccount extends
BankAccount
{ private double
interestRate;
public SavingsAccont
(double rate)
{ constructor
implementation }
public void addInterest
( )
{ method
implementation }
}
public class
BankAccount
{ private double
balance;
public BankAccount( )
{ balance = 0; }
public BankAccount
(double init )
{ balance = init; }
public void deposit
(double amount)
{ balance = balance +
amount; }
public void withdraw
(double amount)
{ balance = balance -
amount; }
public double
getBalance( )
{ return balance; }
}
public class
SavingsAccount
extends BankAccount
{ private double
interestRate; //
instance variable
public SavingsAccont
(double rate) //
constructor
{ interestRate = rate; }
public void addInterest
( ) // instance method
{ double interest =
getBalance() *
interestRate / 100;
deposit(interest);
}
}
// Example Code
SavingsAccount
collegeFund = new
SavingsAccount(10);
collegeFund.deposit
(500); // OK to use
superclass’s method
collegeFund.
addInterest( );
Converting Between
Class Types
A SavingsAccount
object is a special
case of a BankAccount
object. So, you can
store a reference to a
SavingsAccount object
into a BankAccount
object.
SavingsAccount
collegeFund = new
SavingsAccount(10);
BankAccount anAccount =
collegeFund;
Object anObject =
collegeFund;
The three object
references stored in
collegeFund,
anAccount, and
anObject refer to the
same object of type
SavingsAccount.
However, the
anAccount knows less
than the full story
about the object to
which it refers
anAccount.deposit(1000)
; // OK
anAccount.addInterest( )
; // Not OK
Because anAccount is
an object of type
BankAccount, we
cannot use the
addInterest method.
anObject.deposit(1000); //
Not OK
The variable anObject
knows even less,
because it is an object
of type Object and
deposit is not a
method of the object
class.
Why would anyone
want to know less
about an object and
store a reference in an
object variable of a
superclass? Reuse.
Consider the transfer
method
void transfer(BankAccount
other, double amount)
{ withdraw(amount);
other.deposit(amount);
}
We can use this
method to transfer
money from one bank
account to another:
BankAccount
momsChecking = … ;
BankAccount
harrysChecking = … ;
momsChecking.transfer
(harrysChecking, 1000);
We can also use this
method to transfer
money into a
SavingAccount:
SavingsAccount
CollegeFund = … ;
momsChecking.
transfer(collegeFund,
1000);
However, we cannot
convert between
unrelated classes:
Rectangle r =
CollegeFund; // Error
Subclass reference can
be assigned to
superclass reference
Object anObject =
collegeFund;
But, superclass
reference must be
casted to subclass
reference.
SavingsAccount x =
(SavingsAcount)anObject;
as long as we
absolutely sure that
anObject really refers
to a SavingsAccount
object.
To play it safe, we can
use the instanceof
operator to test
whether an object
belongs to a particular
class (or one of its
subclasses).
Object instanceof
ClasssName
Rectangle r;
if (x instanceof
Rectangle)
r = (Rectangle) x;
SavingsAccount x;
if (anObject instanceof
SavingsAccount)
x = (SavingsAccount)
anObject;
else
x = null;
Hierarchies
Hierarchies are
frequently represented
as trees, with the
most general concepts
at the root.
In Java, it is common
to group classes in
inheritance
hierarchies.
Inheritance
Hierarchies
The classes
representing the most
general concepts are
near the root, more
specialized classes
towards the branches.
When designing a
hierarchy of classes,
those common
properties are
collected in a
superclass. More
specialized properties
can be found in
subclasses.
Consider a bank that
offers its customers
three kinds of
accounts:
CheckingAccount
SavingsAccount
TimeDepositAccount
The checking account
has no interest, gives
you a small number of
free transactions per
month, and charges a
transaction fee for
each additional
transactions.
The savings account
compounds interest
monthly.
The time deposit
account has a penalty
for early withdrawal.
The inheritance
hierarchy for these
account classes is:
protected Members
A superclass’s public
members are
accessible anywhere
the program has a
reference to that
superclass type or one
of its subclasses
types.
A superclass’s private
members are
accessible only in
methods of that
superclasses.
A superclass’s
protected members
serve as an
intermediate level of
protection between
public and private
access.
protected Members
A superclass’s
protected members
can be accessed by
methods of the
superclass, by
methods of subclasses
and by methods of
other classes in the
same package
(protected members
have package access).
Subclass methods can
normally refer to public
and protected
members of the
superclass simply by
using the member
names.
Inheritance
// Definition of a class
Point
public class Point {
protected int x, y;
public Point()
{ setPoint(0,0); }
public Point(int a, int b)
{ setPoint(a, b); }
public void setPoint(int
a, int b)
{ x = a;
y = b;
}
public int getX()
{ return x; }
public int getY()
{ return y; }
public String toString()
{ return “[“ + x + “, “ + y
+ “]”; } }
// Definition of class
circle
public class Circle
extends Point {
protected double
radius;
public Circle()
{ // implicit call to
superclass constructor
setRadius(0); }
public Circle(double r,
int a, int b)
{ super(a, b);
setRadius(r);
}
public void setRadius
(double r)
{ radius = (r >= 0.0 ? r :
0.0); }
pubic double getRadius
()
{ return radius; }
public double area()
{ return Math.PI *
radius * radius; }
public String toString()
{ return “Center = “ +
“[“ + x + “,” + y + “]” +
“; Radius = “ + radius;
}
}
// Main program
import java.text.
DecimalFormat;
import javax.swing.
JOptionPane;
public class
InheritanceTest {
public static void main
(String[] args)
{ Point pointRef, p;
Circle circleRef, c;
String output;
p = new Point(30,50);
c = new Circle(2.7, 120,
89);
output = “Point p: “+
p.toString() +
“\nCircle c: “ +
c.toString() ;
pointRef = c;
output += “\n\nCircle c
(via pointRef): “ +
pointRef.toString() ;
circleRef = (Circle)
pointRef;
output += “\n\nCircle c
(via circleRef): “ +
circleRef.toString() ;
DecimalFormat
precision2 = new
DecimalFormat(“0.00”)
;
output += “\n\nArea of
c (via circleRef): “ +
precision2.format
(circleRef.area());
if (p instanceof Circle)
{
circleRef = (Circle) p;
output += “\n\ncast
successful”;
}
else
output += “\n\n p does
not refer to a circle”;
JOptionPane.
showMessageDialog
(null, output,
“Inheritance”,
JOptionPane.
INFORMATION_
MESSAGE);
System.exit(0);
}
}
Inheriting Instance
Variables and Methods
When defining
additional methods for
a subclass, there are 3
options:
1. We can override
methods from the
superclass by
specifying a method
with the same
signature (i.e., the
same name and the
same parameters).
2. We can inherit
methods from the
superclass. If we do
not explicitly override
a superclass method,
we automatically
inherit it.
3. We can define new
methods that does not
exist in the superclass.
When defining
additional instance
variables for a
subclass, there are 2
options:
1. We can inherit
variables from the
superclass.
2. We can define new
variables. These new
variables are only
presented in subclass
objects.
Remember! We can
never override
instance variables.
Shadowing
The newly defined
subclass variable
shadows the
superclass variable.
The superclass
variable is still
present, but it cannot
be accessed.
When you refer to
balance in a
SavingAccount
method, you access
the new instance
variable.
Shadowing instance
variables is harmless.
Shadowing Instance
Variables
A subclass has no
access to the private
instance of the
superclass
public class
CheckingAccount
extends BankAccount
{ public void deposit
(double amount)
{ transactionCount++;
balance = balance +
amount; // ERROR
}
}
To solve the problem
public class
CheckingAccount
extends BankAccount
{ private double
balance; // shadow
variable
public void deposit
(double amount)
{ transactionCount++;
balance = balance +
amount; // OK but
different balance
}
}
Example
public class
BankAccount
{ private double
balance;
public double
getBalance() {…}
public void deposit
(double amount) {…}
public void withdraw
(double amount) {…}
}
public class
CheckingAccount
extends BankAccount
{ private int
transactionCount;
public void deposit
(double amount) {…}
public void withdraw
(double amount) {…}
public void deductFees
() {…}
}
Subclass methods
have no access rights
to the private data of
the superclass.
If we want to modify a
private superclass
variable, we must use
a public method of the
superclass.
Can we write the
deposit method as
public void deposit
(double amount)
{ transactionCount++;
deposit(amount); // Is
it OK?
}
If we define the
method like that, it will
call itself infinitely.
We need to be more
specific that we want
to invoke the
superclass’s deposit
method by using a
special keyword super
for this purpose:
public void deposit
(double amount)
{ transactionCount++;
super.deposit(amount);
}
Calling a Superclass
Methods
public class
CheckingAccount
extends BankAccount
{ private static final int
FREE_TRANSANCTIONS
= 3;
private static final
double TRANSACTION_
FEE = 2.0;
public void withdraw
(double amount) //
override method
{ transactionCount++;
super.withdraw
(amount); // calling a
superclass method.
// what if we call
withdraw(amount);
}
public void deductFees
( )
{ if (transactionCount >
FREE_TRANSACTIONS)
{ double fees =
TRANSACTION_FEE
* (transactionCount -
FREE_TRANSACTIONS);
super.withdraw(fees);
}
}
Now, let consider the
TimeDepositAccount
class, a subclass of the
SavingsAccount:
public class
TimeDepositAccount
extends
SavingsAccount
{ private int
periodsToMaturity;
private static final
double EARLY_
PENALTY= 20.0;
public void addInterest
( )
{ periodsToMaturity- - ;
super.addInterest( );
}
public void withdraw
(double amount)
{ if (periodsToMaturity
> 0)
super.withdraw(EARLY_
PENALTY);
super.withdraw
(amount);
}
}
The
TimeDepositAccount
is two levels away
from the BankAccount.
The BankAccount is a
superclass, but not the
immediate superclass.
The
TimeDepositAccount
inherits two methods
from the BankAccount
class, namely
getBalance, and
deposit.
Methods can be
inherited from an
indirect superclass as
long as none of the
intermediate
superclasses
overrides them.
The method call
super.addInterest in
addInterest method of
the
TimeDepositAccount
refers to the
addInterest method in
SavingsAccount class.
The super.withdraw in
the withdraw method
of the
TimeDepositAccount
refers to the withdraw
method in
BankAccount.
Subclass Construction
Calling a Superclass
Constructor
ClassName
(parameters)
{ super(parameters);
…
}
Call the superclass
constructor to set the
balance to the initial
balance
public class
CheckingAccount
extends BankAccount
{ public
CheckingAccount
(double init)
{ super(init);
transactionCount = 0;
}
}
The constructor call
must be the first
statement of the
subclass constructor.
Subclass Constructor
When the keyword
super is followed by a
parenthesis, it
indicates a call to the
superclass
constructor.
If super is followed by
a period and a method
name, it indicates a
call to a superclass
method.
Or, we can implement
the CheckingAccount
constructor without
calling the superclass
constructor as can be
seen in the following
example:
public class
CheckingAccount
extends BankAccount
{ public
CheckingAccount
(double init)
{ // use superclass’s
default constructor
super.deposit(init);
transactionCount = 0;
}
}
public class
TimeDepositAccount
extends
SavingsAccount
{ public
TimeDepositAccount
(double rate, int
maturity )
{ super(rate);
periodsToMaturity =
maturity ;
}
}
Subclass Construction
If a subclass
constructor does not
call the superclass
constructor, the
superclass is
constructed with its
default constructor.
If a superclass does
not have a default
constructor, then the
complier reports an
error.
For example,
CheckingAccount can
be implemented
without calling the
superclass
constructor.
The BankAccount class
is constructed with its
default constructor,
which set the balance
to zero.
Then the
CheckingAccount
constructor must
explicitly deposit the
initial balance.
public
CheckingAccount
(double init)
{ // use superclass’s
default constructor
super.deposit(init);
transactionCount = 0;
However, in the case
of
TimeDepositAccount,
the superclass,
SavingsAccount, has
no default constructor.
Thus, we must call the
superclass constructor
explicitly.
public
TimeDepositAccount
(double rate, int
maturity )
{ super(rate);
periodsToMaturity =
maturity ;
}
Polymorphism
“is-a” relationship:
Every object of the
subclass is also a
superclass object, but
with special
properties.
For example, every
CheckingAccount is a
BankAccount.
It is possible to use a
subclass object in
place of a superclass
object.
What is polymorphism?
Let’s consider the
transfer method that
can be used to transfer
money from another
account:
public void transfer
(BankAccount other,
double amount)
{ withdraw(amount);
other.deposit(amount);
}
BankAccount
collegeFund = … ;
CheckingAccount
harrysChecking = … ;
collegeFund.transfer
(harrysChecking, 1000)
;
Which deposit
method?
BankAccount.deposit( )
or CheckingAccount.
deposit( )
Consider the call to the
deposit method, the
other parameter has
type BankAccount.
On the other hand, the
CheckingAccount class
provides its own
deposit method that
updates the
transaction count.
Since the other
variable actually refers
to an object of the
subclass
CheckingAccount, it
would be appropriate if
the CheckingAccount.
deposit method was
called instead.
The principle that the
actual type of the
object determines the
method to be called is
called polymorphism.
In Java, method calls
are always determined
by the type of the
actual object, not the
type of the object
reference.
In Java, all instance
methods are
polymophic.
The term
“polymorphism”
comes from the Greek
words for “many
shapes”.
Program
AccountTest.java
public class AccountTest
{ public static void main
(String[] args)
{ SavingsAccount
momsSavings = new
SavingsAccount(0.5);
TimeDepositAccount
collegeFund =
new TimeDepositAccount
(1, 3);
CheckingAccount
harrysChekcing = new
CheckingAccount(0);
momsSavings.deposit
(10000);
collegeFund.deposit
(10000);
momsSavings.transfer
(harryschecking, 2000);
collegeFund.transfer
(harryschecking, 980);
harrysChecking.withdraw
(500);
harrysChecking.withdraw
(80);
harrysChecking.withdraw
(400);
endOfMonth
(momsSavings);
endOfMonth
(collegeFund);
endOfMonth
(harrysChecking);
printBalance(“mom’s
savings”,
momsSavings);
printBalance(“the
college Fund”,
collegeFund);
printBalance(“Harry’s
checking”,
harrysChecking);
}
public static void
endOfMonth
(SavingsAccount savings)
{ savings.addInterest
( ); }
public static void
endOfMonth
(CheckingAccount
checking)
{ checking.deductFees
( ); }
public static void
printBalance(String
name, BankAccount
account)
{ System.out.println
(“The balance of “ +
name + “ account is $”
+
account.getBalance( ) )
;
}
}
Polymorphism vs.
Overload
Let’s take a look at a
polymorphic method
call such as
other.deposit(amount),
there are several
deposit methods that
can be called.
Both polymorphic
deposit and withdraw
methods are called.
What happens when
the method name is
overloaded;i.e., when
a single class has
several methods with
the same name but
different parameter
types?
For example, we can
have two constructors
BankAccount() and
BankAccount(double).
The compiler selects
the appropriate
method when
compiling a program.
Another example of
overloading, the static
endOfMonth methods
in the AccountTest
class. The compiler
uses the type of the
explicit parameter to
pick the appropriate
method.
The main difference
between
polymorphism and
overloading is the
binding method.
Polymorphism uses a
selection method
called late binding, i.e.,
the compiler doesn’t
make any decision
when translating the
method, it is the virtual
machine that selects
the appropriate
method.
Overloading uses early
binding technique
which compiler picks
an overloaded method
when translating the
program.
Interfaces
Many object-oriented
programming
languages have
multiple inheritance,
i.e.,more than one
superclass.
In Java, a class cannot
have two direct
superclasses.
To support this
feature, we use
interfaces instead of
superclasses.
The purpose of
interfaces is to support
the concept of writing
a single method for
any data type.
Interfaces
An interface is similar
to a class, but
An interface does not
have instance
variables
All methods in an
interface are abstract;
they have a name,
parameters, and a
return type, but they
don’t have an
implementation.
All methods in an
interface are
automatically public.
The interface
declaration lists all
methods that the
interface requires.
For example, the
java.lang package
defines a Comparable
interface as:
public interface
Comparable
{ int compareTo(Object
other);
// no implementation
}
We don’t extend
interfaces; we
implement them, using
the special
implements keyword:
public class
SavingAccount extends
BankAccount
implements
Comparable
{ …
}
Any class that
implements the
Comparable interface
must supply the
compareTo method
public class
SavingsAccount
extends BankAccount
implements
Comparable
{ …
public int compareTo
(Object other)
{ // supply
implementation …
SavingsAccount
otherAcc =
(SavingsAccount)
other;
if (interestRate <
otherAcc.interestRate)
return -1;
if (interestRate >
otherAcc.interestRate)
return 1;
return 0;
} ...
}
Note that the class
must declare the
method as public,
whereas the interface
does not -- all methods
in an interface are
public.
Once a class
implements an
interface, you can
convert a class
reference to an
interface reference:
SavingsAccount
harrysSavings = new
SavingAccount();
Comparable first =
harrysSavings;
However, we can
never construct an
interface:
Comparable second; // OK
second = new
Comparable( ); // ERROR
All interface
references refer to
objects of other
classes-- classes that
implement the
interface.
first.compareTo(second);
A class can have only
one superclass, but it
can implement any
number of interfaces
public class
SavingsAccount
extends BankAccount
implements
Comparable, Cloneable
Constants in
Interfaces
public interface
SwingConstants
{ int NORTH = 1;
int NORTH_EAST = 2;
int EAST = 3;
…
}
// SwingConstants.
NORTH;
all variables in an
interface are
automatically public
static final
Abstract Classes
Abstract Classes : A
class which we cannot
create objects
public abstract class
BankAccount
{ publicabstract void
deductFees( );
…
}
The reason for using
abstract classes is to
force programmers to
create subclasses.
BankAccount
anAccount; // OK
anAccount = new
BankAccount(); // Error
anAccount = new
SavingsAccount(); //
OK
anAccount = null; // OK
Note that we can still
have an object
reference whose type
is an abstract class.
Abstarct classes differ
from interfaces -- they
can have instance
variables and concrete
methods.
Final Methods and
Classes: To prevent
others from creating
subclasses or from
overriding certain
methods
public final class String
{ …. }
public class MyApplet
extends Applet
{ publicfinal boolean
checkPasswd(String
passwd)
{ … }
}
Access Control
Java has four levels of
controlling access to
variables, methods,
and classes:
public access
private access
protected access
Package access (the
default, when no
access modifier is
given)
Proteced data can be
accessed by the
methods of a class and
all its subclasses.
public class
BankAccount
{ protected double
balance; }
If a superclass
declares a method to
be publicly accesible,
we cannot override it
to be more private.
public class BankAccount
{ public void withdraw
(double amount) { … } }
public class
TimeDepositAccount
extends BankAccount
{ private void withdraw
(double amount) { … } //
ERROR
}
Overriding Object
Superclass Methods
The methods of the
Object class are very
general:
- String toString( ) //
Returns a string
representation of the
object
- boolean equals
(Object other) // Test
whether the object
equals
// another object
- Object clone( ) //
Makes a full copy of an
object
Overriding the toString
Method
The toString method
returns a string
representation for
each object.
Rectangle x = new
Rectangle(5, 10, 20, 30);
String s = x.toString( ); //
set s to
“java.awt.Rectangle[x=5,
// y=10,
width=20,height=30]”
The toString method is
called whenever we
concatenate a string
with an object.
System.out.println(“x
means ” + x); // will print
“x means
// java.awt.Rectangle
[x=5,y=10,width=20,height
=30]”
Let’s try the toString
method for the
BankAccount class:
BankAccount x = new
BankAccount(5000);
String s = x.toString( ); //
sets s to “BankAccount@
d246bf”
// print the name of the
class and the memory
address of the object
To work better,
overriding toString
method:
public class BankAccount
{ public String toString()
{ return “BankAccount
[balance=” + balance + “]
”; }
}
BankAccount x = new
BankAccount(5000);
String s = x.toString( );
// sets s to
“BankAccount
[balance=5000]”
Overriding the equals
Method
The equals method is
called whenever we
want to compare two
objects:
if (account1.equals
(account2)) …
// contents are the same
Compare to:
if (account1 = = account2)
…
// test two references are
the same object
public class
BankAccount
{ public boolean equals
(Object otherObj)
{ if (otherObj
instanceof
BankAccount)
{ BankAccount other =
(BankAccount)
otherObj;
return balance = =
other.balance;
}
else
return false;
}
}
Overriding the clone
method
To make a copy of an
object:
public class BankAccount
{ public Object clone( )
{ BankAccount clonedAcc
= new BankAccount
(balance);
return clonedAcc;
}
}
When we call the
method:
BankAccount acc1 = new
BankAccount(1000);
BankAccount acc2 =
(BankAccount)acc1.clone();
Compared to:
BankAccount acc1 =
new BankAccount
(1000);
BankAccount acc2 =
acc1;
Clone Mutable
Instance Variables
Consider the following
class:
public class Customer
{ private String name;
private BankAccount
account;
public Customer(String
aName)
{ name = aName;
account = new
BankAccount();
}
public String getName()
{ return name; }
public BankAccount
getAccount()
{ return account; }
getAccount method
breaks encapsulation
because anyone can
modify the object
state without going
through the public
interface:
Customer harry = new
Customer(“Harry
Handsome”);
BankAccount account =
harry.getAccount();
// anyone can
withdraw money!
account.withdraw
(100000);
So, we should clone
the object reference:
public BankAccount
getAccount()
{ return (BankAccount)
account.clone(); }
The rule of thumb is
that a class should
clone all references to
mutable objects that it
gives out.
The converse is true
as well-- a class
should clone
references that it
receives:
public Customer(String
aName, BankAccount
anAccount)
{ name = aName;
account = (BankAccount)
anAccount.clone();
}
Using Object.clone()
If we have an object
with many instance
variables, it can be
tedious to copy them
all into a new object.
The Object.clone()
simply creates a new
object whose instance
variables are copies of
the original.
public class
BankAccount
{ public Object clone()
{ // not complete
Object clonedAccount
= super.clone();
return clonedAccount;
}
}
Object.clone() checks
that the object being
cloned implements the
Cloneable interface
and do exception
handling.
public class BankAccount
implements Cloneable
{ public Object clone()
{try
{ // clones all instance
variables
Object clonedAccount =
super.clone();
return clonedAccount;
}
catch (
CloneNotSupportedEx
ception e)
{ return null; }
}
}
If an object contains a
reference to another
mutable object, then
we need to call clone
for that reference, in
order to prevent
shallow copy problem
(see Figure 11).
public class Customer
implements Cloneable
{ private String name;
private BankAcount
account;
public Object clone()
{ try
{ Object
clonedCustomer =
(Customer)super.clone
();
clonedCustomer.
account =
(BankAccount)
account.clone();
return
clonedCustomer;
}
catch (
CloneNotSupportedEx
ception e)
{ return null; }
}
}
Packages
A Java package is a
set of related classes.
Table1: Important
Packages in the Java
Library
java.lang Langauge
supoort Math
java.util Utilites
Random
java.io Input/Output
PrintStream
java.awt Abstract
Windowing Color
Toolkit
java.applet Applets
Applet
java.net Networking
Socket
java.sql Database
access ResultSet
javax.swing Swing UI
JButton
To put classes in a
package:
package
packagename;
package
com.horstmann.ccj;
import
java.io.InputStream;
import java.io.
InputStreamReader;
public class
ConsoleReader { … }
….
Importing Packages
import
com.horstmann.ccj.
ConsoleReader;
Labels:
learn java language online
lesson 7-Arrays & Vectors
7. Arrays and Vectors
To be able to use
arrays and vectors to
collect objects
To implement partially
filled arrays
To be able to pass
arrays to methods
To learn about
common array
algorithms
To build classes
containing arrays and
vectors
To learn how to use
two-dimensional
arrays
Arrays
An array is a collection
of data items of the
same type.
Every element of the
collection can be
accessed separately
by using index.
In Java, array indexing
starts from 0.
Array can be
constructed using
double[ ] data = new
double[10];
An Array Reference
and an Array
Using Arrays to Store
Data
To access the fifth
element in an array,
we use
data[4] = 35.5;
System.out.println
(“The price = ” + data
[4]);
sum = sum + data[4];
double[ ] data; // error :
not initialized
data[0] = 10.5; // assign
a value to data[0]
Bounds Error
double[ ] data = new
double[10];
// we can access data
[0], data[1], … data[9]
data[10] = 35.5; //
cause
ArrayIndexOutOfBound
sException
Size of the array:
arrayRefName.length
double[ ] data = new
double[10];
for (int i = 0; i <
data.length; i++)
if (data[i] < lowest)
lowest = data[i];
Don’t combine array
access and index
increment
x = v[i++]; => x = v[i]; i+
+;
Array Initialization
int[] primes = new int
[5];
primes[0] = 2; primes
[1] = 3; primes[2] = 5;
primes[3] = 7; primes
[4] = 11;
// better to use the
following
int[ ] primes = {2, 3, 5,
7, 11};
Copying Arrays
Reference
double[] data = new
double[10];
// … fill array
double[] prices;
prices = data; // copy
reference of array
Make a true copy of
array
double[] prices = new
double[data.length];
for (int j = 0; j <
data.length; j++)
prices[j] = data[j];
Better if we use the
static
System.arrayCopy
method
System.arrayCopy
(from, fromStart, to,
toStart, count);
System.arrayCopy
(data, 0, prices, 0,
data.length);
Partially Filled Arrays
When we need to set
the size of an array
before you know how
many elements there
are, we can make an
array that is large
enough and partially fill
it.
final int DATA_LENGTH =
1000;
double[] data = new
double[DATA_LENGTH];
then keep a variable
that tells how many
elements are actually
used
int dataSize = 0; // for
collect size of array
and increment this
variable every time
data is added to the
array
double price =
console.readDouble();
data[dataSize] = price;
dataSize++;
Remember! Not to
access the data over
the datasize
for (int j=0; j <
dataSize; j++) // not
data.length
System.out.println(data
[j]);
and not to overfill the
array
if (dataSize <
data.length) // OK
{ data[dataSize] =
price;
dataSize++;
}
else // Array is full
{ System.out.println
(“Sorry, array is full”);
}
If the array fills up, we
can create a new,
larger array; copy all
elements into the new
array; and then attach
the new array to the
old array variable.
if (dataSize >= data.length)
{ //
double[] newData = new
double[2 * data.length];
// copy data
System.arrayCopy(data,
0, newData, 0,
data.length);
//create new reference for
"new data" ่
data = newData;
}
Suppose we want to
write a program that
reads a set of prices
offered by 10 vendors
for a particular product
and then prints them,
marking the lowest
one.
public class BestPrice
{ public static void
main(String[] args)
{ final int DATA_LENGTH =
1000;
double[] data = new
double[DATA_LENGTH];
int dataSize = 0;
// read data
ConsoleReader
console = new
ConsoleReader
(System.in);
boolean done = false;
while (!done)
{ System.out.println
("Enter price, 0 to
quit:");
double price =
console.readDouble();
if (price == 0) // end of
input
done = true;
else if (dataSize <
data.length)
{ // add price to data
array
data[dataSize] = price;
dataSize++;
}
else // array is full
{ System.out.println
("Sorry, the array is
full.");
done = true;
}
}
// compute lowest
price
if (dataSize == 0)
return; // no data
double lowest = data
[0];
for (int i = 1; i <
dataSize; i++)
if (data[i] < lowest)
lowest = data[i];
// print out prices,
marking the lowest
one
for (int i = 0; i <
dataSize; i++)
{ System.out.print(data
[i]);
if (data[i] == lowest)
System.out.print(" <--
lowest price");
System.out.println();
}
}
}
Array Parameters and
Return Values
The method to
compute the average
of an array of floating-
point numbers
public static double
average(double[] data)
{ if (data.length == 0)
return 0;
double sum = 0;
for (int j = 0; j <
data.length; j++)
sum = sum + data[j];
return sum /
data.length;
}
double[] prices =
{ 10.5, 24.5, 30.5, 10.0,
50.4 };
System.out.println
(“Price average = ” +
average(prices));
Array Parameters
When an array is
passed to a method,
the array parameter
contains a copy of the
reference to the
argument array.
A method can modify
the entries of any array
that you pass to it.
The method can return
an array if the returning
result consists of a
collection of values of
the same type.
A method can return an
array
public static int[]
randomData(int length,
int n)
{ Random generator =
new Random();
int[] data = new int
[length];
for (int j = 0; j <
data.length; j++)
data[j] =
generator.nextInt(n);
return data;
}
Array Algorithms
Finding a Value: want
to know the price of
first product that has
price lower or equal
1000
double [] prices;
double targetPrice = 1000;
int j = 0;
boolean found = false;
while (j < prices.length
&& !found)
{ if (prices[j] <=
targetPrice)
found = true;
else
j++;
}
if (found)
System.out.println(“Item”
+ j + “ has a price of” +
prices[j]);
Counting: want to
know the number of
product that has price
lower or equal 1000
double [] prices;
double targetPrice = 1000;
int count = 0;
for (int j = 0; j <
prices.length; j++)
{ if (prices[j] <=
targetPrice)
count++;
}
System.out.println(count
+ “ computers.”);
Removing an Element
from an Unordered
Array
If we want to remove
an element from an
unordered array, we
simply overwrite the
element to be
removed with the last
element of the array.
However, an array
cannot be shrunk to
get rid of the last
element, so we have
to used the technique
of a partially filled
array.
Removing an Element :
Array not sorted
public class Remove1
{ public static void
main(String[] args)
{ ConsoleReader
console = new
ConsoleReader
(System.in);
String[] staff = new
String[5];
staff[0] = "Harry"; staff
[1] = "Romeo"; staff[2]
= "Dick";
staff[3] = "Juliet"; staff
[4] = "Tom";
int staffSize =
staff.length;
print(staff, staffSize);
System.out.println
("Remove which
element? (0 - 4)");
int pos =
console.readInt();
// overwrite the
removed element with
the last element
staff[pos] = staff
[staffSize - 1];
staffSize--;
print(staff, staffSize);
}
/**
Prints an array of
strings
@param s the string
array
@param sSize the
number of strings in
the array
*/
public static void print
(String[] s, int sSize)
{ for (int i = 0; i < sSize;
i++)
System.out.println(i + ":
" + s[i]);
}
}
Remove an Element
from an Ordered Array
After removing an
element, we have to
move all elements
beyond the element to
be removed by one
slot.
Removing from an
Ordered Array
public class Remove2
{ public static void
main(String[] args)
{ ...
int staffSize =
staff.length;
print(staff, staffSize);
System.out.println
("Remove which
element? (0 - 4)");
int pos =
console.readInt();
// shift all elements
above pos down
for (int i = pos; i <
staffSize - 1; i++)
staff[i] = staff[i + 1];
staffSize--;
print(staff, staffSize);
}
}
Inserting an Element
Suppose we want to
insert an element in
the middle of an array,
we must move all
elements beyond the
insertion location by
one slot.
When we insert an
element, we start
moving at the end of
an array, move that
element, then go to
the one before it. This
operation is in reverse
order from removing
operation.
public class Insert
{ public static void
main(String[] args)
{ String[] staff = new
String[6];
staff[0] = “Mary”;
staff[1] = “Ben”;
staff[2] = “Sandy”;
staff[3] = “Janet”;
staff[4] = “Peter”;
int staffSize =
staff.length - 1;
System.out.print
("Insert before which
element? (0 - 4) ");
int pos =
console.readInt();
// shift all element
after pos up by one
for (int i = staffSize; i >
pos; i--)
staff[i] = staff[i - 1];
// insert new element
into freed slot
staff[pos] = "New,
Nina";
staffSize++;
print(staff, staffSize);
}
Parallel Arrays, Arrays
of Objects
and Array as Object
Data
When we want to
analyze a data set that
contains more than
one data item, such as
a data set that contains
the names, prices, and
quality of a collection
of products.
We want to look for
products that give
good quality at low
price.
The problem with this
program is that it
contains three arrays
of the same length.
Bestdata.java
public class BestData
{ public static void
main(String[] args)
{ final int DATA_MAX =
500;
String[] name = new
String[DATA_MAX];
double[] price = new
double[DATA_MAX];
int[] score = new int
[DATA_MAX];
int Size = 0;
// read data from
console
ConsoleReader
console = new
ConsoleReader
(System.in);
Boolean done = false;
while (!done)
{ System.out.println
(“Enter name or leave
blank when done: ”);
String inLine =
console.readLine();
if (inLine == null ||
inLine.equals(“ ”))
done = true;
else if (Size < DATA_
MAX)
{ name[Size] = inLine;
System.out.println
(“Enter price: “);
inLine =
console.readLine();
price[Size] =
Double.parseDouble
(inLine);
System.out.println
(“Enter score: “);
inLine =
console.readLine();
score[Size] =
Integer.parseInt
(inLine);
Size++;
}
else
{ System.out.println
(“Sorry, the array is
full. “);
done = true;
}
}
// compute best buy
if (Size == 0) return; //
no data
double best = score[0]
/ price[0];
for (int j = 0; j < Size; j+
+)
if (score[j] / price[j] >
best)
best = score[j] / price
[j];
final int COLUMN_WIDTH
= 30;
for (int j = 0; j < Size; j+
+)
{ System.out.print
(name[j]);
int pad = COLUMN_
WIDTH - name[j].length();
for (int k = 1; k < = pad; k+
+)
System.out.print(“ “);
System.out.print(“ $” +
price[j] +“ score = “ +
score[j]);
if (score[j] / price[j] ==
best)
System.out.print(“ <--
best buy “);
System.out.println();
}
}
}
Parallel Arrays
The problem with
Bestdata.java is that it
contains three parallel
arrays namely name[j],
price[j], and score[].
We must ensure that
the arrays always have
the same length and
that each slice is filled
with values that
actually belong
together.
We can solve this
problem by turning this
concept into class.
Product.java
class Product
{
private String name;
private double price;
private int score;
public Product(String n,
double p, int s)
{ name = n;
price = p;
score = s;
}
public String getName
()
{ return name;
}
public double getPrice
()
{ return price;
}
public int getScore()
{ return score;
}
}
Bestproduct.java
public class
BestProduct
{ public static void
main(String[] args)
{ final int DATA_MAX =
500;
Product[] data = new
Product[DATA_MAX];
int Size = 0;
// read data from
console
ConsoleReader
console = new
ConsoleReader
(System.in);
Boolean done = false;
while (!done)
{ Product p =
readProduct(console);
if (p == null)
done = true;
else if (Size < DATA_
MAX)
{ data[Size] = p;
Size++;
}
else
{
System.out.println
(“Sorry, the array is
full.”);
done = true;
}
}
// compute best buy
if (Size == 0) return; //
no data
double best = data[0]
.getScore() / data[0]
.getPrice();
for (int j = 1; j < Size; j+
+)
{ double ratio = data[j]
.getScore() / data[j]
.getPrice();
if (ratio > best)
best = ratio;
}
// print out data
for (int j = 0; j < Size; j+
+)
{ printProduct(data[j]);
if (data[j].getScore() /
data[j].getPrice() ==
best)
System.out.print(“ <--
best buy “);
}
}
public static Product
readProduct
(ConsoleReader in)
{ System.out.println
(“Enter name or leave
blank when done:”);
String name =
in.readLine();
if (name == null ||
name.equals(“ “))
return null;
System.out.println
(“Enter price: “);
String inLine =
in.readLine();
double price =
Double.parseDouble
(inLine);
System.out.println
(“Enter score: “);
inLine = in.readLine();
int score =
Integer.parseInt
(inLine);
return new Product
(name, price, score);
}
public static void
printProduct(Product p)
{ final int WIDTH = 30;
System.out.print
(p.getName());
int pad = WIDTH –
p.getName().length;
for (int j = 1; j <= pad; j+
+)
System.out.print(“ “);
System.out.print(“ $” +
p.getPrice() + “ score =
“ +
p.getScore());
}
}
Example:
PolygonTest.java
Arrays as Object Data
import
java.applet.Applet;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.geom.Line2D;
import
java.awt.geom,Point2D
;
public class
PolygonTest extends
Applet
{ public void paint
(Graphics g)
{ Graphics2D g2 =
(Grahpics2D) g;
Polygon triangle = new
Polygon(3);
triangle.add(new
Point2D.Double(40, 40)
);
triangle.add(new
Point2D.Double(120,
160));
triangle.add( new
Point2D.Double(20,
120));
double x = 200;
double y = 200;
double r = 50;
Polygon pentagon =
new Polygon(5);
for (int j = 0; j < 5; j++)
pentagon.add(new
Point2D.Double(
x + r * Math.cos(2 *
Math.PI * j / 5),
y + r * Math.sin(2 *
Math.PI * j / 5)));
triangle.draw(g2);
pentagon.draw(g2);
}
}
// Polygon class
definition
class Polygon
{ public polygon(int n)
{ corners = new
Point2D.Double[n];
cornerCount = 0;
}
public void add
(Point2D.Double p)
{ if (cornerCount <
corners.length)
{ corners[cornerCount]
= p;
cornerCount++;
}
}
public void draw
(Graphics2D g2)
{ for (int j = 0; j <
cornerCount; j++)
{ Point2D.Double from
= corners[j];
Point2D.Double to =
corners[(j+1) %
corners.length];
g2.draw(new
Line2D.Double(from, to);
}
}
private Point2D.Double
[] corners;
private int
cornerCount;
}
Example
To generate a triangle,
we can use the
following segment of
code:
Polygon triangle = new
Polygon(3);
triangle.add(new
Point2D.Double(40,40))
;
triangle.add(new
Point2D.Double
(120,160));
triangle.add(new
Point2D.Double(20,120)
);
Another kind of
geometric shape is
called a regular
polygon which has all
sides of the same
length.
A regular n-gon with
center (x,y) and radius
r has n corners, c0, c1,
…, cn-1, where
ci = (x+r•cos(2p i/n), y
+r•sin(2p i/n))
A regular polygon can
be generated by:
Polygon pentagon =
new Polygon(5);
for (int j = 0; j < 5; j++)
pentagon.add(new
Point2D.Double(
x +r*Math.cos
(2*Math.PI*j/5),
y+r*Math.sin
(2*Math.PI*j/5)));
PolygonTest.java
import
java.applet.Applet;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.geom.Line2D;
import
java.awt.geom.Point2D
;
public class
PolygonTest extends
Applet
{ public void paint
(Graphics g)
{ Graphics2D g2 =
(Graphics2D) g;
Polygon triangle = new
Polygon(3);
triangle.add(new
Point2D.Double(40,40))
;
triangle.add(new
Point2D.Double
(120,160));
triangle.add(new
Point2D.Double(20,120)
);
double x = 200;
double y = 200;
double r = 50;
Polygon pentagon =
new Polygon(5);
for (int j = 0; j < 5; j++)
{ pentagon.add(new
Point2D.Double(
x+r*Math.cos
(2*Math.PI*j/5),
y+r*Math.sin
(2*Math.PI*j/5)));
}
triangle.draw(g2);
pentagon.draw(g2);
}
}
// define polygon class
class Polygon
{ public Polygon(int n)
{ corners = new
Point2D.Double[n];
cornersSize = 0;
}
public void add
(Point2D.Double p)
{ if (cornersSize <
corners.length)
{ corners[cornersSize]
= p;
cornersSize++;
}
}
public void draw
(Graphics2D g2)
{ for (int j = 0; j <
cornersSize; j++)
{ Point2D.Double from
= corners[j];
Point2D.Double to =
corners[(j+1) %
corners.length];
g2.draw(new
Line2D.Double(from,
to));
// Line2D.Double line =
new Line2D.Double
(from, to);
// g2.draw(line);
}
}
private Point2D.Double
[] corners;
private int cornersSize;
}
Vector
A Vector is a container
of objects that grows
automatically
A Vector can hold
objects of any types
Internally, the Vector
class keeps an Object
[] array.
There is no limit to the
number of elements
that we add.
We can add new
elements at the end of
the vector with the
add method.
However, vectors are
objects of a class and
not arrays so we
cannot use the []
operator to access
vector.
Instead, we use the
set method to write an
element, and the get
method to read an
element.
products.set(0, p);
Example
// Consider
BestProduct.java
Vector products = new
Vector();
boolean done = false;
while (!done)
{ Product p =
readProduct();
if (p == null) // last
product read
done = true;
else
products.add(p); // add
the object to the end
of the vector
}
Vector positions start
at 0.
The number of
elements stored in a
vector is obtained by
the size method.
int n = products.size();
To read an element
from a vector, we use
the get method:
products.get(i) is the
ith element in the
vector products.
However, because the
return type of the get
method is the class
Object, we must cast
the return value of the
get method to the
correct type.
Product p = (Product)
products.get(i);
An example of a loop
that traverses the
elements of a vector.
for (int j = 0; j <
products.size(); j++)
{ Product p = (Product)
products.get(j);
do something with p;
}
We can also insert an
object in the middle of
a vector by using v.add
(j,p) to add the object
p at position j, move
all elements by one
position, from position
j to the last element in
the vector, and
increase the size of
the vector by 1.
The call v.remove(j)
removes the element
at position j, moves all
elements after the
removed element
down by one position,
and reduce the size of
the vector of the
vector by 1.
Since numbers are not
objects in Java, we
cannot have vectors
of numbers.
To store sequence of
integers, floating-point
numbers, or boolean
values, we must use
wrapper classes.
The classes Integer,
Double, and Boolean
wrap numbers and
truth values inside
objects.
These wrapper
objects can be stored
inside vectors.
The Double class is a
number wrapper.
There is a constructor
that makes a Double
object out of a double
value:
Double d = new Double
(29.74);
Conversely, the
doubleVal method
retrieves the double
value that is stored
inside the Double
object.
double x =
d.doubleValue();
To add a number into a
vector, we first
construct a wrapper
object, then add the
object to the vector:
Vector data = new
Vector();
data.add(new Double
(29.95));
To retrieve the
number, we need to
cast the return value
of the get method to
Double, then call the
doubleValue method:
double x = ((Double)
data.get(0))
.doubleValue();
Converting Vectors to
Arrays
The advantage of
vectors is the dynamic
growth since we need
not know the final size
of the vector.
The disadvantage is
the cumbersome
access syntax.
We can convert a
vector to an array with
the copyInfo method:
// read values into a
vector
vector productVector =
new Vector();
boolean done = false;
while (!done)
{ Product p =
readProduct();
if (p == null)
done = true;
else
productVector.add(p);
}
// allocate an array of
the correct size
Product[] products =
new Product
[productVector.size()];
// copy the elements
from the vector to the
array
productVector.
copyInfo(products);
for (int j = 0; j <
products.length; j++)
do something with
products[j];
Two-Dimensional
Arrays
An arrangement
consisting of rows and
columns of values is
called a two-
dimensional array or
matrix.
To construct a 2D
array, we need to
specify how many ros
and columns we need,
for example
int[][] powers = new
int[10][8];
To access a particular
element in the matrix,
we specify two
subscripts in separate
brackets:
powers[3][4] =
Math.pow(2, 4);
Two-Dimensional
Arrays
In Java, 2D arrays are
stored as arrays of
arrays:
int[][] powers = new
int[10][8];
For example, power is
an array of 10 objects,
each of which is an int
[] array of length 8.
The number of rows is:
int nrows =
powers.length;
The number of
columns is:
int ncols = power[0]
.length;
// See example:
Table2.java
Table2.java
public class Table2
{ public static void
main(String[] agrs)
{ final int COL_WIDTH =
10;
int[][] powers = new
int[10][8];
for (int i = 0; i <
powers.length; i++)
for (int j = 0; j <
powers[i].length; j++)
powers[i][j] = (int)
Math.pow(i+1, j+1);
printTable(powers,
COL_WIDTH);
}
public static void
printTable(int[][] table,
int width)
{ for (int i = 0; i <
table.length; i++)
{ for (int j = 0; j < table
[i].length; j++)
{ System.out.print
(format(table[i][j],
width));
}
System.out.println();
}
}
public static String
format(int n, int width)
{ String nstr = “” + n;
while (nstr.length() <
width)
nstr = “ “ + nstr;
return nstr;
}
}
To be able to use
arrays and vectors to
collect objects
To implement partially
filled arrays
To be able to pass
arrays to methods
To learn about
common array
algorithms
To build classes
containing arrays and
vectors
To learn how to use
two-dimensional
arrays
Arrays
An array is a collection
of data items of the
same type.
Every element of the
collection can be
accessed separately
by using index.
In Java, array indexing
starts from 0.
Array can be
constructed using
double[ ] data = new
double[10];
An Array Reference
and an Array
Using Arrays to Store
Data
To access the fifth
element in an array,
we use
data[4] = 35.5;
System.out.println
(“The price = ” + data
[4]);
sum = sum + data[4];
double[ ] data; // error :
not initialized
data[0] = 10.5; // assign
a value to data[0]
Bounds Error
double[ ] data = new
double[10];
// we can access data
[0], data[1], … data[9]
data[10] = 35.5; //
cause
ArrayIndexOutOfBound
sException
Size of the array:
arrayRefName.length
double[ ] data = new
double[10];
for (int i = 0; i <
data.length; i++)
if (data[i] < lowest)
lowest = data[i];
Don’t combine array
access and index
increment
x = v[i++]; => x = v[i]; i+
+;
Array Initialization
int[] primes = new int
[5];
primes[0] = 2; primes
[1] = 3; primes[2] = 5;
primes[3] = 7; primes
[4] = 11;
// better to use the
following
int[ ] primes = {2, 3, 5,
7, 11};
Copying Arrays
Reference
double[] data = new
double[10];
// … fill array
double[] prices;
prices = data; // copy
reference of array
Make a true copy of
array
double[] prices = new
double[data.length];
for (int j = 0; j <
data.length; j++)
prices[j] = data[j];
Better if we use the
static
System.arrayCopy
method
System.arrayCopy
(from, fromStart, to,
toStart, count);
System.arrayCopy
(data, 0, prices, 0,
data.length);
Partially Filled Arrays
When we need to set
the size of an array
before you know how
many elements there
are, we can make an
array that is large
enough and partially fill
it.
final int DATA_LENGTH =
1000;
double[] data = new
double[DATA_LENGTH];
then keep a variable
that tells how many
elements are actually
used
int dataSize = 0; // for
collect size of array
and increment this
variable every time
data is added to the
array
double price =
console.readDouble();
data[dataSize] = price;
dataSize++;
Remember! Not to
access the data over
the datasize
for (int j=0; j <
dataSize; j++) // not
data.length
System.out.println(data
[j]);
and not to overfill the
array
if (dataSize <
data.length) // OK
{ data[dataSize] =
price;
dataSize++;
}
else // Array is full
{ System.out.println
(“Sorry, array is full”);
}
If the array fills up, we
can create a new,
larger array; copy all
elements into the new
array; and then attach
the new array to the
old array variable.
if (dataSize >= data.length)
{ //
double[] newData = new
double[2 * data.length];
// copy data
System.arrayCopy(data,
0, newData, 0,
data.length);
//create new reference for
"new data" ่
data = newData;
}
Suppose we want to
write a program that
reads a set of prices
offered by 10 vendors
for a particular product
and then prints them,
marking the lowest
one.
public class BestPrice
{ public static void
main(String[] args)
{ final int DATA_LENGTH =
1000;
double[] data = new
double[DATA_LENGTH];
int dataSize = 0;
// read data
ConsoleReader
console = new
ConsoleReader
(System.in);
boolean done = false;
while (!done)
{ System.out.println
("Enter price, 0 to
quit:");
double price =
console.readDouble();
if (price == 0) // end of
input
done = true;
else if (dataSize <
data.length)
{ // add price to data
array
data[dataSize] = price;
dataSize++;
}
else // array is full
{ System.out.println
("Sorry, the array is
full.");
done = true;
}
}
// compute lowest
price
if (dataSize == 0)
return; // no data
double lowest = data
[0];
for (int i = 1; i <
dataSize; i++)
if (data[i] < lowest)
lowest = data[i];
// print out prices,
marking the lowest
one
for (int i = 0; i <
dataSize; i++)
{ System.out.print(data
[i]);
if (data[i] == lowest)
System.out.print(" <--
lowest price");
System.out.println();
}
}
}
Array Parameters and
Return Values
The method to
compute the average
of an array of floating-
point numbers
public static double
average(double[] data)
{ if (data.length == 0)
return 0;
double sum = 0;
for (int j = 0; j <
data.length; j++)
sum = sum + data[j];
return sum /
data.length;
}
double[] prices =
{ 10.5, 24.5, 30.5, 10.0,
50.4 };
System.out.println
(“Price average = ” +
average(prices));
Array Parameters
When an array is
passed to a method,
the array parameter
contains a copy of the
reference to the
argument array.
A method can modify
the entries of any array
that you pass to it.
The method can return
an array if the returning
result consists of a
collection of values of
the same type.
A method can return an
array
public static int[]
randomData(int length,
int n)
{ Random generator =
new Random();
int[] data = new int
[length];
for (int j = 0; j <
data.length; j++)
data[j] =
generator.nextInt(n);
return data;
}
Array Algorithms
Finding a Value: want
to know the price of
first product that has
price lower or equal
1000
double [] prices;
double targetPrice = 1000;
int j = 0;
boolean found = false;
while (j < prices.length
&& !found)
{ if (prices[j] <=
targetPrice)
found = true;
else
j++;
}
if (found)
System.out.println(“Item”
+ j + “ has a price of” +
prices[j]);
Counting: want to
know the number of
product that has price
lower or equal 1000
double [] prices;
double targetPrice = 1000;
int count = 0;
for (int j = 0; j <
prices.length; j++)
{ if (prices[j] <=
targetPrice)
count++;
}
System.out.println(count
+ “ computers.”);
Removing an Element
from an Unordered
Array
If we want to remove
an element from an
unordered array, we
simply overwrite the
element to be
removed with the last
element of the array.
However, an array
cannot be shrunk to
get rid of the last
element, so we have
to used the technique
of a partially filled
array.
Removing an Element :
Array not sorted
public class Remove1
{ public static void
main(String[] args)
{ ConsoleReader
console = new
ConsoleReader
(System.in);
String[] staff = new
String[5];
staff[0] = "Harry"; staff
[1] = "Romeo"; staff[2]
= "Dick";
staff[3] = "Juliet"; staff
[4] = "Tom";
int staffSize =
staff.length;
print(staff, staffSize);
System.out.println
("Remove which
element? (0 - 4)");
int pos =
console.readInt();
// overwrite the
removed element with
the last element
staff[pos] = staff
[staffSize - 1];
staffSize--;
print(staff, staffSize);
}
/**
Prints an array of
strings
@param s the string
array
@param sSize the
number of strings in
the array
*/
public static void print
(String[] s, int sSize)
{ for (int i = 0; i < sSize;
i++)
System.out.println(i + ":
" + s[i]);
}
}
Remove an Element
from an Ordered Array
After removing an
element, we have to
move all elements
beyond the element to
be removed by one
slot.
Removing from an
Ordered Array
public class Remove2
{ public static void
main(String[] args)
{ ...
int staffSize =
staff.length;
print(staff, staffSize);
System.out.println
("Remove which
element? (0 - 4)");
int pos =
console.readInt();
// shift all elements
above pos down
for (int i = pos; i <
staffSize - 1; i++)
staff[i] = staff[i + 1];
staffSize--;
print(staff, staffSize);
}
}
Inserting an Element
Suppose we want to
insert an element in
the middle of an array,
we must move all
elements beyond the
insertion location by
one slot.
When we insert an
element, we start
moving at the end of
an array, move that
element, then go to
the one before it. This
operation is in reverse
order from removing
operation.
public class Insert
{ public static void
main(String[] args)
{ String[] staff = new
String[6];
staff[0] = “Mary”;
staff[1] = “Ben”;
staff[2] = “Sandy”;
staff[3] = “Janet”;
staff[4] = “Peter”;
int staffSize =
staff.length - 1;
System.out.print
("Insert before which
element? (0 - 4) ");
int pos =
console.readInt();
// shift all element
after pos up by one
for (int i = staffSize; i >
pos; i--)
staff[i] = staff[i - 1];
// insert new element
into freed slot
staff[pos] = "New,
Nina";
staffSize++;
print(staff, staffSize);
}
Parallel Arrays, Arrays
of Objects
and Array as Object
Data
When we want to
analyze a data set that
contains more than
one data item, such as
a data set that contains
the names, prices, and
quality of a collection
of products.
We want to look for
products that give
good quality at low
price.
The problem with this
program is that it
contains three arrays
of the same length.
Bestdata.java
public class BestData
{ public static void
main(String[] args)
{ final int DATA_MAX =
500;
String[] name = new
String[DATA_MAX];
double[] price = new
double[DATA_MAX];
int[] score = new int
[DATA_MAX];
int Size = 0;
// read data from
console
ConsoleReader
console = new
ConsoleReader
(System.in);
Boolean done = false;
while (!done)
{ System.out.println
(“Enter name or leave
blank when done: ”);
String inLine =
console.readLine();
if (inLine == null ||
inLine.equals(“ ”))
done = true;
else if (Size < DATA_
MAX)
{ name[Size] = inLine;
System.out.println
(“Enter price: “);
inLine =
console.readLine();
price[Size] =
Double.parseDouble
(inLine);
System.out.println
(“Enter score: “);
inLine =
console.readLine();
score[Size] =
Integer.parseInt
(inLine);
Size++;
}
else
{ System.out.println
(“Sorry, the array is
full. “);
done = true;
}
}
// compute best buy
if (Size == 0) return; //
no data
double best = score[0]
/ price[0];
for (int j = 0; j < Size; j+
+)
if (score[j] / price[j] >
best)
best = score[j] / price
[j];
final int COLUMN_WIDTH
= 30;
for (int j = 0; j < Size; j+
+)
{ System.out.print
(name[j]);
int pad = COLUMN_
WIDTH - name[j].length();
for (int k = 1; k < = pad; k+
+)
System.out.print(“ “);
System.out.print(“ $” +
price[j] +“ score = “ +
score[j]);
if (score[j] / price[j] ==
best)
System.out.print(“ <--
best buy “);
System.out.println();
}
}
}
Parallel Arrays
The problem with
Bestdata.java is that it
contains three parallel
arrays namely name[j],
price[j], and score[].
We must ensure that
the arrays always have
the same length and
that each slice is filled
with values that
actually belong
together.
We can solve this
problem by turning this
concept into class.
Product.java
class Product
{
private String name;
private double price;
private int score;
public Product(String n,
double p, int s)
{ name = n;
price = p;
score = s;
}
public String getName
()
{ return name;
}
public double getPrice
()
{ return price;
}
public int getScore()
{ return score;
}
}
Bestproduct.java
public class
BestProduct
{ public static void
main(String[] args)
{ final int DATA_MAX =
500;
Product[] data = new
Product[DATA_MAX];
int Size = 0;
// read data from
console
ConsoleReader
console = new
ConsoleReader
(System.in);
Boolean done = false;
while (!done)
{ Product p =
readProduct(console);
if (p == null)
done = true;
else if (Size < DATA_
MAX)
{ data[Size] = p;
Size++;
}
else
{
System.out.println
(“Sorry, the array is
full.”);
done = true;
}
}
// compute best buy
if (Size == 0) return; //
no data
double best = data[0]
.getScore() / data[0]
.getPrice();
for (int j = 1; j < Size; j+
+)
{ double ratio = data[j]
.getScore() / data[j]
.getPrice();
if (ratio > best)
best = ratio;
}
// print out data
for (int j = 0; j < Size; j+
+)
{ printProduct(data[j]);
if (data[j].getScore() /
data[j].getPrice() ==
best)
System.out.print(“ <--
best buy “);
}
}
public static Product
readProduct
(ConsoleReader in)
{ System.out.println
(“Enter name or leave
blank when done:”);
String name =
in.readLine();
if (name == null ||
name.equals(“ “))
return null;
System.out.println
(“Enter price: “);
String inLine =
in.readLine();
double price =
Double.parseDouble
(inLine);
System.out.println
(“Enter score: “);
inLine = in.readLine();
int score =
Integer.parseInt
(inLine);
return new Product
(name, price, score);
}
public static void
printProduct(Product p)
{ final int WIDTH = 30;
System.out.print
(p.getName());
int pad = WIDTH –
p.getName().length;
for (int j = 1; j <= pad; j+
+)
System.out.print(“ “);
System.out.print(“ $” +
p.getPrice() + “ score =
“ +
p.getScore());
}
}
Example:
PolygonTest.java
Arrays as Object Data
import
java.applet.Applet;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.geom.Line2D;
import
java.awt.geom,Point2D
;
public class
PolygonTest extends
Applet
{ public void paint
(Graphics g)
{ Graphics2D g2 =
(Grahpics2D) g;
Polygon triangle = new
Polygon(3);
triangle.add(new
Point2D.Double(40, 40)
);
triangle.add(new
Point2D.Double(120,
160));
triangle.add( new
Point2D.Double(20,
120));
double x = 200;
double y = 200;
double r = 50;
Polygon pentagon =
new Polygon(5);
for (int j = 0; j < 5; j++)
pentagon.add(new
Point2D.Double(
x + r * Math.cos(2 *
Math.PI * j / 5),
y + r * Math.sin(2 *
Math.PI * j / 5)));
triangle.draw(g2);
pentagon.draw(g2);
}
}
// Polygon class
definition
class Polygon
{ public polygon(int n)
{ corners = new
Point2D.Double[n];
cornerCount = 0;
}
public void add
(Point2D.Double p)
{ if (cornerCount <
corners.length)
{ corners[cornerCount]
= p;
cornerCount++;
}
}
public void draw
(Graphics2D g2)
{ for (int j = 0; j <
cornerCount; j++)
{ Point2D.Double from
= corners[j];
Point2D.Double to =
corners[(j+1) %
corners.length];
g2.draw(new
Line2D.Double(from, to);
}
}
private Point2D.Double
[] corners;
private int
cornerCount;
}
Example
To generate a triangle,
we can use the
following segment of
code:
Polygon triangle = new
Polygon(3);
triangle.add(new
Point2D.Double(40,40))
;
triangle.add(new
Point2D.Double
(120,160));
triangle.add(new
Point2D.Double(20,120)
);
Another kind of
geometric shape is
called a regular
polygon which has all
sides of the same
length.
A regular n-gon with
center (x,y) and radius
r has n corners, c0, c1,
…, cn-1, where
ci = (x+r•cos(2p i/n), y
+r•sin(2p i/n))
A regular polygon can
be generated by:
Polygon pentagon =
new Polygon(5);
for (int j = 0; j < 5; j++)
pentagon.add(new
Point2D.Double(
x +r*Math.cos
(2*Math.PI*j/5),
y+r*Math.sin
(2*Math.PI*j/5)));
PolygonTest.java
import
java.applet.Applet;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.geom.Line2D;
import
java.awt.geom.Point2D
;
public class
PolygonTest extends
Applet
{ public void paint
(Graphics g)
{ Graphics2D g2 =
(Graphics2D) g;
Polygon triangle = new
Polygon(3);
triangle.add(new
Point2D.Double(40,40))
;
triangle.add(new
Point2D.Double
(120,160));
triangle.add(new
Point2D.Double(20,120)
);
double x = 200;
double y = 200;
double r = 50;
Polygon pentagon =
new Polygon(5);
for (int j = 0; j < 5; j++)
{ pentagon.add(new
Point2D.Double(
x+r*Math.cos
(2*Math.PI*j/5),
y+r*Math.sin
(2*Math.PI*j/5)));
}
triangle.draw(g2);
pentagon.draw(g2);
}
}
// define polygon class
class Polygon
{ public Polygon(int n)
{ corners = new
Point2D.Double[n];
cornersSize = 0;
}
public void add
(Point2D.Double p)
{ if (cornersSize <
corners.length)
{ corners[cornersSize]
= p;
cornersSize++;
}
}
public void draw
(Graphics2D g2)
{ for (int j = 0; j <
cornersSize; j++)
{ Point2D.Double from
= corners[j];
Point2D.Double to =
corners[(j+1) %
corners.length];
g2.draw(new
Line2D.Double(from,
to));
// Line2D.Double line =
new Line2D.Double
(from, to);
// g2.draw(line);
}
}
private Point2D.Double
[] corners;
private int cornersSize;
}
Vector
A Vector is a container
of objects that grows
automatically
A Vector can hold
objects of any types
Internally, the Vector
class keeps an Object
[] array.
There is no limit to the
number of elements
that we add.
We can add new
elements at the end of
the vector with the
add method.
However, vectors are
objects of a class and
not arrays so we
cannot use the []
operator to access
vector.
Instead, we use the
set method to write an
element, and the get
method to read an
element.
products.set(0, p);
Example
// Consider
BestProduct.java
Vector products = new
Vector();
boolean done = false;
while (!done)
{ Product p =
readProduct();
if (p == null) // last
product read
done = true;
else
products.add(p); // add
the object to the end
of the vector
}
Vector positions start
at 0.
The number of
elements stored in a
vector is obtained by
the size method.
int n = products.size();
To read an element
from a vector, we use
the get method:
products.get(i) is the
ith element in the
vector products.
However, because the
return type of the get
method is the class
Object, we must cast
the return value of the
get method to the
correct type.
Product p = (Product)
products.get(i);
An example of a loop
that traverses the
elements of a vector.
for (int j = 0; j <
products.size(); j++)
{ Product p = (Product)
products.get(j);
do something with p;
}
We can also insert an
object in the middle of
a vector by using v.add
(j,p) to add the object
p at position j, move
all elements by one
position, from position
j to the last element in
the vector, and
increase the size of
the vector by 1.
The call v.remove(j)
removes the element
at position j, moves all
elements after the
removed element
down by one position,
and reduce the size of
the vector of the
vector by 1.
Since numbers are not
objects in Java, we
cannot have vectors
of numbers.
To store sequence of
integers, floating-point
numbers, or boolean
values, we must use
wrapper classes.
The classes Integer,
Double, and Boolean
wrap numbers and
truth values inside
objects.
These wrapper
objects can be stored
inside vectors.
The Double class is a
number wrapper.
There is a constructor
that makes a Double
object out of a double
value:
Double d = new Double
(29.74);
Conversely, the
doubleVal method
retrieves the double
value that is stored
inside the Double
object.
double x =
d.doubleValue();
To add a number into a
vector, we first
construct a wrapper
object, then add the
object to the vector:
Vector data = new
Vector();
data.add(new Double
(29.95));
To retrieve the
number, we need to
cast the return value
of the get method to
Double, then call the
doubleValue method:
double x = ((Double)
data.get(0))
.doubleValue();
Converting Vectors to
Arrays
The advantage of
vectors is the dynamic
growth since we need
not know the final size
of the vector.
The disadvantage is
the cumbersome
access syntax.
We can convert a
vector to an array with
the copyInfo method:
// read values into a
vector
vector productVector =
new Vector();
boolean done = false;
while (!done)
{ Product p =
readProduct();
if (p == null)
done = true;
else
productVector.add(p);
}
// allocate an array of
the correct size
Product[] products =
new Product
[productVector.size()];
// copy the elements
from the vector to the
array
productVector.
copyInfo(products);
for (int j = 0; j <
products.length; j++)
do something with
products[j];
Two-Dimensional
Arrays
An arrangement
consisting of rows and
columns of values is
called a two-
dimensional array or
matrix.
To construct a 2D
array, we need to
specify how many ros
and columns we need,
for example
int[][] powers = new
int[10][8];
To access a particular
element in the matrix,
we specify two
subscripts in separate
brackets:
powers[3][4] =
Math.pow(2, 4);
Two-Dimensional
Arrays
In Java, 2D arrays are
stored as arrays of
arrays:
int[][] powers = new
int[10][8];
For example, power is
an array of 10 objects,
each of which is an int
[] array of length 8.
The number of rows is:
int nrows =
powers.length;
The number of
columns is:
int ncols = power[0]
.length;
// See example:
Table2.java
Table2.java
public class Table2
{ public static void
main(String[] agrs)
{ final int COL_WIDTH =
10;
int[][] powers = new
int[10][8];
for (int i = 0; i <
powers.length; i++)
for (int j = 0; j <
powers[i].length; j++)
powers[i][j] = (int)
Math.pow(i+1, j+1);
printTable(powers,
COL_WIDTH);
}
public static void
printTable(int[][] table,
int width)
{ for (int i = 0; i <
table.length; i++)
{ for (int j = 0; j < table
[i].length; j++)
{ System.out.print
(format(table[i][j],
width));
}
System.out.println();
}
}
public static String
format(int n, int width)
{ String nstr = “” + n;
while (nstr.length() <
width)
nstr = “ “ + nstr;
return nstr;
}
}
Labels:
learn java language online
Subscribe to:
Posts (Atom)