Professional Documents
Culture Documents
Exception Handling
Exception Handling
Exception Handling
In Java, the parent class dealing with errors and exceptions is Throwable, which has two major subclasses: Exception and Error. The Error class defines the internal system errors; whereas the Exception class defines the errors caused by the program. In general, the exception handling is to account for and manage the possible run-time errors which may be raised by the program. For each block of statements that may raise an exception, an exception handling routine should be coded. The basic syntax is: try { ........statements that may throw exceptions.... } // try catch(Exception-type1 identifier) { ........exception handling statements...... } // catch catch (Exception-type2 identifier) { ........exception handling statements...... } // catch The primary operations involved in handling exceptions include: 1. Declare an exception -- using the key word throws; for example, public static void main(String []x) throws IOException 1. 1. Throw an exception -- using the operator throw; for example, throw new Exception(......); Catch and handle an exception -- using the try and catch block or blocks. try { .........} catch(Exception1........) { ......} catch(Exception2.......) {......}
The Exception class (java.lang.Throwable) provides a set of methods to assist in the exception handling. Listed below are some of them, followed by a program example. Method: public String getMessage() Description: Returns the detailed error or exception message. Method: Exception: public String toString() Returns a brief description of the exception
-2-
Catch1.java // Handling exception import java.io.*; public class Catch1 { public static void main(String []x) throws IOException { int k = 12; int n = 0; try { // try to divide a number by 0; System.out.println(k / n); System.out.println(k + n); } // try catch (Exception e) { System.err.println(e.toString()); System.err.println(e.getMessage()); } // catch System.out.println(Press any key to exit.); System.in.read(); } // main } // Catch1 The output produced by the above program is: java.lang.ArithmeticException: / by zero / by zero
-3-
+-- InputStream
+-- OutputStream
+-- RandonAccessFile
To support stream operations, Java has several interfaces, including top level interfaces DataInput, DataOutput, and sub-level interfaces ObjectInput, and ObjectOutput. For example, the java.io.RandomAccessFile is derived directly rom java.lang.Object and implements the DataInput and DataOutput interfaces in order to support both reading and writing to a random access file. A random access file is treated as a large array of bytes stored in the file system. The java.io.Reader and java.io.Writer classes, introduced by JDK 1.1, are the two top character stream classes. In addition, the java.io.StreamTokenizer class takes an input stream and parses it into "tokens", allowing the tokens to be read one at a time. The inheritance trees are: java.lang.Object java.lang.Object | | +--java.io.Reader +--java.io.Writer java.lang.Object | +-- java.io.StreamTokenizer
Java provides a rich set of stream classes for input and output operations. All these stream classes are defined in java.io package and are categorized into byte streams and character streams. The InputStream and OutputStream are the top classes of all byte stream subclasses. Reader and Writer classes are the top classes for character stream subclasses. The overall inheritance hierarchies are shown below. Byte Stream Classes java.io.InputStream | +-- ByteArrayInputStream +-- FileInputStream +-- FilterInputStream | +-- BufferedInputStream +-- DataInputStream ------------------------------------->>>> DataInput +-- LineNumberInputStream +-- PushBackInputStream +-- ObjectInputStream --------------------------------------->>>> ObjectInput +-- PipeInputStream +-- SequenceInputStream +-- StringBufferInputStream java.io.OutputStream | +-- ByteArrayOutputStream +-- FileOutputStream
-4-
+--
FilterOutputStream | +-- BufferedOutputStream +-- DataOutputStream ------------------------------------->>>> DataOutput +-- PrintStream +-- ObjectOutputStream -------------------------------------------->>>> ObjectOutput +-- PipeOutputStream
Character Stream Classes java.io.Reader | +-- CharacterArrayReader +-- InputStreamReader | +-- FileReader +-- FilterReader | +-- PushBackReader +-- StringReader +-- PipeReader +-- BufferedReader | +-- LineNumberReader java.io.Writer | +-- BufferedWriter +-- CharacterArrayWriter +-- OutputStreamWriter | +-- FileWriter +-- FilterWriter +-- PipeWriter +-- PrintWriter +-- StringWriter java.io.File Class In addition to byte stream classes and character stream classes, the java.io.File class (derived directly from java.lang.Object class) presents an abstract, system-independent view of hierarchical pathnames. Hence, it is an abstract representation of file and directory pathnames. An abstract pathname has two components: 1. An optional system-dependent prefix string, such as a disk-drive specifier, "/" for the UNIX root directory, or "\\" for a Win32 pathname. 2. A sequence of zero or more string names. A pathname can be either an absolute path or a relative path to the currently directory.
-5-
In summary, the java.io package contains a collection of stream classes that support these algorithms for reading and writing streams. These classes are divided into two class hierarchies based on the data type (either characters or bytes) on which they operate, as depicted by the below figure. Other stream classes are also present to support special stream operations, such as the RandomAccessFile class. A collection of interfaces are also found in the java.io package in order to standardize and ease file stream manipulations, such as file/directory path and object serialization. Reading File Streams All file streams are automatically opened when created. You can close any stream explicitly by calling its close() method. Otherwise, the garbage collector can implicitly close it. The character and byte streams are all sequential access streams. In JDK 1.0, DataInputStream along with FileInputStream is to read from a file -- these classes will raise a deprecation warning message. DataInputStream myfile = new DataInputStream(new FileInputStream(file-path)); To eliminate the deprecation message, the BufferedReader and FileReader are recommended: BufferedReader infile = new BufferedReader(new FileReader(file-path)); The next program demonstrates how a file is opened for reading. It reads a line from the input file and displays counters for lowercase letters, uppercase letter, and spaces found in the input line. // CountChar.java import java.io.*; /** * This program demonstrates: File input manipulation. * It counts and display the number of lowercase letters, the number of uppercase letters, and * the number of spaces on each input line. A line number is also displayed. * @author: Andy Yao, Ph.D. */ public class CountChar { /** * Sequential file access. * If DataInputStream along with FileInputStream is used, a deprecation message will be raised. * BufferedReader along with FileReader should be used to process the input file. * The input file is the program source code itself. * @param upper is the counter for uppercase letters. * @param lower is the counter for uppercase letters. * @param spaces is the counter for uppercase letters. * @param linecnt keeps track of the line number. */ public static void main(String []args) throws IOException { // DataInputStream infile; // deprecated BufferedReader infile;
-6-
String line; int upper, lower, spaces, k, linecnt = 0; char c; try { //infile = new DataInputStream(new FileInputStream("employee.java")); // deprecated infile = new BufferedReader(new FileReader("employee.java")); // Call readLine() method to read a line from the input file stream. while ((line = infile.readLine()) != null) // A null denotes the end of file condition is true. { ++linecnt; // The line count upper = lower = spaces = 0; for (k=0; k < line.length(); ++k) // Step through the input line, character by character. { c = line.charAt(k); // get one character if ((c >= 'a') && c <= 'z')) // Lowercase letter? ++lower; else if ((c >= 'A') && c <= 'Z')) // Uppercase letter? ++upper; else if (c == ' ') // A space character? ++spaces; } // for // Display the output System.out.println(linecnt + ": " + line); System.out.print("Uppercase: " + upper); System.out.print(" Lowercase: " + lower); System.out.println(" Spaces: " + spaces); } // while not end of file infile.close(); // close the input file stream } // try catch (IOException x) { System.err.println(x.toString()); System.exit(1); } // catch } // main } // CountChar The output generated by the above program is: 1: Lynda 38726.89 Clerk Uppercase: 2 Lowercase: 8 Spaces: 4 2: Eva 49287.52 Programmer Uppercase: 2 Lowercase: 11 Spaces: 7 3: Ava 38929.20 Analyst
-7-
Uppercase: 2 Lowercase: 8 Spaces: 7 4: Dave 38729.15 Programmer Uppercase: 2 Lowercase: 12 Spaces: 6 The next program shows how to process input file stream and use StringTokenizer to split a line into fields. Fields of the input lines are separated by spaces. It is a common practice that an input file is processed as a sequence of bytes and read into a String object on line by line basis. The input String then is processed using other utilities, such as StringTokenizer. // ReadFile.java import java.io.*; import java.util.*; /** * Demonstrates the file reading operation and the use of StringTokenizer. * Reads the data file line by line and parses each line into tokens (fields). * These tokens are saved in three separate String arrays. * This program also calculates and displays the total salary of all employees. * Assumption: The input file is located under the current working directory. * Listed below are two sample input lines: * Wesley 49287.52 Programmer * Addison 38929.20 Analyst * @author: Andy Yao, Ph.D. */ public class ReadFile { static BufferedReader infile; static String line; static int k = 0; static String []name = new String[100]; static String []title = new String[100]; static double []salary = new double[100]; public static void main(String []args) { try { infile = new BufferedReader(new FileReader("employee.dat")); while ((line = infile.readLine()) != null) { saveRec(line, k); // save the input line into 3 arrays. ++k; } // while infile.close(); } // try catch (IOException x) { System.err.println(x.toString());
-8-
System.exit(1); } // catch printRec(k); // Print the output } // main // Using StringTokenizer to split a line into fields. static void saveRec(String s, int k) { // Split the line into fields. StringTokenizer record = new StringTokenizer(s); name[k] = record.nextToken(); // name // Covert the salary in String into double. salary[k] = Double.valueOf(record.nextToken()).doubleValue(); title[k] = record.nextToken(); } // saveRec // Displays the records and the total salary for all employees. static void printRec(int size) { double total = 0; for (int k=0; k < size; ++k) { total = total + salary[k]; System.out.println(name[k] + " " + title[k] + " " + salary[k]); } System.out.println("Total salary: " + total); } // printRec } // ReadFile
The output generated by the above program, ReadFile.java, is: Lynda Clerk 38726.89 Eva Programmer 49287.52 Ava Analyst 38929.2 Dave Programmer 38729.15 Total salary: 165672.76 The above program, ReadFile.java, uses the java.io.BufferedReader class to process an input file. The java.io.BufferedReader is directly derived form the java.io.Reader abstract class: +--java.io.Reader | +--java.io.BufferedReader
-9-
The java.io.BufferedReader has two constructors: BufferedReader(Reader in); // Create a buffering character-input stream that uses a default-sized input buffer. BufferedReader(Reader in, int size); // Create a buffering character-input stream that uses an input buffer of the specified size. Listed below are some of the commonly used java.io.BufferedReader methods: int read() ; // Read a single character. Return -1 at the end of the stream. String readLine(); // Read a line of text. Return a null at the end of the file. long skip(long n);// Skip n characters. boolean ready(); // Indicates whether this stream is ready to be read. The parameter in to the BufferedReader constructors can be an object created from any subclass of the java.io.Reader class. In a general practice, we need a class which takes either a file path or file descriptor to create a Reader stream object. The java.io.FileReader (see inheritance tree below) has the following three constructors and can be used for such a purpose: FileReader(String filepath); FileReader(File file); FileReader(FileDescriptor fd); java.io.Reader | +--java.io.InputStreamReader | +--java.io.FileReader
The general statements used to process a file stream using the java.io.BufferedReader are: BufferedReader infile = new BufferedReader(new FileReader(.........)); String line; while ((line = infile.readLine()) != null) // read a line ................... or BufferedReader infile = new BufferedReader(new FileReader(.........)); int c; while ((c = infile.read()) != -1) // read a character ................... The next program uses the read() method to process the input file one character at a time. It is essential that the value returned by the read() method must be converted into a character for output. Replacing Statement A by String line; and Statement B by while ((line = infile.readLine()) != null) System.out.println(line); will achieve the same goal and produce the same output.
- 10 -
ReadFile2.java import java.io.*; /* read() returns the integer equivalence of /** the input character or -1 when the end of * Demonstrates the file reading operation. file stream is reached. */ * This program displays a file by reads // Statement B * one character at a time. while ((c = infile.read()) != -1) */ // covert to a character for output public class ReadFile2 { System.out.print((char) c); public static void main(String []args) infile.close(); { } // try BufferedReader infile; catch (IOException x) int c; // Statement A { try System.err.println(x.toString()); { System.exit(1); infile = new BufferedReader } // catch (new FileReader("employee.dat")); } // main } // ReadFile2
Writing File Streams To process an output file stream (writing to a file), we, again, have many options. Presented in this section are two different options. The first option is to use the java.io.BufferedWriter and java.io.FileWriter classes. WriteFile.java
- 11 -
// Writing to a file stream using BufferedWriter and FileWriter classes. import java.io.*; public class WriteFile { public static void main(String []args) { BufferedWriter outfile; // Line 5 try { outfile = new BufferedWriter(new FileWriter("output1.dat")); // Line 8 outfile.write("Hello, world!"); // Line 9 outfile.close(); } // try catch (IOException x) { System.err.println("Error: " + x.toString()); System.exit(1); } // catch } // main } // WriteFile The second option is to use the java.io.DataOutputStream and java.io.FileOutputStream, and java.io.File classes; i.e., modifying the above to replace Line 5 by DataOutputStream outfile; Line 8 by DataOutputStream outfile = new DataOutputStream( new FileOutputStream(new File("output2.dat"))); and Line 9 by outfile.writeBytes(Hello, world!); The java.io.BufferedWriter class is derived directly from the java.io.Writer abstract class and has two constructors: // Create a buffered character output stream that uses a buffer of the default size. BufferedWriter(Writer out); // Create a buffered character output stream, using an output buffer of the given size. BufferedWriter(Writer out, int size); The parameter out can be an object created from any subclass of the java.io.Writer class. In a general practice, an object is created from the java.io.BufferedWriter file for this purpose. Three commonly used java.io.BufferedWriter methods are listed below. void write(int c); // Write a single character. void write(String str); // Write a string. void write(char[] buf); // Write an array of characters. The inheritance tree and constructors for the java.io.FileWriter class are listed below. FileWriter(String filepath); java.io.Writer FileWriter(File file); | FileWriter(FileDescriptor fd); +--java.io.OutputStreamWriter
- 12 -
| +--java.io.FileWriter
If one of the first three constructors is used to create a FileWriter file stream object, a new file is created, truncating the original file contents if the file exists. The last constructor may be used to set the append flag to true, if it is so desired to keep the original file contents and to append to the end of the existing file, as demonstrated by the following program, AppendFile.java. AppendFile.java
// Appending to an existing file stream. import java.io.*; public class AppendFile { public static void main(String []args) { BufferedWriter outfile; try { // open a file for appending outfile = new BufferedWriter (new FileWriter("output1.dat", true)); outfile.write("A new line"); outfile.close(); } // try catch (IOException x) { System.err.println(x.toString()); System.exit(1); } // catch } // main } // AppendFile
The following Java standalone application, SaveFile.java, saves the text lines entered by the user in a TextArea object into a file stream. This program uses a Frame object as its container to hold GUI objects. Generally, an applet is a subclass derived from either the java.applet.Applet class or java.swing.JApplet class; whereas a standalone application uses a Frame or JFrame class to create a top-level container. // SaveFile.java import java.awt.*; import java.awt.event.*; import java.io.*; /** * This GUI based program saves the text entered in a TextArea to a file. * The output file is under the current working directory. * This is a standalone application, using Frame as the GUI container. */ public class SaveFile extends Frame implements ActionListener { TextArea inarea; Button save; public static void main(String []args) { SaveFile x = new SaveFile(); // create an instance x.setSize(200, 250); // set the Frame size x.setVisible(true); // make the Frame show } // main
- 13 -
/* * Tasks of the constructor are: * sets up the frame title and Layout manager * creates a Button and a TextArea * attach GUI objects to event listener * adds the GUI objects to the Frame **/ public SaveFile() { // constructor setTitle("File Output"); // set the Frame title setLayout(new GridLayout(2, 1)); inarea = new TextArea(10, 30); add(inarea); save = new Button("Save"); save.addActionListener(this); // Register to the event listener add(save); } // constructor public void actionPerformed(ActionEvent e) { if (e.getSource() == save) { try { // Statement A BufferedWriter outfile = new BufferedWriter(new FileWriter("output.dat")); // Statement B // BufferedWriter outfile = new BufferedWriter(new FileWriter("output.dat", true)); outfile.write(inarea.getText()); outfile.close(); inarea.setText(""); } // try catch (IOException x) { System.err.println(x.toString()); System.exit(1); } // catch } // if } // actionPerformed } // SaveFile The Statement A can be replaced by the Statement B, if the original file contents need to be saved; thus, appending to the file.
// BothIO.java
- 14 -
// This program combines both input and output operations. import java.awt.*; import java.awt.event.*; import java.io.*; /** * This GUI based program saves the text entered in a TextArea to a file. * It also reads from a text file into a second TextArea, using * BufferedReader and FileReader classes. * The input and output file are under the current working directory. * This is a standalone application, using Frame as the GUI container. */ public class BothIO extends Frame implements ActionListener { TextArea inarea, outarea; Button save, read; /* * Tasks of the constructor are: * sets up the frame title and Layout manager * creates Buttons and TextAreas * attach GUI objects to event listener * adds the GUI objects to the Frame **/ public BothIO() // constructor { setTitle("File Output"); // set the Frame title setLayout(new GridLayout(2, 2)); inarea = new TextArea(10, 30); add(inarea); save = new Button("Save"); save.addActionListener(this); // Register to the event listener add(save); outarea = new TextArea(10, 30); outarea.setEditable(false); // output only add(outarea); read = new Button("Read"); read.addActionListener(this); // Register to the event listener add(read); } // constructor public static void main(String []args) { BothIO x = new BothIO(); // create an instance x.setSize(200, 250); // set the Frame size x.setVisible(true); // make the Frame show
- 15 -
} // main public void actionPerformed(ActionEvent e) { String line; if (e.getSource() == save) { try { // write to a text file FileOutputStream os = new FileOutputStream(new File("myOutput.dat")); DataOutputStream outfile = new DataOutputStream(os); outfile.writeBytes(inarea.getText()); outfile.close(); inarea.setText(""); } // try catch (IOException x) { System.err.println("Error: " + x.toString()); System.exit(1); } // catch } // if save if (e.getSource() == read) { try { // Read from a text file BufferedReader infile = new BufferedReader(new FileReader("myOutput.dat")); while ((line = infile.readLine()) != null) outarea.append(line + "\n"); infile.close(); } // try catch (IOException x) { System.err.println("Error: " + x.toString()); System.exit(1); } // catch } // if read } // actionPerformed } // BothIO
- 16 -
system. There is a kind of cursor, or index into the implied array, called the file pointer. Input operations read bytes starting at the file pointer and advance the file pointer past the bytes read. If the random access file is created in read/write mode, then output operations are also available. Output operations write bytes starting at the file pointer and advance the file pointer past the bytes written. Output operations that write past the current end of the implied array cause the array to be extended. The file pointer can be read by the getFilePointer() method and moved/set by the seek() method. The java.io.RandomAccessFile class implements both the DataInput and DataOutput interfaces and therefore can be used for both reading and writing. When creating an instance of the java.io.RandomAccessFile class, you must indicate whether you wish to read from the file, write to the file, or both. For example, the following statement opens a random file for reading new RandomAccessFile("myfile.txt", "r"); The next statement opens the a file for both reading and writing: new RandomAccessFile("myfile.txt", "rw"); After the file is opened, you can use the readXXX or writeXXX methods to perform I/O on the file. The file pointer indicates the current cursor or index location in the file. When the file is first created, the file pointer is 0, indicating the beginning of the file. Calls to the readXXX and writeXXX methods adjust the file pointer by the number of bytes read or written. The RandomAccessFile class provides three methods for explicitly manipulating the file pointer. skipBytes seek getFilePointer Moves the file pointer forward the specified number of bytes. Positions the file pointer just before the specified byte. Returns the current byte location of the file pointer.
To randomly access to a file, the RandomAccessFile class is used. This class provides the methods for reading and writing a file at a specific location. To move the read/write header (pointer), the seek() method is used. A random-access file can be opened either for read-only or for read/write. The next program Randomio.java illustrates the use of the RandonAccessFile class and its methods to manipulate the file contents in a random fashion. Method: public RandomAccessFile(String filename, String access-mode) filename the file path access-mode: r for reading; rw for read/write. Method: Method: Method: public long length() public void seek(long where) writeBytes(String s) Description: This method returns the size of the file in byte (long). Description: It moves the read/write pointer to where. Description: This method writes s to the file.
- 17 -
Method:
readLine()
Description: This method reads a line from the file and returns the line as a String. It returns a null when the end of file condition becomes true. Method: public void close() Description: This method is used to close the file. The next program (RandomIO.java), displays the following lines on the monitor. Hello, world Hello, A New line ------- Final result ---Hello, A New line How wonderful Hence, the file Random.DAT will contain two lines: Hello, A New line How wonderful // RandomIO.java // Random file I/O operations import java.io.*; public class RandomIO { static String filename = "A:Random.DAT"; public static void main(String []args) { try { append("Hello, world\n"); showit(); update(7, "A New line\n"); showit(); append("How wonderful"); System.out.println("---- Final result ----"); showit(); } // try catch(Exception x)
- 18 -
{ System.err.println(x.toString()); System.exit(1); } // catch } // main static void append(String msg) throws IOException { // append "msg" to the end of the file RandomAccessFile myfile; try { myfile = new RandomAccessFile (new File(filename), "rw"); myfile.seek(myfile.length()); myfile.writeBytes(msg); myfile.close(); } // try catch (Exception x) { System.err.println(x.toString()); System.exit(1); } } // append static void update(long where, String s) throws IOException { // Write "s" to the file at "where" RandomAccessFile myfile; try { myfile = new RandomAccessFile (new File(filename), "rw"); if (where > myfile.length()) where = myfile.length();
- 19 -
myfile.seek(where); myfile.writeBytes(s); myfile.close(); } // try catch (Exception x) { System.err.println(x.toString()); System.exit(1); } } // insert static void showit() throws IOException { // Displays the file contents RandomAccessFile myfile; try { String line; myfile = new RandomAccessFile (new File(filename), "r"); while ((line = myfile.readLine()) != null) System.out.println(line); myfile.close(); } // try catch (Exception x) { System.err.println(x.toString()); System.exit(1); } } // showit } // RandomIO
- 20 -
The conversion of a path name string to or from an abstract path name is system-dependent. When an abstract path name is converted into a path name string, each name is separated from the next by a single copy of the default separator character. The default name-separator character is defined by the system property file separator. When a path name string is converted into an abstract path name, the names within it may be separated by the default name-separator character or by any other name-separator character that is supported by the underlying system. A path name, whether abstract or in string form, may be either absolute or relative. An absolute path name is complete in that no other information is required in order to locate the file that it denotes. A relative path name, in contrast, must be interpreted in terms of information taken from some other path name. By default the classes in the java.io package always resolve relative path names against the current working directory. This directory is named by the system property user.dir, and is typically the directory in which the Java virtual machine was invoked. Instances of the java.io.File class are immutable; that is, once created, the abstract path name represented by a java.io.File object will never change. The following program, MyDir.java, displays the files and subdirectories of the current directory. The list() method of the java.io.File class is used to retrieve the file/directory list. String[] list(); // return a list of file/directory names. This program works like the DIR command in MS/DOS or the ls command in UNIX. MyDir.java
- 21 -
// Using the java.io.File class to display files/directories // under the current working directory. import java.io.*; public class MyDir { public static void main(String []args) { String []dirList; try { File mypath = new File("."); // current directory dirList = mypath.list(); // get the file list for (int k=0; k < dirList.length; ++k) System.out.println(dirList[k]); } // try catch(Exception e) { e.printStackTrace(); System.exit(2); } } // main } // MyDir Let us modify the previous program, MyDir.java, to take the first command line argument as the directory, instead of the current directory. The synopsis for running this program is: MyDir2 DirectoryName The modified program, MyDir2.java, is shown below. MyDir2.java
- 22 -
// Using the java.io.File class to display files/directories // under the directory specified as a command line argument. // Synopsis: MyDir2 DirectoryName import java.io.*; public class MyDir2 { public static void main(String []args) { String []dirList; try { if (args.length != 1) // expecting 1 command line argument { System.err.println("Please specify a directory"); System.exit(1); } File mypath = new File(args[0]); dirList = mypath.list(); // get the file/directory list for (int k=0; k < dirList.length; ++k) System.out.println(dirList[k]); } // try catch(Exception e) { e.printStackTrace(); System.exit(2); } } // main } // MyDir2
Object Serialization
The term object serialization means (1) converting all attributes (states) of an object into a sequence of bytes and (2) saving this sequence bytes into as an object stream. An object stream is not necessarily a file stream. Such a mechanism can thus be used to prepare an object for transmission across networks and operating systems. The java.io.Serializable interface is designed for such purposes and, thus, can be used to implement the object persistence and portability. Data Persistence means that the lifetime of an object is not determined by whether the program is running or that an object lives in between the invocation of programs, such as a client application (an applet or a standalone application) and a server-side program (a servlet). Data Portability means that objects can be retrieved by Java programs running under different operating system or networking environment, as long as the data class is accessible. The serializability of a class is done by implementing the java.io.Serializable interface. Subclasses of a serializable class are also serializable, too. The Serializable interface has no methods or attributes (fields) and serves only to identify the semantics of being serializable. By functionality, Java classes can be group into: User interface or driver classes and Data classes. The data classes are to represent the objects in interest and should be coded to have the ability to be serialized. Thus, the syntax:
- 23 -
public class ClassName implements Serializable { ........ } Classes that require special handling or operation for the object serialization and de-serialization will need to implement the writeObject() and readObject() methods. The signatures of these java.io.Serializable interface are: void writeObject(java.io.ObjectOutputStream out); void readObject(java.io.ObjectInputStream in); In general practice, classes that need to ensure its objects can be serialized will simply include the implements Serializable expression as shown above and will not need to specifically define the Serializable.writeObject() or Serializeable.readObject() methods. It is essential to realize that serialization is actually performed by special kind of stream objects, generally known as object streams: ObjectOutputStream for serialization and ObjectInputStream for deserialization). The java.io.ObjectOutputStream.writeObject() and java.io.ObjectInput.readObject() methods are used to actually serialize and de-serialize objects. The writeObject() method is responsible for writing the state of the object for its particular class so that the readObject() method will be able to restore (deserialize) it. Thus, object state is saved by writing the serializable attributes/fields to the java.io.ObjectOutputStream object using the writeObject() method. Attributes declared as static or transient cannot and will not be serialized. On the other hand, the readObject() method is used to retrieve (restore) the state of an object from an java.io.ObjectInputStream. Serializing an object Objects are serialized by writing them to an object stream, for example: // Serialize today's date to a file stream. FileOutputStream outfile = new FileOutputStream("mytmpfile"); ObjectOutputStream out = new ObjectOutputStream(outfile); out.writeObject(new Date()); // serialize a Date object out.flush(); // or out.close(); The signature for the constructor is: ObjectOutputStream(OutputStream out); Thus, out can be any object created from any subclass of the java.io.OutputStream class; in the case, a FileOutputStream object. The writeObject() method is then invoked to store the state of the object. Many objects can be saved to the same ObjectOutputStream. The sequence in which the objects are saved will be followed by the java.io.ObjectInputStream.readObject() method for de-serialization. The signature for the writeObject() method is: void writeObject(Object obj); The java.io.ObjectOutputStream class provides a rich set of methods, such as writeInt(), writeDouble(),.... for object serialization. However, the writeObject() is probably the most commonly used method. De-serializing an object To reverse the process, the following is a typical temple.
- 24 -
// Deserialize a date from a file stream. FileInputStream infile = new FileInputStream("mytmpfile"); ObjectInputStream in = new ObjectInputStream(infile); Date date = (Date) in.readObject(); A java.io.ObjectInputStream, in this a FileInputStream, is needed as the source stream for an object de-serialization. The state of an object is read/retored from the stream. Generally, objects are read with the readObject() method. Objects will be de-serialized in the sequence they were serialized. Of course, the java.io.ObjectInputStream class also provides other de-serialization methods, such as readInt(), readChar(),...... To demonstrate the serialization and deserialization of objects, let us develop a class, named EmpRec, to represent employee records, and then develop two programs to serialize some EmpRec objects and de-serialize the serialized EmpRec objects. A file stream is used for storing the attributes of these EmpRec objects. EmpRec.java // EmpRec class represents the employee information. // EmpRec is a serializable class. /* A class should provide a set of methods: constructor, readers, and writers. Usually, a pair of reader and writer methods is provided for each attribute. The toString() method defined in Object class is a typical reader method. */ import java.io.*; public class EmpRec implements Serializable { // attributes int ID; // employee ID String name; // employee name public EmpRec(int v, String s) // constructor { setID(v); setName(s); } // reader and writer methods public String toString() // reader method defined in root class { return name + ID; } // toString -- defined in Object class public void setID(int v) { ID = v; } public int getID() { return ID; } public void setName(String s) { name = s; } public String getName() { return name; } } // EmpRec The expression implement serializable makes all objects created from the EmpRec class and its subclasses to be serializable. The next program, SaveEmp.java, is to create some EmpRec objects and serialize them into a file. SaveEmp.java
- 25 -
// Serializing EmpRec objects into a file stream emp.out. // This program writes/serializes two EmpRec objects to an ObjectOutputStream. import java.io.*; public class SaveEmp { public static void main(String[] args) { EmpRec emp1 = new EmpRec(99, "Lynda"); EmpRec emp2 = new EmpRec(88, "Ava"); try { // Create an ObjectOutputStream object for serialization.. ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("emp.out")); // Write the objects to the file "emp.out" out.writeObject(emp1); out.writeObject(emp2); out.close(); } // try catch (Exception e) { e.printStackTrace(); System.exit(1); } } // main } // SaveEmp When executed, the above program, SaveEMp.java, writes two EmpRec objects into the file emp.out located under the current directory. The MS/DOS command type emp.out or the UNIX command cat emp.out can be used to verify that the objects are serialized. The file emp.out is considered to contain binary data. The following program, ReadEmp.java, deserialize the EmpRec objects serialized by the previous program, SaveEmp.java. ReadEmp.java // De-serializes the EmpRec objects serialized by SaveEmp.java. // This program de-serializes two EmpRec objects from an ObjectInputStream. import java.io.*; public class ReadEmp { public static void main(String[] args) { EmpRec x; try { // Create an ObjectInputStream for object de-serialization. ObjectInputStream in = new ObjectInputStream(new FileInputStream("emp.out")); // Read and deserialize the objects from the file "emp.out" x = (EmpRec) in.readObject(); System.out.println(x.toString()); x = (EmpRec) in.readObject(); System.out.println(x.toString()); in.close(); } // try catch (Exception e) { e.printStackTrace(); System.exit(1); } } // main } // ReadEmp
- 26 -
To test the above two programs, SaveEmo and ReadEmp, let us develop a standalone application which invokes SaveEmp.main() and ReadEmp.main() to first save a couple of EmpRec objects and later retrieve them. EmpDriver.java // A driver class used to test ReadEmp and SaveEmp public class EmpDriver { public static void main(String[] args) { String [] x = new String[2]; System.out.println(Saving....); SaveEmp.main(x); System.out.println(Reading....); ReadEmp.main(x); } // main } // EmpDriver C:\> javac EmpDriver.java C:\> java EmpDriver Saving.... Reading.... Lynda99 Ava88
- 27 -
(2) Extracting from the jar file generated from the above step: C:\> mkdir temp C:\> cd temp C:\> jar xvf ..\fileA.jar created: META-INF/ extracted: META-INF/MANIFEST.MF extracted: SaveEmp.class extracted: ReadEmp.class extracted: EmpRec.class As an example, this step is done in a different directory from the original directory. (3) Using a jar file does not require the jar file contents to be extracted. However, the CLASSPATH must be either updated or specified to include a jar file for the program compilation and execution. C:\> cp ..\EmpDriver.java C:\> rm *..class <<===== Remove all class files C:\> javac EmpDriver.java EmpDriver.java:8: Undefined variable or class name: SaveEmp SaveEmp.main(x); ^ EmpDriver.java:10: Undefined variable or class name: ReadEmp ReadEmp.main(x); ^ 2 errors C:\> javac -classpath .;..\fileA.jar EmpDriver.java C:\> java EmpDriver Saving.... Exception in thread "main" java.lang.NoClassDefFoundError: SaveEmp at EmpDriver.main(EmpDriver.java:8) C:\> java -classpath .;..\fileA.jar EmpDriver Saving... Reading.. Lynda99 Ava88 Notice that the CLASSPATH option is used in both compiling and running the above program. To list the options of the jar command: C:\> jar Usage: jar {ctxu}[vfm0M] [jar-file] [manifest-file] [-C dir] files ... Options: -c create new archive -t list table of contents for archive -x extract named (or all) files from archive
- 28 -
-u update existing archive -v generate verbose output on standard output -f specify archive file name -m include manifest information from specified manifest file -0 store only; use no ZIP compression -M Do not create a manifest file for the entries -C change to the specified directory and include the following file The jar command is also useful in creating a jar file for running a java program. To do so, a manifest file needs to be created to specify the name of the main class (driver class). The general steps are: Compile all java files into bytecodes C:\> java *.java Create a manifest file using a text editor, for example, mytest.txt Main-Class: EmpDriver
- 29 -
Java Applet
Listed below is a Java Applet which must be brought up by an Internet browser and thus must be referenced to by an HTML page using <APPLET> and </APPLET> HTML tags. In other words, a Java applet is, in fact, a client application used in an Internet environment to provide dynamic features that are missing from HTML pages. An applet is known as a client-side Java application. // First2.java // An applet which display Hello, Welcome! at the specified (x, y) position. import java.awt.Graphics; import javax.swing.JApplet; public class First2 extends JApplet { public void paint(Graphics x) { x.drawString("Hello! Welcome!", 20, 25); } // paint } // First2
RunFirst2.htm
<HTML> <APPLET CODE="First2" width=200 height=150> </APPLET> </HTML> A Java applet is a class derived from the Applet or JApplet library class. An Internet browser expects certain properties (attributes and methods) from a Java applet; such properties should be transparent to the programmers and should have already been defined in a library class, such as Applet or JApplet. The Java programmer only needs to extend the library applet to create a new applet to address the solution for the current task. This operation is termed as inheritance. In other words, a new applet inherits from the library applet class. The inheritance syntax is: public class ClassName extends Applet {....} The Applet or JApplet class is referred to as a super class, a parent class, or a base class. The new class represented by ClassName is known as the derived class, sub class, or child class. In the above example program, the class name is First2. The program should be saved in a file with the same name as the class. The statement import is used to indicate to the Java compiler that other classes from other packages will be used. Java library classes are saved into many library packages. For example, the JApplet class is saved in the package javax.swing. Thus, one of the following statements is used: import javax.swing.*; import javax.swing.JApplet; In Java, a simple statement is terminated by a semicolon. A pair of braces is used to construct a compound statement; thus, the closing brace terminates a compound statement. The above program also uses the Graphics class of the java.awt package. The term awt stands for Abstract Windows Tools -- Javas GUI-based application programming interface. A Java applet is supposed to be presented by the Internet Browser as part of a HTML document and
- 30 -
thus requires a corresponding HTML file to execute it. Most of the Java development tools will automatically generate an HTML file for such a purpose. Unlike the Java language, HTML is a case insensitive programming language. Each statement/directive is enclosed in a pair of angle brackets <> and is termed as a tag. The <applet> tag indicates the name of the Java class ( bytecode). The width and height indicate the size of the applet. Thus, the above applet is of size 300 pixels by 80 pixels. Assuming both First2.java and runfirst2.htm files are saved under the same directory, the following steps compile and run the above Java applet. System Prompt> javac First2.java System Prompt> appletviewer runfirst2.htm The general steps for developing an applet are: I. II. III. IV. Creating a Java source file (.java) using a text editor Creating an HTML file (.htm or .html) using a text editor Compiling the .java file into bytecode (.class file) using Java compiler (javac) Running the bytecode (.class file) using Java applet viewer (appletviewer) or a browser <======= Compile First2.java <======= Test the applet
- 31 -
An applet must have either java.applet.Applet or javax.swing.JApplet as its parent class. A standalone application must contain a method with the following signature: public static void main(String []x) In general, a GUI-based application must have a window container serving as the user interface and a container to hold/glue user interface objects. Hence, there are two major kinds of containers: window-enabled and non-window. A window container cannot be contained inside another container; whereas, a non-window container MUST be contained inside another container. The java.applet.Applet and javax.swing.JApplet classes are non-window container classes and signify that they should be executed in an internet browsers environment/window. A standalone application, on the other hand, needs a window container. The java.awt.Frame or javax.swing.JFrame is generally used as a window container for a standalone GUI application. A GUI program may have many containers. A container can consist of user interface components and graphic context. Only components can react to user events. Graphic context is simply the drawing on a container and is NOT an object. It is possible to develop a Java program which can run as a standalone application under a local operating system and as an applet via a web browser. In other words, there is no need to develop two versions of the same programs. The mechanism is to provide the necessary and desired applet methods, such as init() and paint(), as well as the main() method as required by a standalone application in the same class declaration. The main() method creates an instance of the class, adds it to its frame container, and invoke the start() method of the applet object. If the program is executed as an applet, the main() method is never invoked at all. The next two program examples illustrate how to implement a Java program to run as an applet as well as a standalone application. TwoFace.java
- 32 -
// Java program to run as an applet and an application import java.awt.*; import java.applet.Applet; public class TwoFace extends Applet { String msg = "I am an applet and surfing...."; public void paint(Graphics g) { g.drawString(msg, 60, 20); } // paint
// The main() method is invoked, when the program runs as an application. // This method is invoked, when the program runs as an application. public static void main(String []args) { Frame frame1 = new Frame("Application"); TwoFace x = new TwoFace(); // create an applet object x.msg = "I am an application and running....."; // Add the applet to the frame frame1.add(x); x.start(); // start s the applet object frame1.setVisible(true); // show the frame object } // main } //TwoFace To run the above program as a standalone application, enter: java TwoFace To run it as an applet, an HTML file will be needed. Run.htm <HTML> <APPLET Code=TwoFace WIDTH=200 HEIGHT=100> </APPLET> </HTML> To test the above program as an applet, bring up a web browser and then open up Run.htm from the browser. You need to make sure that both TwoFace.class and Run.htm files are located in the same folder.
- 33 -
Summarized in the following table are the major event interfaces, their listener methods and registration (add) methods. Interface ActionListener AdjustmentListener ComponentListener Listener Method actionPerformed (ActionEvent) adjustValue (ComponentEvent) componentHidden(ComponentEvent) componentMoved(ComponentEvent) componentResized(ComponentEvent) componentShown(ComponentEvent) componentAdded(ContainerEvent) componentRemoved(ContainerEvent) focusGained(FocusEvent) focusLost(FocusEvent) itemChanged(ItemEvent) keyPressed(KeyEvent) keyReleased(KeyEvent) keyTyped(KeyEvent) mouseClicked(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mousePressed(MouseEvent) mouseReleased(MouseEvent) mouseDragged(MouseEvent) mouseMoved(MouseEvent) textValueChanged(TextEvent) windowActivated(WindowEvent) windowClosed(WindowEvent) windowClosing(WindowEvent) windowDeactivated(WindowEvent) windowDeiconified(WindowEvent) windowIconified(WindowEvent) windowopened(WindowEvent) Add Method addActionListener (ActionListener) addAdjustmentListener (AdjustmentListener) addComponentListener (ComponentListener) addContainerListener (ContainerListener) addFocusListener (FocusListener) addItemListener (ItemListener) addkeyListener (KeyListener) addMouseListener (MouseListener)
Associated with each of the above interfaces, there is a method used to de-register an object from the interface. The naming convention for this method is: removeInterFaceName(.) For example, removeActionListener (ActionListener) // DrawLine4.java import java.awt.*;
- 34 -
import java.applet.Applet; import java.awt.event.*; public class DrawLine4 extends Applet implements MouseListener, MouseMotionListener { final int MAXLINES = 10; Point starts[] = new Point[MAXLINES]; // starting points Point ends[] = new Point[MAXLINES]; // ending points Point anchor; // start of current line Point currentpoint; // current end of line int currline = 0; // number of lines public void init() { // register event listeners addMouseListener(this); addMouseMotionListener(this); } // init // MouseListener listener methods public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) // same as mouseDown in JDK 1.0 { anchor = new Point(e.getX(),e.getY()); } public void mouseReleased(MouseEvent e) // same as mouseUp in JDK 1.0 { addline(e.getX(),e.getY()); } // MouseMotionListener listener methods public void mouseDragged(MouseEvent e) { currentpoint = new Point(e.getX(),e.getY()); repaint(); } public void mouseMoved(MouseEvent e) {} void addline(int x,int y) { starts[currline] = anchor; ends[currline] = new Point(x,y); ++currline; currentpoint = null; anchor = null; repaint(); } // addline public void paint(Graphics g) // same as mouseDrag in JDK 1.0
- 35 -
{ // Draw existing lines for (int i = 0; i < currline; i++) g.drawLine(starts[i].x, starts[i].y, ends[i].x, ends[i].y); // draw current line g.setColor(Color.yellow); if (currentpoint != null) g.drawLine(anchor.x,anchor.y, currentpoint.x,currentpoint.y); } }
Let us use the MouseListener interface as the event listener to capture the mouse click. In this program, a click on the mouse causes a blue square be displayed at the mouse clicking point. //DrawDot3.java // Display a filled square at the mouse pointer when the mouse is pressed. // Using a frame as the container, instead of an applet. import java.awt.event.*; import java.awt.*; public class DrawDot3 extends Frame implements MouseListener, WindowListener { private int x, y = 0; // x, y coordinates public DrawDot3() // Default constructor { setTitle("DrawDot3"); // Register to the event listeners addMouseListener(this); addWindowListener(this); } // constructor public static void main(String[] args) { DrawDot3 x = new DrawDot3(); x.setSize(250, 250); x.setVisible(true); } // Draw a blue square at the point (x, y) public void paint(Graphics g) { g.drawString("("+x+", "+y+")", x-5, y-5); g.setColor(Color.blue);
- 36 -
g.fillRect(x, y, 7, 7); } // paint // MouseListener listener methods // When the mouse is pressed, the mouse pointer location will be stored in (x, y) public void mousePressed(MouseEvent e) { // Get (x, y) coordinates using getX() and getY() methods x = e.getX(); y = e.getY(); repaint(); } public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} // Listener methods declared in the WindowListener interface public void windowClosing(WindowEvent e) { setVisible(false); // or dispose(); System.exit(1); } // windowClosing public void windowClosed(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowActivated(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } public void windowOpened(WindowEvent e) { } } // DrawDot3
- 37 -
Using Menus
The steps of adding the menu capability are: 1. create a menu bar 2. activate the menu bar 3. create a menu 4. add the menu to the menu bar 5. create a menu item 6. add the menu item to the menu The javax.swing package provides classes to support the memnu capability. These class are: JMenuBar, JMenu, and JMenuItem. The method setMenuBar() allows javax.swing.JFrame to set an active menu bar. One menu bar can be set to active at any given point of time. In order to react to the click on a menu item event, the ActionListener interface will be used. Hence, a menu item works almost identically to a push button (javax.swing.JButton). Each menu and menu item is associated with a label. Separators can also be added by calling the method addSeparator() of the JMenu class. The Java statements corresponding to the steps listed above may look like: 1. JMenuBar menuBar = new JMenuBar(); 2. setMenuBar(menuBar); // e.g., new JFrame().setMenuBar(menuBar); 3. JMenu menuA = new JMenu(A Menu); 4. menuBar.add(menuA); 5. JMenuItem menuItemA = new JMenuItem(Menu Item A); 6. menuA.add(menuItemA); The following program example, UseMenu.java, shows how menu capabilities can be implemented in a GUI program. This program also demonstrates how to implement an event withdrawal and to activate a specific menu bar. // Using JMenuBar, JMenu, and JMenuItem to set up menus import javax.swing.*; import java.awt.event.*; public class UseMenu extends JFrame implements ActionListener { JMenuItem item1, item2, item3, item4, item5, item6; JMenuBar menuBar1, menuBar2; public static void main(String []args) { UseMenu x = new UseMenu(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(350, 250); x.setVisible(true); } // main // constructor
- 38 -
public UseMenu() { setTitle("Using Menus"); // Step 1 create menu bar menuBar1 = new JMenuBar(); menuBar2 = new JMenuBar(); // Step 2 activate a menu bar setJMenuBar(menuBar1); // Step 3 create menu JMenu menuA = new JMenu("Menu 1"); JMenu menuB = new JMenu("Menu 2"); JMenu menuC = new JMenu("Menu 3"); // Step 4 add menu to a menu bar menuBar1.add(menuA); menuBar1.add(menuB); menuBar2.add(menuC); // Step 5 create menu item item1 = new JMenuItem("ONE"); item2 = new JMenuItem("2"); item3 = new JMenuItem("Switch menu bar"); item4 = new JMenuItem("Selection #4"); item5 = new JMenuItem("change size"); item6 = new JMenuItem("Exit"); // Step 6 add menu item to a menu menuA.add(item1); menuA.addSeparator(); menuA.add(item2); menuB.add(item3); menuB.addSeparator(); menuB.add(item4); menuB.add(item5); menuC.add(item6); // event registration item1.addActionListener(this); item2.addActionListener(this); item3.addActionListener(this); item4.addActionListener(this); item5.addActionListener(this); item6.addActionListener(this); } // constructor public void actionPerformed(ActionEvent e) { if (e.getSource() == item1) setTitle("Menu Item1 selected"); if (e.getSource() == item2)
- 39 -
{ setTitle("Welcome to Menu Item2"); item2.removeActionListener(this); } if (e.getSource() == item3) { setTitle("New menu bar"); setJMenuBar(menuBar2); } if (e.getSource() == item4) setTitle("4 is a simple Four"); if (e.getSource() == item5) { setTitle("Give me 5"); setSize(190, 210); } if (e.getSource() == item6) System.exit(0); repaint(); } // actionPerformed } // UseMenu
- 40 -
//UseJList1.java // Using javax.JList to allow a single selection at a time import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; public class UseJList1 extends JFrame { JLabel label; JList myList; String [] msg = {"BMW","Vacation","Free lunch","Try again","Try harder"}; public static void main(String []args) { UseJList1 x = new UseJList1(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(250, 250); x.setVisible(true); } // main public UseJList1() // constructor { setTitle("UseJList1"); String [] s = new String[5]; for (int k=0; k < s.length; ++k) s[k] = "Try Me" + k; myList = new JList(s); // create a JList object from a String array // Allow only single selection
- 41 -
myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JScrollPane sp = new JScrollPane(myList); // adding scrolling capability JPanel p = new JPanel(); p.add(sp); // Event registration and handling // The ListSelectionListener interface handles a click on a list item. myList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { String value = (String) myList.getSelectedValue(); label.setText(value + " says: " + msg[myList.getSelectedIndex()]); } }); getContentPane().add(p, BorderLayout.SOUTH); label = new JLabel("Try Your Luck"); getContentPane().add(label, BorderLayout.NORTH); } // constructor } // UseJList1 // FileList.java // Using javax.Jlist to show the current directory contents import java.io.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; public class FileList extends JFrame { JLabel label; JList myList; JTextField fileType; String []dirList; public static void main(String []args) { FileList x = new FileList(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(250, 250); x.setVisible(true); } // main public FileList(){// constructor setTitle("FileList"); try { File mypath = new File("."); // current directory dirList = mypath.list(); // get the file list } catch(Exception e) { e.printStackTrace(); System.exit(2); JPanel p = new JPanel(); fileType = new JTextField(20); p.add(fileType);
- 42 -
myList = new JList(dirList); // create a JList object myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JScrollPane sp = new JScrollPane(myList); // adding scrolling capability p.add(sp); getContentPane().add(p, BorderLayout.SOUTH); label = new JLabel(""); getContentPane().add(label, BorderLayout.NORTH); // event registration and handling // The ListSelectionListener interface handles a click on a list item. myList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { String v = (String) myList.getSelectedValue(); label.setText(v); if ((new File(v)).isFile()) fileType.setText(v + " is a file"); if ((new File(v)).isDirectory()) fileType.setText(v + " is a directory"); } }); } // constructor } // FileList The next program retrieves database records and displays them in a JList. // UseJList2.java // Using javax.JList to display database records import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import java.sql.*; import java.util.*; public class UseJList2 extends JFrame { JLabel label; JList myList; Vector lName; // store last names Object []fName; // hold first names public static void main(String []args) { UseJList2 x = new UseJList2(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(350, 250); x.setVisible(true); } // main public UseJList2() // constructor {
- 43 -
setTitle("UseJList2"); lName = new Vector(); // store last names try { getRecord(); // Retrieve database records } catch(Exception e) { System.err.println(e); } myList = new JList(lName); // create a JList object based on last names // Allow only single selection myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JScrollPane sp = new JScrollPane(myList); // adding scrolling capability JPanel p = new JPanel(); p.add(sp); // event registration and handling // The ListSelectionListener interface handles a click on a list item. myList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { label.setText(fName[myList.getSelectedIndex()].toString()); } }); getContentPane().add(p, BorderLayout.SOUTH); label = new JLabel(); // used to show the first name getContentPane().add(label, BorderLayout.NORTH); } // constructor public void getRecord() throws Exception { // The name of the JDBC driver String driverName = "sun.jdbc.odbc.JdbcOdbcDriver"; // JDBC connection URL String connectionURL = "jdbc:odbc:SimpleDB"; Connection con = null; // JDBC Connection object Statement stmt = null; // JDBC Statement object // SQL statement used to retrieve records String sqlStatement = "SELECT LastName, FirstName FROM PEOPLE"; ResultSet rs = null; // JDBC ResultSet object try { Class.forName(driverName).newInstance(); // Load the driver // Establish a database connection con = DriverManager.getConnection(connectionURL); stmt = con.createStatement(); // Create the SQL statement rs = stmt.executeQuery(sqlStatement); // Execute the query Vector v = new Vector(); // Step through the entire ResultSet
- 44 -
while (rs.next()) { lName.addElement(rs.getString(1)); // get the last name v.addElement(rs.getString(2)); // get the first name } // while fName = v.toArray(); // convert the first names into an array } // try catch (Exception e) { System.err.println(e); } finally { // Cleaning up if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (con != null) con.close(); } // finally } // getRecord } // UseJList2
javax.swing.JTree class Constructors: JTree(); JTree(Hashtable x); // create a tree object based on a Hashtable JTree(TreeNode x); // create a tree object based on a TreeNode JTree(TreeNode x, boolean leaf); // true, if child node is a leaf node JTree(TreeModel x); // create a tree object based on a TreeModel JTree(Object []x); // create a tree object based on an Object array JTree(Vector x); // create a tree object based on a Vector // UseJTree1.java // Using javax.JTree to create and show a list of strings import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; public class UseJTree1 extends JFrame { JTree myTree; public static void main(String []args) { UseJTree1 x = new UseJTree1(); x.setSize(180, 200); x.setVisible(true); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } // main
- 45 -
public UseJTree1() // constructor { setTitle("UseJTree1"); String []s = new String[10]; for (int k=0; k < s.length; ++k) s[k] = "Hello " + k; myTree = new JTree(s); // create a JTree object based on a String array JScrollPane sp = new JScrollPane(myTree); // adding scrolling capability getContentPane().add(sp); } // constructor } // UseJTree1 The above program simply creates a JTree object to show a list of strings. No event associated statements were added. Each node in the tree is a leaf node and does not any child nodes. Thus, the icons shown in the above figure are leaf icons. The DefaultMutableTreeNode class represents a node in a tree component and is one of many tree node related classes facilitated by the javax.swing.tree package. Thus, a tree node can be created by call the constructor: DefaultMutableTreeNode(Object x); // UseJTree2.java import java.awt.event.*; import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; public class UseJTree2 extends JFrame { JTree myTree; public static void main(String []args) { UseJTree2 x = new UseJTree2(); x.setSize(250, 200); x.setVisible(true); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } // main public UseJTree2() // constructor { setTitle("UseJTree2"); String []s = {"Hello", "Howdy", "Hey", "Hi"}; DefaultMutableTreeNode temp; DefaultMutableTreeNode myRoot = new DefaultMutableTreeNode("Top"); for (int k=0; k < s.length; ++k) { temp = new DefaultMutableTreeNode(s[k]); // add 4 child nodes
- 46 -
for (int n=1; n<=4; ++n) temp.add(new DefaultMutableTreeNode(s[k] + n)); myRoot.add(temp); // add a child node to the top level } myTree = new JTree(myRoot); // create a JTree object based on a tree node JScrollPane sp = new JScrollPane(myTree); // adding scrolling capability getContentPane().add(sp); } // constructor } // UseJTree2 The add() method used in the above program is declared in the javax.swing.tree.MutableTreeNode interface as: void add(MutableTreeNode x); // add a child node to the current tree node Currently, the javax.swing.tree.DefaultMutableTreeNode class is the only class that implements the MutableTreeNode interface. The parent interface of the MutableTreeNode interface is javax.swing.tree.TreeNode. The setEditable(boolean x) method can be used to either allow the label of a tree node to be changed or not. By default, the labels cannot be modified. To change the label of a tree node, click once on the label and enter the new label. The nest program, UseJTree3.java, is modified from UseJTree2.java by calling the setEditable() method to allow the labels to be modifiable. // UseJTree3.java // Editing the labels of the JTree nodes import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; public class UseJTree3 extends JFrame { JTree myTree; public static void main(String []args) { UseJTree3 x = new UseJTree3(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(250, 200); x.setVisible(true); } // main public UseJTree3() // constructor { setTitle("UseJTree3"); String []s = {"Hello", "Howdy", "Hey", "Hi"}; DefaultMutableTreeNode temp;
- 47 -
DefaultMutableTreeNode myRoot = new DefaultMutableTreeNode("Top"); for (int k=0; k < s.length; ++k) { temp = new DefaultMutableTreeNode(s[k]); // add 4 child nodes for (int n=1; n<=4; ++n) temp.add(new DefaultMutableTreeNode(s[k] + n)); myRoot.add(temp); // add a child node to the top level } myTree = new JTree(myRoot); // create a JTree object based on a tree node myTree.setEditable(true); // allow editing on labels JScrollPane sp = new JScrollPane(myTree); // adding scrolling capability getContentPane().add(sp); } // constructor } // UseJTree3
Let us modify UseJTree3.java to UseJTree4.java in order to allow the user to add and remove nodes. Nodes can be added at the same level as the currently selected node as well as at a lower level to the selected node. However, there can be only one top/root node. Such a modification requires calling methods of the javax.swing.tree.DefaultTreeModel class. In order words, the tree structure created and used in UseJTree3.java remains unchanged. A DefaultTreeModel object will be created based on the set of DefaultMutableTreeNode objects created in UseJTree3.java in order to call methods of the DefaultTreeModel class to add or remove nodes. Basically, it is like wrapping around a set of DefaultMutableTreeNode objects to add the manipulation capabilities. Hence, the javax.swing.tree.JTree object will be created from a DefaultTreeModel object, instead of a set of DefaultMutableTreeNode objects. The general syntax is: new JTree(new DefaultTreeModel(new DefaultMutableNode(....))); With these modifications in place, UseJTree4.java, listed below, allows the user to add and remove nodes from a tree. This program, however, does not save the changes. It simply updates the graphical user interface part of the tree. // UseJTree4.java // Adding and removing tree nodes using DefaultTreeModel import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; public class UseJTree4 extends JFframe { JTree myTree;
- 48 -
DefaultMutableTreeNode myRoot; DefaultTreeModel treeMod; // a tree model public static void main(String []args) { UseJTree4 x = new UseJTree4(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(250, 200); x.setVisible(true); } // main public UseJTree4() // constructor { setTitle("UseJTree4"); String []s = {"Hello", "Howdy", "Hey", "Hi"}; DefaultMutableTreeNode temp; myRoot = new DefaultMutableTreeNode("Top"); for (int k=0; k < s.length; ++k) { temp = new DefaultMutableTreeNode(s[k]); for (int n=1; n<=3; ++n) // add 3 child nodes temp.add(new DefaultMutableTreeNode(s[k] + n)); myRoot.add(temp); // add a child node to the top level } treeMod = new DefaultTreeModel(myRoot); // create a tree model for manipulation myTree = new JTree(treeMod); // create a JTree object based on a TreeModel myTree.setEditable(true); // allow editing on labels JScrollPane sp = new JScrollPane(myTree); // adding scrolling capability getContentPane().add(sp, BorderLayout.CENTER); JPanel p = new JPanel(); addButton(p); // add buttons as selections getContentPane().add(p, BorderLayout.SOUTH); } // constructor // Add push buttons for adding and removing nodes void addButton(JPanel panel) { JButton samelevel = new JButton("Add Same Level Node"); JButton childlevel = new JButton("Add Child Node"); JButton delete = new JButton("Delete"); panel.add(samelevel); panel.add(childlevel); panel.add(delete); // Event registration and handling samelevel.addActionListener(new ActionListener() {
- 49 -
public void actionPerformed(ActionEvent e) { // 1. Asking the tree which node was selected DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) myTree.getLastSelectedPathComponent(); if (selectedNode == null) return; // no node was selected // 2. Asking the selected node who is its parent DefaultMutableTreeNode parent = (DefaultMutableTreeNode) selectedNode.getParent(); if (parent == null) return; // There can be only one root node // create a new tree node DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("New"); // 3. Asking the parent node for the index of the selected node int selectedIndex = parent.getIndex(selectedNode); // 4. Adding the new node to the tree model at the same level treeMod.insertNodeInto(newNode, parent, selectedIndex + 1); // 5. Builds an array of nodes for a specific node and up TreeNode[] nodes = treeMod.getPathToRoot(newNode); TreePath path = new TreePath(nodes); // create a new tree path // 6. Redisplays the tree nodes to show the newly added node myTree.scrollPathToVisible(path); } }); childlevel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // get the selected node DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) myTree.getLastSelectedPathComponent(); if (selectedNode == null) return; // no node was selected // create a new tree node DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("New"); // 7. Adding the new node to the tree model at the lower level treeMod.insertNodeInto(newNode, selectedNode, selectedNode.getChildCount()); TreeNode[] nodes = treeMod.getPathToRoot(newNode); TreePath path = new TreePath(nodes); myTree.scrollPathToVisible(path); // display the new node } }); delete.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // get the selected node DefaultMutableTreeNode selectedNode =
- 50 -
(DefaultMutableTreeNode) myTree.getLastSelectedPathComponent(); if (selectedNode != null && selectedNode.getParent() != null) // 8. remove a node from the tree model treeMod.removeNodeFromParent(selectedNode); } }); } // addButton } // UseJTree4 Let us take a closer look at the methods and classes used in the preceding example, UseJTree4.java. In addition to the DefaultTreeModel class, this program also uses the javax.swing.tree.TreePath class which represents a path to a node in a tree and is used to construct a path from an array of tree nodes in order to uniquely identifying the path from the root of the tree to a specific node. The following constructor is used to instantiate an object from the DefaultTreeModel class: DefaultTreeModel(TreeNode x); // Creates a tree in which any node can have child nodes // where x represents the root for a set of tree nodes Other methods used in UseJTree4.java include: 1. Object getLastSelectedPathComponent(); // JTree class Returns the selected node 2. TreeNode getParent(); // DefaultMutableTreeNode class 3. 4. int getIndex(TreeNode ChildNode); // DefaultMutableTreeNode class Returns the index of the specified child node. void insertNodeInto(TreeNode x, TreeNode parent, int where); // DefaultTreeModel class Adds the newnode x as a child node of parent node at the index where. TreeNode [] getPathtoRoot(TreeNode x); // DefaultTreeModel class Returns an array of tree nodes from the immediate parent of a specific node up to and including the root node. The last element in the returned array is the node. void scrollPathToVisible(TreePath path); // JTree class Displays tree nodes. The JTree object must be contained in a JScrollPane container. int getChildCount(); Returns the number of children of the node. // DefaultMutableTreeNode class
5.
6. 7. 8.
void removeNodeFromParent(MutableTreeNode x); // DefaultTreeModel class Removes a child node from the current node
Tree Traversal
The javax.swing.tree.DefaultMutableTreeNode class provides four tree traversal methods: Enumeration breadthFirstEnumeration();
- 51 -
Enumeration depthFirstEnumeration(); Enumeration preorderFirstEnumeration(); // same as depthFirst Enumeration postorderFirstEnumeration(); // reverse of preorder // UseJTree5.java // Visiting tree nodes in two different ways: breadth first and depth first. import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; import java.util.*; public class UseJTree5 extends JFrame { JTree myTree; DefaultMutableTreeNode myRoot; DefaultTreeModel treeMod; JScrollPane sp2; public static void main(String []args) { UseJTree5 x = new UseJTree5(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(250, 200); x.setVisible(true); } // main public UseJTree5() // constructor { setTitle("UseJTree5"); String []s = {"Hello", "Howdy", "Hey", "Hi"}; DefaultMutableTreeNode temp; myRoot = new DefaultMutableTreeNode("Top"); for (int k=0; k < s.length; ++k) { temp = new DefaultMutableTreeNode(s[k]); for (int n=1; n<=3; ++n) // add 3 child nodes temp.add(new DefaultMutableTreeNode(s[k] + n)); myRoot.add(temp); // add a child node to the top level } treeMod = new DefaultTreeModel(myRoot); myTree = new JTree(treeMod); // create a JTree object based on a tree node myTree.setEditable(true); // allow editing getContentPane().setLayout(new GridLayout(1, 3)); getContentPane().add(new JScrollPane(myTree)); DefaultMutableTreeNode x; Vector v = new Vector(); Enumeration nodeRec = myRoot.breadthFirstEnumeration(); // breadth first while (nodeRec.hasMoreElements()) {
- 52 -
x = (DefaultMutableTreeNode) nodeRec.nextElement(); v.addElement(x.toString()); } JList nodeList = new JList(v); getContentPane().add(new JScrollPane(nodeList)); v = new Vector(); nodeRec = myRoot.depthFirstEnumeration(); // depth first while (nodeRec.hasMoreElements()) { x = (DefaultMutableTreeNode) nodeRec.nextElement(); v.addElement(x.toString()); } nodeList = new JList(v); getContentPane().add(new JScrollPane(nodeList)); } // constructor } // UseJTree5 The next program demonstrates how to visit tree nodes in the original order in which they are added to the tree. A recursive function is used to visit each node at all levels of a tree. // UseJTree6.java // Showing the sub-tree of a selected node import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; import java.io.*; import java.util.*; public class UseJTree6 extends JFrame { JTree myTree; DefaultMutableTreeNode myRoot; DefaultTreeModel treeMod; JTextArea outbox; public static void main(String []args) { UseJTree6 x = new UseJTree6(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(400, 300); x.setVisible(true);
- 53 -
} // main public UseJTree6() // constructor { setTitle("UseJTree6"); String []s = {"Hello", "Howdy", "Hey", "Hi"}; DefaultMutableTreeNode temp; myRoot = new DefaultMutableTreeNode("Top"); for (int k=0; k < s.length; ++k) { temp = new DefaultMutableTreeNode(s[k]); for (int n=1; n<=3; ++n) // add 3 child nodes temp.add(new DefaultMutableTreeNode(s[k] + n)); myRoot.add(temp); // add a child node to the top level } treeMod = new DefaultTreeModel(myRoot); myTree = new JTree(treeMod); // create a JTree object based on a tree node myTree.setEditable(true); // allow editing JScrollPane sp = new JScrollPane(myTree); // adding scrolling capability getContentPane().add(sp, BorderLayout.WEST); // add a text area to display the sub-tree of a selected node. outbox = new JTextArea(10, 20); sp = new JScrollPane(outbox); getContentPane().add(sp, BorderLayout.EAST); JPanel p = new JPanel(); addButton(p); getContentPane().add(p, BorderLayout.SOUTH); } // constructor // Add push buttons for adding and removing nodes void addButton(JPanel panel) { JButton subtree = new JButton("Show Sub-tree"); panel.add(subtree); // Event registration and handling subtree.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // Get the selected node DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) myTree.getLastSelectedPathComponent(); if (selectedNode == null) return; // no node was selected outbox.setText(""); // clear the output area printNode(selectedNode); // print tree nodes recursively }
- 54 -
}); } // addButton // print each tree node recursively void printNode(DefaultMutableTreeNode node) { int k = node.getChildCount(); // Get child count if (k == 0) // This is a leaf node; recursion stops here { outbox.setText(outbox.getText() + node.toString() + "\n"); // print the leaf return; } // print a root node and the number of child nodes it has outbox.setText(outbox.getText() + node.toString() + " " + k + "\n"); // Asking each child node to print itself for (int n=0; n < k; ++n) printNode((DefaultMutableTreeNode) node.getChildAt(n)); // recursive call } // printNode } // UseJTree6
// UseJTree7.java // Adding, removing, showing, and saving tree nodes import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.tree.*; import javax.swing.event.*; import java.io.*; public class UseJTree7 extends JFrame { JTree myTree; DefaultMutableTreeNode myRoot; DefaultTreeModel treeMod; JTextArea outbox; PrintWriter outfile; public static void main(String []args) { UseJTree7 x = new UseJTree7(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(500, 300); x.setVisible(true); } // main public UseJTree7() // constructor {
- 55 -
setTitle("UseJTree7"); String []s = {"Hello", "Howdy", "Hey", "Hi"}; DefaultMutableTreeNode temp; myRoot = new DefaultMutableTreeNode("Top"); for (int k=0; k < s.length; ++k) { temp = new DefaultMutableTreeNode(s[k]); // add 3 child nodes for (int n=1; n<=3; ++n) temp.add(new DefaultMutableTreeNode(s[k] + n)); myRoot.add(temp); // add a child node to the top level } treeMod = new DefaultTreeModel(myRoot); myTree = new JTree(treeMod); // create a JTree object based on a tree node myTree.setEditable(true); // allow editing JScrollPane sp = new JScrollPane(myTree); // adding scrolling capability add(sp, BorderLayout.WEST); outbox = new JTextArea(10, 20); sp = new JScrollPane(outbox); add(sp, BorderLayout.EAST); JPanel p = new JPanel(); addButton1(p); add(p, BorderLayout.NORTH); p = new JPanel(); addButton2(p); add(p, BorderLayout.SOUTH); } // constructor // Add push buttons for adding and removing nodes void addButton1(JPanel panel) { JButton samelevel = new JButton("Add Same Level Node"); JButton childlevel = new JButton("Add Child Node"); JButton delete = new JButton("Delete"); panel.add(samelevel); panel.add(childlevel); // Event registration and handling samelevel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) myTree.getLastSelectedPathComponent(); if (selectedNode == null) return;
- 56 -
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) selectedNode.getParent(); if (parent == null) return; DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("New"); int selectedIndex = parent.getIndex(selectedNode); // adding the new node to the top level node treeMod.insertNodeInto(newNode, parent, selectedIndex + 1); // display the new node TreeNode[] nodes = treeMod.getPathToRoot(newNode); TreePath path = new TreePath(nodes); myTree.scrollPathToVisible(path); } }); childlevel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) myTree.getLastSelectedPathComponent(); if (selectedNode == null) return; DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("New"); // add the new node treeMod.insertNodeInto(newNode, selectedNode, selectedNode.getChildCount()); // display the new node TreeNode[] nodes = treeMod.getPathToRoot(newNode); TreePath path = new TreePath(nodes); myTree.scrollPathToVisible(path); } }); } // addButton1 void addButton2(JPanel panel) { JButton delete = new JButton("Delete"); JButton subtree = new JButton("Show Sub tree"); JButton save = new JButton("save"); // saving the tree nodes into a file panel.add(delete); panel.add(subtree); panel.add(save); delete.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {
- 57 -
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) myTree.getLastSelectedPathComponent(); if (selectedNode != null && selectedNode.getParent() != null) treeMod.removeNodeFromParent(selectedNode); } }); subtree.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // Get the selected node DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) myTree.getLastSelectedPathComponent(); if (selectedNode == null) return; // no node was selected outbox.setText(""); // clear the output area printNode(selectedNode); // print tree nodes recursively } }); // Saving all tree nodes into a file save.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { outfile = new PrintWriter(new BufferedWriter(new FileWriter("jtree7.dat"))); saveNode(myRoot); // save tree nodes recursively outfile.close(); } catch (IOException ioe) { System.err.println(ioe); } } }); } // addButton2 // print each tree node recursively void printNode(DefaultMutableTreeNode node) { int k = node.getChildCount(); // Get child count if (k == 0) { outbox.setText(outbox.getText() + node.toString() + "\n"); // print the leaf return; // This is a leaf node; recursion stops here } outbox.setText(outbox.getText() + node.toString() + " " + k + "\n"); // Asking each child node to print itself
- 58 -
for (int n=0; n < k; ++n) printNode((DefaultMutableTreeNode) node.getChildAt(n)); // recursive call } // printNode // Save all tree nodes into a file recursively void saveNode(DefaultMutableTreeNode root) { int k = root.getChildCount(); // Get child count try { if (k == 0) { outfile.println(root.toString()); return; // This is a leaf node; recursion stops here } outfile.println(root.toString() + " " + k); // Asking each child node to save itself for (int n=0; n < k; ++n) saveNode((DefaultMutableTreeNode) root.getChildAt(n)); // recursive call } catch (Exception oe) { System.err.println(oe); } } // saveNode } // UseJTree7 In the above program, the coding for the saveNode() method is very much like the coding for the printNode() method. The logic used in both saveNode() and printNode() methods is: Is the current node a leaf? Yes, save/print the node and return Otherwise, 1. save/print the node along with the number of child nodes it has 2. for each child node, ask the child node to repeat te same logic Thus, the base/minimum case for the above recursive logic is: when a leaf (a tree node with no child nodes) is reached
javax.swing.JTable class The javax.swing.JTable class is used to display and edit two-dimensional tables of cells. Some Constructors: JTable(); // table cells are created from a 2-D array; the column is created from an array JTable(Object [][] cells, Object []heading); // table cells are created from a Vector of Vectors; the column is a separate Vector JTable(Vector cells, Vector heading); JTable(TableModel x); // create a table object based on a TableModel object The JTable object can be set up to display any data model which implements the TableModel
- 59 -
interface with a couple of lines of code: TableModel myData = new MyTableModel(); JTable table = new JTable(myData); // UseJTable1.java. // Using javax.JTable to create a simple table based on a 2-D array import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; public class UseJTable1 extends JFrame { JTable myTable; public static void main(String []args) { UseJTable1 x = new UseJTable1(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(180, 200); x.setVisible(true); } // main public UseJTable1() { setTitle("UseJTable1"); String [][]menu = { {"Monday", "Lobster"}, {"Tuesday", "Shrimp"}, {"Wednesday", "Tuna"}, {"Thursday", "Chicken Salad"}, {"Friday", "On a diet"}, {"Saturday", "Fruits"}, {"Sunday", "All-you-can-eat-buffet"} }; String []heading = {"Day", "Today's Dinner"}; // create a JTable object using a 2-D array // Cells of a table are saved in a 2-D array. // The headings for the columns in the tables are saved in an array myTable = new JTable(menu, heading); JScrollPane sp = new JScrollPane(myTable); // adding scrolling capability getContentPane().add(sp); } // constructor } // UseJTable1 The next program, UseJTable2.java, uses a table to display database records which are saved in a vector of vectors. A vector is used to keep track of rows retrieved from the database. Each row contains, usually, two or more columns. Hence, each row needs to be a vector itself. This implementation is much flexible than using a two dimensional array. // UseJTable2.java // Using javax.JTable to display database records which are stored in a Vector of Vectors. import java.awt.*; import java.awt.event.*;
- 60 -
import javax.swing.*; import javax.swing.event.*; import java.util.*; import java.sql.*; public class UseJTable2 extends JFrame { JTable myTable; public static void main(String []args) { UseJTable2 x = new UseJTable2(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(280, 200); x.setVisible(true); } // main public UseJTable2() // constructor { setTitle("UseJTable2"); Vector dbRec = new Vector(), heading = new Vector(); try { getdbRec(dbRec, heading); } catch (Exception e) { System.err.println(e); } // create a JTable object using Vectors // Cells of a table are saved in a Vector. // The headings for the columns in the tables are saved in another Vector myTable = new JTable(dbRec, heading); JScrollPane sp = new JScrollPane(myTable); // adding scrolling capability getContentPane().add(sp); } // constructor // Retrieve database information and save it in Vectors. // Rows are saved in a Vector of Vectors (Each element of "rows" is a Vector). // Column names are saved in a separate Vector. void getdbRec(Vector rows, Vector columns) { String driverName = "sun.jdbc.odbc.JdbcOdbcDriver"; // JDBC driver name String connectionURL = "jdbc:odbc:SimpleDB"; // The name of the JDBC driver Connection con = null; // JDBC Connection object Statement stmt = null; // JDBC Statement object String sqlStatement = "SELECT LastName, FirstName FROM PEOPLE"; ResultSet rs = null; // JDBC ResultSet object try
- 61 -
{ Class.forName(driverName).newInstance(); // Load the driver // Establish a database connection con = DriverManager.getConnection(connectionURL); stmt = con.createStatement(); // Create the SQL statement rs = stmt.executeQuery(sqlStatement); // Execute the query // Get the information on the columns ResultSetMetaData rsmd = rs.getMetaData(); // Get data dictionary information int columnCount = rsmd.getColumnCount(); for (int k=1; k <= columnCount; ++k) // index starts with 1 columns.addElement(rsmd.getColumnLabel(k)); // get column names // Step through the entire ResultSet to get each record int rowCount = 0; // Vector index starts with 0 while (rs.next()) { rows.addElement(new Vector()); // a Vector for each row for (int k=1; k <= columnCount; ++k) // get column data for the current row ((Vector) rows.elementAt(rowCount)).addElement(rs.getString(k)); ++rowCount; } // while } // try catch (Exception e) { System.err.println(e); } finally { // Cleaning up try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (con != null) con.close(); } catch (Exception e) { System.err.println(e); } } // finally } // getdbRec } // UseJTable2
java.swing.table.DefaultTableModel class In a general practice, a table model is used to provide the data for the cells of a table. The javax.swing.table.DefaultTableModel class implements the TableModel interface to represent a tabular data model. The constructors of this class are described below. DefaultTableModel(); // creates a table of zero columns and zero rows. DefaultTableMode(int rows, int columns); // creates a table of size rows x columns DefaultTableMode(Object [][]rows, Object [] columnNames);
- 62 -
DefaultTableMode(Object []columnNames, int rowSize); DefaultTableMode(Vector rows, Vector columnNames); DefaultTableMode(Vector rows, int rowSize); By default, the label or heading for each column is named in alphabetically starting with A. The setColumnIdentifiers() method can be used to replace the default column headings. Some Methods: int getRowCount(); // returns the # of rows in the table int getColumnCount(); // returns the # of columns in the table void setValueAt(Object x, int row, int column); // assign x into a table cell void setColumnIdentifiers(Object [] columnames); // change the column headings void setColumnIdentifiers(Vector columnames); // change the column headings The following program, UseJTable3.java, uses a DefaultTableModel object to store values of a weekly work schedule and displays the schedule in a JTree object. // UseJTable3.java // Create a table object based on a table model (DefaultTableModel). // A weekly work schedule (5 days a week, 8 hours a day) import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; public class UseJTable3 extends JFrame { JTable myTable; DefaultTableModel myModel; public static void main(String []args) { UseJTable3 x = new UseJTable3(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(500, 200); x.setVisible(true); } // main public UseJTable3() // constructor { setTitle("UseJTable3"); // create a table model used to hold data values, like a 2-D array myModel = new DefaultTableModel(9, 6); // 9 rows, 6 columns // Assign table headings (Monday - Friday) at the first row myModel.setValueAt("Monday", 0, 1); myModel.setValueAt("Tuesday", 0, 2); myModel.setValueAt("Wednesday", 0, 3); myModel.setValueAt("Thursday", 0, 4); myModel.setValueAt("Friday", 0, 5); // Put values in table cells (one-hour slots) at the left most column
- 63 -
int t = 9; // starting at 9:00 for (int k=1; k < myModel.getRowCount(); ++k, ++t) myModel.setValueAt(t + ":00", k, 0); // create a JTable object based on a table model myTable = new JTable(myModel); JScrollPane sp = new JScrollPane(myTable); getContentPane().add(sp); } // constructor } // UseJTable3
Via a JTable object, the user can click in a table cell and enter text in it. In a sense, a table can be viewed as a two dimensional array of text boxes. Let us modify the preceding program to eliminate the default column headings by calling the setColumnIdentifiers() method of the DefaultTableModel class. Such a modification eliminates the redundant first row used by the default column headings. The new column names can be stored in a Vector or as an array of objects. This program, UseJTable4.java, uses a String array is used. In the next program example, UseJTable5.java, we will add the necessary features allowing the table contents (columns and cells) to be saved into a text file. Naturally, such a program should be coded so that it is able to create the table model object by reading data values saved in a file. In a general practice, a menu bar is used to communicate with the user interactively to provide additional functions. // Use JTable4.java // Modified from UseJTable3.java to set/change column headings // Create a table object based on a table model (DefaultTableModel). // A weekly work schedule (5 days a week, 8 hours a day) import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; public class UseJTable4 extends JFrame { JTable myTable; DefaultTableModel myModel; public static void main(String []args) { UseJTable4 x = new UseJTable4(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(500, 200); x.setVisible(true); } // main public UseJTable4() // constructor { setTitle("UseJTable4");
- 64 -
// create a table model used to hold data values, like a 2-D array myModel = new DefaultTableModel(8, 6); // 8 rows, 6 columns // A string array is used to store the column names String columnNames[] = { " ", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" }; // Replace the default column names by Monday - Friday myModel.setColumnIdentifiers(columnNames); // Put values in table cells (one-hour slots) at the left most column int t = 9; // starting at 9:00 for (int k = 0; k < myModel.getRowCount(); ++k, ++t) myModel.setValueAt(t + ":00", k, 0); // create a JTable object based on a table model myTable = new JTable(myModel); JScrollPane sp = new JScrollPane(myTable); getContentPane().add(sp); } // constructor } // UseJTable4 More methods of the DefaultTableModel class: Object getValueAt(int row, int column); // Returns a cell value String getColumnName(int column); // Returns the column name void addRow(Object []x); // Appends a row as the last row void addRow(Vector x); // Appends a row as the last row void insertRow(int row, Object []x); // Inserts a row at row index void insertRow(int row, Vector x); // Inserts a row at row index void removeRow(int row); // Deletes a row at row index void removeRow(int row); // Deletes a row at row index void addColumn(Object name); // Appends a column as the last column void addColumn(Object name, Vector columnData); // Appends a column void addColumn(Object name, Object [] columnData); // Appends a column // UseJTable5.java // Synopsis: java UseJTable5 [-new] dataFile // Creating a table model from a text file, if the file exists. // Saving table contents into a text file. import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; import java.io.*; import java.util.*; public class UseJTable5 extends JFrame { JTable myTable; DefaultTableModel myModel;
- 65 -
String fileName; public static void main(String []args) { if (args.length == 0) { System.err.println("Usage: java UseJTable5 [-new] dataFile"); System.exit(1); } if (args.length == 2 && !(args[0].equals("-new"))) { System.err.println("Usage: java UseJTable5 [-new] dataFile"); System.exit(2); } UseJTable5 x; if (args.length == 1) x = new UseJTable5(args[0]); else x = new UseJTable5(args[1], args[0]); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(500, 200); x.setVisible(true); } // main public UseJTable5(String s, String dummy) // constructor { // handling a new data file setTitle("UseJTable5"); fileName = s; // The name for a new file to be created createModel(); // create a JTable object based on a table model myTable = new JTable(myModel); JScrollPane sp = new JScrollPane(myTable); getContentPane().add(sp); createMenu(); } // constructor public UseJTable5(String s) // constructor { // opens up the specified file to create a table model setTitle("UseJTable5"); fileName = s; createModel(s); // create a JTable object based on a table model myTable = new JTable(myModel); JScrollPane sp = new JScrollPane(myTable); getContentPane().add(sp); createMenu(); } // constructor
- 66 -
// Create a table model based a file contents void createModel(String s) { try { String line; StringTokenizer rec; BufferedReader infile = new BufferedReader(new FileReader(s)); line = infile.readLine(); // get the column names rec = new StringTokenizer(line, ","); Vector heading = new Vector(); // to hold column names Vector cells = new Vector(); // to hold cell values while (rec.hasMoreTokens()) heading.addElement(rec.nextToken(",")); Vector temp; // reference to the new row while ((line = infile.readLine()) != null) { temp = new Vector(); // add a new vector to hold the current row values cells.addElement(temp); rec = new StringTokenizer(line, ","); while (rec.hasMoreTokens()) temp.addElement(rec.nextToken(",")); } infile.close(); myModel = new DefaultTableModel(cells, heading); } catch (IOException ioe) { System.err.println(ioe); } } // createModel // hard coding a table model, if the input file does not exist void createModel() { // create a table model used to hold data values, like a 2-D array myModel = new DefaultTableModel(8, 6); // 8 rows, 6 columns // A string array is used to store the colum names String columnNames[] = { " ", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" }; // Replace the default column names by Monday - Friday myModel.setColumnIdentifiers(columnNames); // Put values in table cells (one-hour slots) at the left most column int t = 9; // starting at 9:00 for (int k=0; k < myModel.getRowCount(); ++k, ++t) myModel.setValueAt(t + ":00", k, 0); } // createModel void createMenu()
- 67 -
{ JMenuBar menuBar = new JMenuBar(); setJMenuBar(menuBar); JMenu optionMenu = new JMenu("Option"); menuBar.add(optionMenu); JMenuItem save = new JMenuItem("Save"); optionMenu.add(save); // Event registration and handling // Saving table contents to a file save.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { String temp; PrintWriter outfile = new PrintWriter (new BufferedWriter(new FileWriter(fileName))); // Get and print column names, separated by commas for (int k=0; k < myModel.getColumnCount()-1; ++k) outfile.print(myModel.getColumnName(k)+ ", "); outfile.println(myModel.getColumnName(myModel.getColumnCount()-1)); // Retrieving table cells like retrieving data from a 2-D array for (int k=0; k < myModel.getRowCount(); ++k) { for (int n=0; n < myModel.getColumnCount()-1; ++n) { // get and print a cell value, separated by commas temp = (String) myModel.getValueAt(k, n); outfile.print(temp + ", "); } outfile.println(myModel.getValueAt(k, myModel.getColumnCount()-1)); } outfile.close(); } catch (IOException ioe) { System.err.println(ioe); } } }); } // createMenu } // UseJTable5
The next program demonstrates the capabilities of adding, inserting, and removing rows from a table. The getSelectedRow() method of the JTable class returns the index for the selected row which can then be passed to the removeRow() method of the DefaultTableModel class to remove the row from the table and passed to the DefaultTableModel.insertRow() method to insert a row to a table.
- 68 -
// UseJTable6.java // Allowing rows to be removed, added, and inserted import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.table.*; public class UseJTable6 extends JFrame { JTable myTable; DefaultTableModel myModel; public static void main(String []args) { UseJTable6 x = new UseJTable6(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(500, 200); x.setVisible(true); } // main public UseJTable6() // constructor { setTitle("UseJTable6"); createModel(); // create a JTable object based on a table model myTable = new JTable(myModel); JScrollPane sp = new JScrollPane(myTable); getContentPane().add(sp); createMenu(); } // constructor // hard coding a table model, if the input file does not exist void createModel() { // create a table model used to hold data values, like a 2-D array myModel = new DefaultTableModel(8, 6); // 8 rows, 6 columns // A string array is used to store the colum names String columnNames[] = { " ", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" }; // Replace the default column names by Monday - Friday myModel.setColumnIdentifiers(columnNames); // Put values in table cells (one-hour slots) at the left most column int t = 9; // starting at 9:00 for (int k=0; k < myModel.getRowCount(); ++k, ++t) myModel.setValueAt(t + ":00", k, 0); } // CreateModel
- 69 -
void createMenu() { JMenuBar menuBar = new JMenuBar(); setJMenuBar(menuBar); JMenu optionMenu = new JMenu("Option"); menuBar.add(optionMenu); JMenuItem addRow = new JMenuItem("Add Row"); JMenuItem insertRow = new JMenuItem("Insert Row"); JMenuItem rmRow = new JMenuItem("Delete Row"); optionMenu.add(addRow); optionMenu.add(insertRow); optionMenu.add(rmRow); // Event registration and handling addRow.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String s[] = { " " }; myModel.addRow(s); // append a new row } }); insertRow.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String s[] = { " " }; // insert a new row before the current row myModel.insertRow(myTable.getSelectedRow(), s); } }); rmRow.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // remove the current row myModel.removeRow(myTable.getSelectedRow()); } }); } // createMenu } // UseJTable6
javax.swing.JEditorPane class
- 70 -
+--javax.swing.JComponent | +--javax.swing.text.JTextComponent | +--javax.swing.JEditorPane The javax.swing.JTextComponent class has three sub-classes: JTextField, JTextArea, and JEditorPane. A javax.swing.JEditorPane object is typically used to display a web document in one of three formats/content types: text/html, text/plain, and text/rtf. This class provides a set of methods to support the web page manipulation and has the following constructors: JEditorPane(); JEditorPane(String url); // url is the default page JEditorPane(URL url); // url is the default URL JEditorPane(String type, String text); // type is the content type // Equivalent to calling setContextType() and setText() methods. Some Methods: String getContenetType(); // returns the content type URL getPage(); // returns the current URL String getText(); // returns the current page void setContentType(String type); // set content type void SetPage(URL url ); // display url void setText(String text); // set the page to text void setEditable(boolean v); // Usually set to false for JEditorPane component addHyperlinkListener(HyperlinkListener x); // event registration/capture when a hyper link is selected A web page may contain hyper links which allow the user to connect to other web pages or URL by clicking on a link. Such an event is captured by the javax.swing.event.HyperlinkListener interface/event listener which consists of only one method: public void hyperlinkUpdate(HyperlinkEvent e); This method is called when a hypertext link is selected/clicked. The following program demonstrates the use of the JEditorPane class. This program also implements the Back button of an Internet browser. A java.util.Stack object is used to keep track of URLs visited by the user. // UseEditorPane1.java // Using javax.swing.JEditorPane class to display HTML Pages import java.awt.*; import java.awt.event.*; import java.io.*; import javax.swing.*; import javax.swing.event.*;
- 71 -
import java.util.*; public class UseEditorPane1 extends JFrame { Stack urlStack; // URL history JTextField urlBox; // shows the current URL JButton back; // History button JEditorPane myPane; // The browser's container public static void main(String []args) { UseEditorPane1 x = new UseEditorPane1(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(600, 600); x.setVisible(true); } // main public UseEditorPane1() // constructor { setTitle("My Browser"); String homePage = "http://www.yahoo.com"; urlStack = new Stack(); // URL history urlBox = new JtextField(40); // the URL box back = new JButton("Back"); // history button (Back) JPanel p = new JPanel(); p.add(urlBox); p.add(back); getContentPane().add(p, BorderLayout.SOUTH); try { myPane = new JeditorPane(homePage); // the default url } catch(IOException ioe) { myPane.setText("Exception: " + ioe); } urlBox.setText(homePage); // show the current URL urlStack.push(homePage); // save the current URL in history myPane.setEditable(false); // allow hyper link to work JScrollPane sp = new JScrollPane(myPane); getContentPane().add(sp, BorderLayout.CENTER); // Event registration and handling for hyperlink listener myPane.addHyperlinkListener(new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent e) { // a hyper link is being clicked. if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { try { urlStack.push(e.getURL().toString()); // Save the URL in the history urlBox.setText(e.getURL().toString()); // show the current URL myPane.setPage(e.getURL()); // display the page } catch(IOException ioe) { myPane.setText("Exception: " + ioe); } }
- 72 -
} }); // A new URL is entered in the URL box urlBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { urlStack.push(urlBox.getText()); // save the current URL in the history myPane.setPage(urlBox.getText()); // display the new page } catch(IOException ioe) { myPane.setText("Exception: " + ioe); } } }); // Event registration and handling for the Back button back.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (urlStack.size() <= 1) return; // no history try { urlStack.pop(); // get the last URL String urlString = (String) urlStack.peek(); urlBox.setText(urlString); myPane.setPage(urlString); } catch(IOException ioe) { myPane.setText("Exception: " + ioe); } } }); } // constructor } // UseEditorPane1
Let us modify the preceding program to add the forward/next button allowing the user to retrieve history pages in both backward and forward directions. This program uses a Vector to keep track of visited pages/URLs. The modified program, UseEditorPane2.java, is listed below. // UseEditorPane2.java // Modified from UseEditorPane1.java to add a Next button. // Using javax.swing.JEditorPane class to display HTML Pages import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; public class UseEditorPane2 extends JFrame { Vector urlHistory; // URL history
- 73 -
int current = 0; // current index in the history JTextField urlBox; JButton back, next; // History button JEditorPane myPane; // The browser's container public static void main(String []args) { UseEditorPane2 x = new UseEditorPane2(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(600, 600); x.setVisible(true); } // main public UseEditorPane2() { setTitle("My Browser"); String homePage = "http://www.yahoo.com"; urlHistory = new Vector(); urlBox = new JTextField(30); back = new JButton("Back"); // Back button next = new JButton("Next"); // Forward button JPanel p = new JPanel(); p.add(urlBox); p.add(back); p.add(next); getContentPane().add(p, BorderLayout.SOUTH); myPane = new JEditorPane(); myPane.setEditable(false); // allowing the hyper link to work try { // display the default page myPane.setPage(homePage); urlBox.setText(homePage); urlHistory.addElement(homePage); // saving the home page in the history } catch(IOException ioe) { myPane.setText("Exception: " + ioe); } JScrollPane sp = new JScrollPane(myPane); getContentPane().add(sp, BorderLayout.CENTER); // Event registration and hanlding for hyperlink listener myPane.addHyperlinkListener(new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { try { String urlString = e.getURL().toString(); urlBox.setText(urlString); myPane.setPage(urlString); if (current == urlHistory.size()-1) // at the last page { urlHistory.addElement(urlString);
- 74 -
++current; } else { urlHistory.setElementAt(urlString, current+1); for (int k=current+2; k < urlHistory.size(); ++k) urlHistory.removeElementAt(k); current = urlHistory.size()-1; } } catch(IOException ioe) { myPane.setText("Exception: " + ioe); } } } }); // Event registration and handling for the text box // Add the new URL to the end of the history urlBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { myPane.setPage(urlBox.getText()); urlHistory.addElement(urlBox.getText()); current = urlHistory.size()-1; } catch(IOException ioe) { myPane.setText("Exception: " + ioe); } } }); // Event registration and handling for the Back button back.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (current == 0) return; // at the first page try { --current; String urlString = (String) urlHistory.elementAt(current); urlBox.setText(urlString); myPane.setPage(urlString); } catch(IOException ioe) { myPane.setText("Exception: " + ioe); } } }); // Event registration and handling for the Next (Forward) button next.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (current == urlHistory.size()-1)
- 75 -
return; // no next page try { ++current; String urlString = (String) urlHistory.elementAt(current); urlBox.setText(urlString); myPane.setPage(urlString); } catch(IOException ioe) { myPane.setText("Exception: " + ioe); } } }); } // constructor } // UseEditorPane2
javax.swing.JSplitPane class +--javax.swing.JComponent | +--javax.swing.JSplitPane A javax.swing.JSplitPane object divides a container into exactly two parts. Thus, to divide a container into more two parts/frames, nested JSplitPane objects will be used. The most commonly used constructor is: JSplitPane(int orientation, Component x, Component y); // The orientation can be JSplitPane.HORIZONTAL_SPLIT or JsplitPane.VERTICAL_SPLIT
// UseSplitPane1.java // Using javax.swing.JSplitPane to divide a window/container // Vertical split import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; public class UseSplitPane1 extends JFrame { JSplitPane myPane; public static void main(String []args) { UseSplitPane1 x = new UseSplitPane1(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(300, 300);
- 76 -
x.setVisible(true); } // main public UseSplitPane1() // constructor { JTextField box1 = new JTextField("Hello, world"); JTextArea box2 = new JTextArea(10, 30); box2.setText("JSplitPane is used to divide two (and only two) Components.\n"); box2.setText(box2.getText() + "The two Components are graphically divided\n"); box2.setText(box2.getText() + "based on the look and feel implementation."); setTitle("Horizontal Split"); // box1 on the top myPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, box1, box2); getContentPane().add(myPane, BorderLayout.CENTER); } // constructor } // UseSplitPane1
// UseSplitPane2.java // Using javax.swing.JSplitPane to divide a window/container // Horizontal split import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; public class UseSplitPane2 extends JFrame { JSplitPane myPane; public static void main(String []args) { UseSplitPane2 x = new UseSplitPane2(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(300, 300); x.setVisible(true); } // main public UseSplitPane2() // constructor { JTextField box1 = new JTextField("Hello, world"); JTextArea box2 = new JTextArea(10, 30); box2.setText("JSplitPane is used to divide two (and only two) Components.\n"); box2.setText(box2.getText() + "The two Components are graphically divided\n");
- 77 -
box2.setText(box2.getText() + "based on the look and feel implementation."); setTitle("Vertical Split"); // box1 on the top myPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, box1, box2); getContentPane().add(myPane, BorderLayout.CENTER); } // constructor } // UseSplitPane2
// UseSplitPane3.java // Nested JSplitPane objects import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; public class UseSplitPane3 extends JFrame { JSplitPane innerFrame, outerFrame; JEditorPane myPane; public static void main(String []args) { UseSplitPane3 x = new UseSplitPane3(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(600, 600); x.setVisible(true); } // main public UseSplitPane3() // constructor { setTitle("Nested JSplitPane"); JTextField box1 = new JTextField("Hello, world"); JTextArea box2 = new JTextArea(10, 30); box2.setText("JSplitPane is used to divide two (and only two) Components.\n"); box2.setText(box2.getText() + "The two Components are graphically divided\n"); box2.setText(box2.getText() + "based on the look and feel implementation."); innerFrame = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, box1, box2); try { myPane = new JEditorPane("http://www.yahoo.com"); } catch (IOException ioe) { System.err.println(ioe); } myPane.setEditable(false); JScrollPane sp = new JScrollPane(myPane); outerFrame = new JSplitPane(JSplitPane.VERTICAL_SPLIT, innerFrame, sp);
- 78 -
getContentPane().add(outerFrame, BorderLayout.CENTER); // Event registration and hanlding for hyperlink listener myPane.addHyperlinkListener(new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { try { myPane.setPage(e.getURL()); } catch(IOException ioe) { myPane.setText("Exception: " + ioe); } } } }); } // constructor } // UseSplitPane3 javax.swing.JTabbedPane class +--javax.swing.JComponent | +--javax.swing.JTabbedPane This class is used to instantiate containers which allow the user to select a group of tabs and provides three constructors: JTabbedPane(); JTabbedPane(int placement); JTabbedPane(int placement, int layout); The placement can be JTabbedPane.TOP, JTabbedPane.BOTTOM, JTabbedPane.LEFT, or JTabbedPane.RIGHT The layout can be: JTabbedPane.WRAP_TAB_LAYOUT or JTabbedPane.SCROLL_TAB_LAYOUT. The methods used to add tabs to the JTabbedPane container are: void addTab(String title, Component x); // Adds a component represented by a title void addTab(String title, Icon, icon, Component x); // with an icon void addTab(String title, Icon, icon, Component x, String tip); // tip is the tooltip to be displayed for the tab The addChangeListener() method is the event handling method. The interface ChangeListener (javax.swing.event package) declares only one method: public void stateChanged(ChangeEvent event); The next program, UseTabbedPane1.java, demonstrates the use of JTabbedPane objects.
- 79 -
// UseTabbedPane.java // Using JTabbedPane objects import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; public class UseTabbedPane1 extends JFrame { JTabbedPane myPane1, myPane2; JEditorPane urlPane; // used to display HTML pages String []urlList = { "http://www.yahoo.com", "http://www.utii.com", "http://www.mcinfonet.org", "http://www.google.com", "http://www.yaofamily.org" }; public static void main(String []args) { UseTabbedPane1 x = new UseTabbedPane1(); x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); x.setSize(200, 400); x.setVisible(true); } // main public UseTabbedPane1() // constructor { setTitle("Using Tabbed Pane"); // The first tab has a wrap-tab layout myPane1 = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT); myPane1.addTab("Hello", new JTextField("Hello")); myPane1.addTab("Howdy", new JLabel("Howdy")); for (int k=1; k <= 6; ++k) myPane1.addTab("Hey" + k, new JButton("Hey" + k)); // The second tab uses a scroll-tab layout myPane2 = new JTabbedPane(JTabbedPane.BOTTOM, JTabbedPane.SCROLL_TAB_LAYOUT); try { urlPane = new JEditorPane(urlList[0]); } catch (IOException ioe) { System.err.println(ioe); } myPane2.add("Yahoo", null); myPane2.add("UTI", null);
- 80 -
myPane2.add("MC", null); myPane2.add("Google", null); myPane2.add("Home Page", null); getContentPane().add(myPane1, BorderLayout.NORTH); // add a container for displaying web pages getContentPane().add(new JScrollPane(urlPane), BorderLayout.CENTER); getContentPane().add(myPane2, BorderLayout.SOUTH); // Event registration and hanlding when a tab is clicked/selected myPane2.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { int n = myPane2.getSelectedIndex(); if (n < 0 ) return; // no tab was selected try { // display the page associated with the selected tab urlPane.setPage(urlList[n]); } catch (IOException ioe) { System.err.println(ioe); } } // stateChanged }); } // constructor } // UseTabbedPane1
To find out which tab is being clicked/selected by the user, the method getSelectedIndex() can be used, as shown in the preceding program. Other useful methods include: Component getSelectedComponet(); // returns the currently selected component Component getComponetAt(int index); // returns the component at index Icon getIconAt(int index); // returns the icon at index int indexOfTab(String title); // returns the index of the tab specified by title void inserTab(String title, Icon, icon, Component x, String tip); String getTitleAt(int tabIndex); // returns the title of a tab at the tabIndex int getTabCount(); // returns the number of tabs in this container void void void void void setTabLayoutPolicy(int layout); setTabPlacement(int placement) setTitleAt(String title, int index); setSelectedIndex(int index); setSelectedComponent(Component x);
- 81 -
of course, like other containers, a JTabbedPane object uses the add() methods to add component to it.
- 82 -
- 83 -
Array5.java // Using an array of Object references /* Object is the top level class in Java. * Thus, a Object reference can point to any objects. **/ import java.awt.*; public class Array5 { public static void main(String []args) { Object []myArray; // a reference to an Object array myArray = new Object[5]; // an array of 5 Object references myArray[0] = new String("I am a String"); myArray[1] = new Button("Go"); myArray[2] = new int[3]; // assign 99 to the first int variable of the array ((int []) myArray[2])[0] = 99; myArray[3] = new TextField("Hello"); myArray[4] = myArray[0]; /* Calling the Object.getClass() method to determine the class of an object. The Class.getName() returns the name of the class for an object.*/ for (int k=0; k < myArray.length; ++k) { System.out.print(k + " "); System.out.print(myArray[k].getClass().getName() + ": "); if (myArray[k] instanceof Button) System.out.println(((Button) myArray[k]).getLabel()); else if (myArray[k] instanceof TextField) System.out.println(((TextField) myArray[k]).getText()); else if (myArray[k] instanceof String) System.out.println(myArray[k]); else System.out.println(((int[]) myArray[k])[0]); } System.exit(0); } // main } // Array5 The instanceof operator (a boolean operator) determines if an object is created from a specific class. The getClass() of the java.lang.Object class returns a reference to the class from which an object is created. The getName() method of the java.lang.Class class returns the name of the current class. Other useful methods declared in java.lang.Class include getSuperclass() and getPackage(). To display the package name of super class name for the Button class from the above program, the following two statements can be added: System.out.println(myArray[1].getClass().getSuperclass().getName());
System.out.println(myArray[1].getClass().getPackage());
- 84 -
The static modifier can only be applied to class members (attributes, methods, and inner classes). The static modifier cannot be applied to local variables. A static attribute is known as a class attributes and will not be instantiated when an object is created from the class. In a sense, the static attribute belongs to the class and is accessible to all objects instantiated from the class and its subclasses. Thus, a static attribute can also be viewed as a global variable in the entire inheritance tree (with objects and subclasses being considered as the leaves and branches of the tree). Non-static attributes are generally known as instance variables or instance attributes and are local to the object that creates them. Summary of the static modifier A static attribute is accessible to all part of the class. Regardless the number of objects created from the class, there is one instance (copy) of the static attribute for the entire class inheritance tree. In a sense, a static attribute is global to the class and its subclasses. It is also known as a class attribute. No local variables (defined inside a method and in the header of a method) can be static. A static method cannot directly access non-static members (attributes, methods, inner classes). A static method can be invoked without having to create an object, does not operate on any object, and is known as a class method. The keyword this reference cannot be used in a static method. It is possible to access a static method from a non-static method. The general syntax for accessing a static method is: ClassName.MethodName(...). For example, System.out.println(Math.random()); A static method cannot be abstract. An inner class can be declared as static, if it needs to be accessible to static methods. An inner class is a member of another class. Only inner classes can be declared as static.
static Attribute
static Method
static Class
The next program illustrates the use of the static attribute. This program consists of three classes: (1) The driver class Modifier4, (2) MyClass4, and (3) MyClass41 (a subclass derived from MyClass4). // Modifier4.java // Using static attribute and inheritance public class Modifier4 { public static void main(String []x) { MyClass4 A = new MyClass4("MyClass4 object A"); MyClass4 B = new MyClass4("MyClass4 object B");
- 85 -
A.setValue(10); // value = 10 System.out.println(A); System.out.println(B); B.setValue(25); // value = 25 System.out.println(A); System.out.println(B); MyClass41 C = new MyClass41("MyClass41 Object C"); System.out.println(C); System.out.println(A); System.out.println(B); } // main } // Modifier4 Notice that there is only one copy of value, regardless how many objects are created from the MyClass4 inheritance tree because value is defined as a static attribute in MyClass4. This program creates two objects (ObjectA and ObjectB) from Myclass4 and one object (ObjectC) from MyClasse41. The output displayed by the above program is shown below. The output shows that there is only copy of the value attribute, even though there are three objects instantiated from MyClass4 and MyCVlass41. MyClass4 object A: 10 MyClass4 object B: 10 MyClass4 object A: 25 MyClass4 object B: 25 MyClass41 Object C: 25 MyClass4 object A: 25 MyClass4 object B: 25
MyClass4.java public class Myclass4 { String msg; static int value; // constructor MycCass4 (String s) { msg = s; } public void setValue(int k) { value = k; } public String toString() { return (msg + ": " + value); } } // MyClass4
MyClass41.java public class MyClass41 extends MyClass4 { int x; /* The constructor itself does not need a parameter; however its parent needs a String parameter. The keyword super is used to call the parent class. */ MyClass41(String s) // constructor { super(s); // call parent classs constructor } } // MyClass41
- 86 -
This chapter gives you a brief introduction to using the JavaTM Foundation Classes (JFC) Swing packages. After telling you about JFC and Swing, it helps you get the necessary software and walks you through how to compile and run a program that uses the Swing packages. Next, it shows you how to run programs using Java Web Start. The next chapter, Learning Swing by Example (page 11), will build on these first steps to help you create several increasingly more complex examples. For now, let's start with the basics.
o o
Which Releases Contain the Swing API? Which Swing Packages Should I Use?
o o o o o
Install the Latest Release of the Java 2 Platform Create a Program That Uses Swing Components Compile the Program Run the Program Running Programs Using Java Web Start