Você está na página 1de 87

JAVA PROGRAMMING LAB LAB MANUAL

CENTRE FOR DISTANCE EDUCATION ANNA UNIVERSITY CHENNAI

ABOUT THIS LAB MANUAL


INTRODUCTION This lab manual for Java Programming is prepared for M.C.A Degree offered by Centre for Distance Education, Anna University, Chennai. There are six chapters in this manual covering how to write java programs for simple java applications and also for the features like string handling, interface, exception handling, threading, java data base connectivity etc. SETTING UP YOUR HOME COMPUTER You do not need to own a computer to take this lab, but since many of you do have one and will want to have one at home, we tell you where and how to go about setting up a Java programming environment in your computer. This lab needs the Java SDK and a text editor named TextPad. If you install these two pieces of software on your computer you will have a Java programming environment that is recommended for use in the course. Two pieces of software can also be downloaded off the internet. The next two sections tell you how to do this. DOWNLOADING THE JAVA SDK FOR YOUR COMPUTER Java was developed at Sun Microsystems Inc and there is a big web site devoted to Java. It contains news, tutorials, and the software needed to develop and run Java program: http://java.sun.com/ If you want to set up your computer with the software needed to compile and run Java programs go the java.sun.com site and click on the J2SE 1.5 SDK link in the Popular
Downloads box on the right of the screen. Don't worry if the numbers are different. That just

means it is a newer version.

Question: What do all those letters mean? Answer: Java 2 Standard Edition (version) 1.5 Software Development Kit. From here on we just call it the Java 2 SDK or simply the SDK. The SDK consists of a compiler, virtual machine, applet viewer, a large library of useful predefined classes, and a few other tools. Back to downloading the SDK. You should be at the following web site: http://java.sun.com/j2se/1.5.0/download.jsp Now look at the heading, Download J2SE v 1.4.1_03 (the numbers may be different), find the version appropriate for you (probably: Windows (all languages, including
English)) and click on the download link in the SDK column. That will take you to a page

that gathers some optional information. Click on the download link again and you will go to another page with a license agreement. If you click on ACCEPT button and then click on
Download j2sdk-1_4_1_03-windows-i586.exe an executable file will be delivered to your

computer. This files about 38 MB in size and takes 15-20 minutes to download on a fast internet connection. To install the Java JDK, simply click on the j2sdk-1_4_1_03- icon to install the Java JDK. DOWNLOADING TEXTPAD The other thing you need to develop programs at home is a text editor. Any text editor will do but there is one in particular, called TextPad that can also be used to compile and run Java programs assuming that you have installed a Java SDK. It is shareware and can be downloaded from the following site: http://www.textpad.com Shareware is not free. You can download it and test-drive it

HISTORY Around 1990, James Gosling, Bill Joy and others at Sun Microsystems began developing a language called Oak. The wanted it primarily to control microprocessors embedded in consumer items such as cable set-top boxes, VCR's, toasters, and also for personal data assistants (PDA). To serve these goals, Oak needed to be:
o o o

Platform independent (since multiple manufacturers involved) Extremely reliable Compact.

However, as of 1993, interactive TV and PDA markets had failed to take off. Then the Internet and Web explosion began, so Sun shifted the target market to Internet applications and changed the name of the project to Java. By 1994 Sun's Hot Java browser appeared. Written in Java in only a few months, it illustrated the power of applets, programs that run within a browser, and also the capabilities of Java for speeding program development. Riding along with the explosion of interest and publicity in the Internet, Java quickly received widespread recognition and expectations grew for it to become the dominant software for browser and consumer applications. However, the early versions of Java did not possess the breadth and depth of capabilities needed for client (i.e. consumer) applications. For example, the graphics in Java 1.0 seemed crude and clumsy compared to mature software developed with C and other languages. Applets became popular and remain common but don't dominate interactive or multimedia displays on web pages. Many other "plug-in" types of programs also run within the browser environment.

So Java has not succeeded at development of consumer applications. However, Java's capabilities grew with the release of new and expanded versions (see below) and it became a very popular language for development of enterprise, or middleware, applications such as on line web stores, transactions processing, database interfaces, and so forth. Java has also become quite common on small platforms such as cell phones and PDAs. Java is now used in several hundred cell phone models. Over 600 million Java Cards, smart cards with additional features provided by Java, have been sold as of the summer of 2004. DEFINITION The term Java actual refers to more than just a particular language like C or Pascal. Java encompasses several parts, including:

A high level language the Java language is a high level one that at a glance looks very similar to C and C++ but offers many unique features of its own.

Java byte code - a compiler, such as Sun's javac, transforms the Java language source code to byte code that runs in the JVM.

Java Virtual Machine (JVM) a program, such as Sun's java, that runs on a given platform and takes the byte code programs as input and interprets them just as if it were a physical processor executing machine code.

Sun provides a set of programming tools such as javac, java and others in a bundle that it calls a Java Software Development Kit for each version of the language and for different platforms such as Windows, Linux, etc... Sun also provides a runtime bundle with just the JVM when the programming tools are not needed. For example, just as many different languages can create machine code for a given processor, compilers of other languages have been created that output byte code to run in the JVM. Similarly, many JVMs have been written by groups outside of Sun.

Java, Open or Closed? Java is not quite an open language but not quite a proprietary one either. All the core language products - compiler, virtual machines (VM), class packages, and other components - are free. Detailed specifications and source code are made openly available. The Java Community Process (JCP) leads the development of new standards for the language. Other companies and organizations can legally create a clean sheet compiler and/or a Virtual Machine as long as it follows the publicly available specifications. Microsoft did this with the Version 1.1 JVM that it used in its Internet Explorer browser.

Sun, however, does still assert final say on the specifications and controls the copyrights to logos, and trademarks. For example, Microsoft's VM differed in a some significant details from the specifications and Sun accused Microsoft of attempting to weaken Java's "write once, run anywhere" capabilities. Sun sued Microsoft and the dispute was later settled out of court. JAVA PROCEDURE The essential steps to creating and running Java programs go as follows:

Create a Java source code file Compile the source code Run the compiled code in a Java Virtual Machine.

The following figure illustrates these steps:

Figure: Steps for creating and running a Java program. You create the code with a text editor and save it to a file with the ".java" suffix. All Java source code files must end with this type name. The first part of the name must match the class name in the source code. In the figure the class name is Test so you must therefore save it to the file name Test. java. We will discuss what class actually means in later chapters. With the javac program, you compile this file as follows: C :> javac Test. java This creates a byte code file (or files if the code file included more than one class) that ends with the "class" type appended. Here the output is Test. class.

The byte code consists of the instructions for the Java Virtual Machine (JVM or just VM). The JVM is an interpreter program that emulates a processor that executes the byte code instructions just as if it were a hardware processor executing native machine code instructions. There are now Java processors that directly execute the byte codes directly in hardware. The platform independence of Java thus depends on the prior creation of JVMs for different platforms. The Java byte code can then run on any platform in which the JVM is available and the program should perform the same. This Write Once, Run Anywhere approach is a key goal of the Java language. With the class file, you run this file as follows: C :> java Test

CONTENTS
1. SIMPLE JAVA APPLICATIONS & STRING HANDLING Hello World Java class Reading Text from Standard Input Reading an Input from Console Handling SubString String Tokenizer String versus String Buffer Reverse a Word toString Program Beep and Date Program 2. PACKAGES Introduction Java Package User-defined Package 3. INTERFACES Programming with Interfaces Separating Interface and Implementation Simple Relatable Interface Using a Interface as Type Rewriting Interfaces 39 29 11

4. THREADS Java Threads Simple Java Thread Program A Counter Class The Thread_Ex Class Locks Grained Locks Semaphores 5. EXCEPTION HANDLING Introduction Handling of Exceptions User-defined Exceptions Trace Class Reraise Class 6. JDBC What is JDBC? Using a JDBC driver Connecting to Database Structuring Statement Parsing a text file into a Database table Processing a Result Set Printing a Database Table

51

62

77

10

Chapter

A SIMPLE JAVA APPLICATION AND STRING HANDLING


Objectives

Understanding classes and their contents Understanding objects and their creations Object initializations using constructors Getting Input from User Understating String Manipulation To implement String Tokenizer

11

The following steps involved to create a simple java program

Step 1: Use an editor to enter the following code for the Hello Word App program:
HelloWorldApp Application

Public class HelloWorldApp { public static void main (String arg []) { System.out.println ("Hello World!"); } } Save this code in a file called HelloWorldApp.java Step 2: Compile the application with the command line: javac HelloWorldApp.java

12

This creates the class file (with the byte code output): HelloWorldApp.class

Step 3: Use the java command to run the program: Java HelloWorldApp

Hello World! The output is printed after the command line. READING TEXT FROM STANDARD INPUT Import java.io.BufferedReader; Import java.io.InputStreamReader; Import java.io.IOException;

Public class ReadFromConsole {

Private static void process (String STR) { System.out.println (processing... > + str + "\n"); }

Private static void doReadFromStdin() {

try {

BufferedReader in Stream = new Buffered Reader ( New InputStreamReader(System. in) );

String inLine = "";

13

While (! (inLine.equalsIgnoreCase ("quit")) && !(inLine.equalsIgnoreCase("exit")) ) { System.out.print("prompt> "); InLine = inStream.readLine (); process(inline); }

} catch (IOException e) {

System.out.println("IOException: " + e);

Public static void main (String[] args) { doReadFromStdin(); } }

READ AN INPUT FROM CONSOLE

import java.io.*; Public class ReadIntFromConsole {

private static void doReadIntFromConsole() {

String inline = null; Int check Integer = 0;

14

Try { Buffered Reader in Stream = new BufferedReader ( new InputStreamReader(System.in) ); System.out.print("Enter a valid Integer and hit <ENTER>: "); Inline = inStream.readLine(); Check Integer = Integer.parseInt(inline);

} catch (NumberFormatException nfe) { System.out.println("You did not enter a valid Integer: " + inline); Return; } catch (IOException e) { System.out.println("IOException: " + e); return; }

System.out.println("You entered a valid Integer: " + checkInteger);

} Public static void main (String [] args) { doReadIntFromConsole(); }


}

EXERCISE Completion of the following simple exercises will insure that you have successfully installed your JDK and know the basic mechanics of creating source code files (i.e. the *.java files), compilation, creating applets and running applications.

15

1. Create your own application program that prints out your name to the command line. STRING HANDLING

INTRODUCTION Strings are objects. The String class is included in the java.lang.String package. Specifically they're instances of the class java.lang.String. This class has many methods that are useful for working with strings. Internally Java Strings are arrays of Unicode characters. For example the String "Hello" is a five element array. Like arrays, Strings begin counting at 0. Thus in the String "Hello" 'H' is character 0, 'e' is character 1, and so on. 01234 Hello

CONSTRUCTORS Public String () Public String (String value) Public String (char value []) public String(char value[], into offset, into count) public String(byte bytes[], int offset, int length, String enc) Throws UnsupportedEncodingException Public String (byte bytes [], String enc) throws UnsupportedEncodingException public String(byte bytes[], int offset, int length) Public String(byte bytes []) public String (StringBuffer buffer) index methods

METHODS

public int length () public int indexOf (int ch)

16

public int indexOf (int ch, int fromIndex) public int lastIndexOf (int ch) public int lastIndexOf (int ch, int fromIndex) public int indexOf (String str) public int indexOf (String str, int fromIndex) public int lastIndexOf(String str) public int lastIndexOf (String str, int fromIndex) valueOf () methods public static String valueOf (char data []) public static String valueOf (char data [], int offset, int count) public static String copyValueOf (char data [], int offset, int count) public static String copyValueOf (char data []) public static String value Of (boolean b) public static String valueOf(char c) public static String valueOf (int i) public static String valueOf (long l) public static String valueOf (float f) public static String valueOf (double d) substring () methods public char charAt (int index) public void get Chars (int srcBegin, int srcEnd, char dst [], int dstBegin) public byte[] getBytes(String enc) throws UnsupportedEncodingException public byte [] getBytes () public String substring (int beginIndex) public String substring(int beginIndex, int endIndex) public String concat(String str) public char [] toCharArray () Beginnings are inclusive. Ends are exclusive. COMPARISONS public boolean equals (Object anObject) public boolean equalsIgnoreCase(String anotherString) public int compareTo(String anotherString)

17

public boolean regionMatches (int toffset, String other, int ooffset, int len) public boolean regionMatches (boolean ignoreCase, int toffset, String other, int ooffset, int len) public boolean startsWith(String prefix, int toffset) public boolean startsWith (String prefix) public boolean endsWith (String suffix) Modifying Strings public String replace (char oldChar, char newChar) public String toLowerCase (Locale locale) public String toLowerCase () public String toUpperCase (Locale locale) public String toUpperCase () public String trim ()

Substring

Public class Substring {

private static void doSubstring () {

String a = "Alex Michael Hunter"; System.out.println("Original string: " + a);

String b = a.substring (5); System.out.println ("a.Substring (5): " + b);

String c = a.substring (5, 12); System.out.println ("a.substring (5, 12): " + c);

String d = a.substring (13,a.length ()); System.out.println ("a. substring (13, a.length ()): " + d);

18

System.out.println(); }

Public static void main (String [] args) { doSubstring(); }

STRING TOKENIZER

import java.util.*; Public class String TokenizerDemo { private static void doStringTokenizer1 () { String a = "Alex Michael Hunter"; System.out.println ("-------------------------"); System.out.println ("Original string : " + a);

StringTokenizer st = new StringTokenizer (a); While (st.hasMoreTokens ()) { System.out.println ("Token: " + st.nextToken ()); } System.out.println (); }

private static void doStringTokenizer2 () {

printResults2 ("A|B|C|D, process2 ("A|B|C|D")); printResults2 ("A||C|D, process2 ("A||C|D")); printResults2 ("A|||D|E, process2 ("A|||D|E"));

19

} private static void printResults2 (String input, String [] outputs) { System.out.println("Input: " + input); for (int i=0; i<outputs.length; i++) { System.out.println ("Output " + i + " was: " + outputs[i]); } } private static String [] process2 (String line) {

int

MAXFIELDS = 5; = "|";

String DELIM

String [] results = new String [MAXFIELDS]; // Unless you ask StringTokenizer to give you the tokens, it silently // discards multiple tokens. StringTokenizer st = new StringTokenizer(line, DELIM, true);

int i = 0;

// Stuff each token into the current user while (st.hasMoreTokens ()) {

String s = st.nextToken (); if (s.equals (DELIM)) { if (i++ >= MAXFIELDS) { // This is messy: A vector would be better to allow any // number of fields Throw new IllegalArgumentException ( "Input line " + line + " has too many fields"); }

20

continue; } Results[i] = s;

} // while return results; } public static void main (String [] args) { doStringTokenizer1 (); doStringTokenizer2 (); }

STRING BUFFER VERSUS STRING:

Java provides two classes: String and String Buffer. The String class is used to store and manipulate character strings that cannot be changed. Another Way of describing this is to say that Strings are read only and immutable. The String Buffer class, on the other hand, is used to represent characters that can be modified. The significant difference between these two classes is performance where String Buffer is faster than String when performing simple concatenations. When a String is being manipulated in code, character string is routinely Concatenated like shown in the following example:

String name = new String ("Alex"); name += ", Hunter";

Now consider the same concatenation, but using a String Buffer:

String Buffer name = new String Buffer ("Alex");

21

name.append (", Hunter");

Now to many developers, the examples above may seem very similar. The + operator appears innocent, but the code generated may produce some surprises. Using a String Buffer for concatenation can in fact produce code that is significantly faster than using a String. To discover why this is the case, Lets examine the generated byte code from our two examples. The byte code for the example using String looks like this:

% javap -c t

//Compiled from t.java

public class t extends java.lang.Object { public t (); public static void main (java.lang.String []); }

Method t () 0 aload_0 1 invokespecial #1 <Method java.lang.Object ()> 4 return

Method void main (java.lang.String []) 0 new #2 <Class java.lang.String> 3 dup 4 ldc #3 <String "Alex"> 6 invokespecial #4 <Method java.lang.String(java.lang.String)> 9 astore_1 10 new #5 <Class java.lang.StringBuffer> 13 dup 14 invokespecial #6 <Method java.lang.StringBuffer()> 17 aload_1 18 invokevirtual #7 <Method java.lang.StringBuffer append(java.lang.String)> 21 ldc #8 <String ", Hunter">

22

23 invokevirtual #7 <Method java.lang.StringBuffer append(java.lang.String)> 26 invokevirtual #9 <Method java.lang.String toString()> 29 astore_1 30 return

The byte code at locations 0 through 9 is executed for the first line of code, namely:

String name = new String("Alex");

Then, the byte code at location 10 through 29 is executed for the concatenation:

name += ", Hunter";

Here is where things get interesting. The byte code generated for the Concatenation creates a String Buffer object, then invokes its append method: The temporary String Buffer object is created at location 10, and its append Method is called at location 23. Because the String class is immutable, a String Buffer must be used for concatenation.

After the concatenation is performed on the String Buffer object, it must be converted back into a String. This is done with the call to the toString Method at location 26. This method creates a new String object from the Temporary String Buffer object. The creation of this temporary String Buffer Object and its subsequent conversion back into a String object are very expensive.

In summary, the two lines of code above result in the creation of three Objects:

1.) A String object at location 0 2.) A String Buffer object at location 10

23

3.) A String object at location 26

Now, let's look at the byte code generated for the example using String Buffer:

% javap -c t

//Compiled from t.java

public class t extends java.lang.Object { public t (); public static void main (java.lang.String[]); } Method t() 0 aload_0 1 invokespecial #1 <Method java.lang.Object ()> 4 return Method void main (java.lang.String[]) 0 new #2 <Class java.lang.StringBuffer> 3 dup 4 ldc #3 <String "Alex"> 6 invokespecial #4 <Method java.lang.StringBuffer(java.lang.String)> 9 astore_1 10 aload_1 11 ldc #5 <String ", Hunter"> 13 invokevirtual #6 <Method java.lang. String Buffer appends (java.lang.String)> 16 pop 17 return

The byte code at locations 0 to 9 is executed for the first line of code:

String Buffer name = new String Buffer ("Alex");

The byte code at location 10 to 16 is then executed for the concatenation:

24

name. Append (", Hunter");

Notice that, as is the case in the first example, this code invokes the append method of a String Buffer object. Unlike the first example, however, there is no need to create a temporary String Buffer and then convert it into A String object. This code creates only one object, the String Buffer, at location 0. To summarize, String Buffer concatenation is significantly faster than String Concatenation. Obviously, String Buffers should be used in this type of Operation when possible. If the functionality of the String class is desired, consider using a String Buffer for concatenation and then performing one Conversion to String.

Public class StringBufferDemo {

public static void main (String [] args) {

StringBuffer name = new String Buffer ("Alex"); Name. append (", Hunter");

// One way to convert a String Buffer to a String String nameStr1 = name.toString ();

// allocates a new string that contains the sequence of characters // currently contained in the string buffer argument. String nameStr2 = new String (name);

System.out.println("name

: " + name);

System.out.println("nameStr1 : " + nameStr1); System.out.println("nameStr2 : " + nameStr2);

25

REVERSE STRING WORD import java.util.*; public class StringReverseWord {

private static void doStringReverseWord() {

String a = "Alex Michael Hunter"; Stack stack = new Stack (); StringTokenizer tempStringTokenizer = new StringTokenizer (a);

while (tempStringTokenizer.hasMoreTokens()) { stack.push (tempStringTokenizer.nextElement ()); }

System.out.println("\nOriginal string: " + a);

System.out.print("Reverse word string: "); while(!stack.empty()) { System.out.print (stack.pop ()); System.out.print (" "); } System.out.println("\n"); }

26

public static void main (String [] args) { doStringReverseWord(); }

TO STRING

Used to provide an example of how to utilize how to format objects using the toString () method. The toString () method is called whenever an object is being passed to System.out.println (), any other equivalent method, or when involved in a string concatenation.

public class ToStringExample {

int x; int y;

public ToStringExample (int xIn, int yIn) { this.x = xIn; this.y = yIn; }

public String to String () { return "Object members => [ x=" + x + ", y=" + y + " ]"; }

public static void main(String[] args) {

ToStringExample a1 = new ToStringExample(10, 20);

27

System.out.println (a1);

BEEP AND DATE import java.util.*; public class BeepDate { public static void main (String [] args) { System.out.println (new Date () + "\007"); } }

28

Chapter

PACKAGES
Objectives

Understanding Java Package Accessing the class members in a Package Package Implementation

PACKAGES A Java package is a mechanism for organizing Java classes into namespaces similar to the modules of Modula. Java packages can be stored in compressed files called JAR files, allowing classes to download faster as a group rather than one at a time. Programmers also typically use packages to organize classes belonging to the same category or providing similar functionality. Java source files can include a package statement at the top of the file to designate the package for the classes the source file defines.

A package provides a unique namespace for the types it contains. Classes in the same package can access each other's protected members. A package can contain the following kinds of types.

29

o o o o

Classes Interfaces Enumerated types Annotations

Using packages In Java source files, the package that the file belongs to is specified with the package keyword. Package java.awt.event; To use a package inside a Java source file, it is convenient to import the classes from the package with an import statement. The statement import java.awt.event.*; imports all classes from the java.awt.event package, while import java.awt.event.ActionEvent; Imports only the Action Event class from the package. After either of these import statements, the Action Event class can be referenced using its simple class name: Action Event my Event = new Action Event (); Classes can also be used directly without an import statement by using the fully-qualified name of the class. For example, java.awt.event.ActionEvent myEvent = new java.awt.event.ActionEvent(); Doesnt require a preceding import statement.

30

[edit] Package access protection Classes within a package can access classes and members declared with default access and class members declared with the protected access modifier. Default access is enforced when neither the public, protected nor private access modifier is specified in the declaration. By contrast, classes in other packages cannot access classes and members declared with default access. Class members declared as protected can be accessed from the classes in same as well as classes in other packages that are subclasses of the declaring class. [edit] Creation of JAR files JAR Files are created with the jar command-line utility. The command jar cf myPackage.jar *.class Compresses all *.class files into the JAR file myPackage.jar. The ' c ' option on the command line tells the jar command to "create new archive." The ' f ' option tells it to create a file. The file's name comes next before the contents of the JAR file. [edit] Package naming conventions Packages are usually defined using a hierarchical naming pattern, with levels in the hierarchy separated by periods (.) (Pronounced "dot"). Although packages lower in the naming hierarchy are often referred to as "sub packages" of the corresponding packages higher in the hierarchy, there is no semantic relationship between packages. The Java Language Specification establishes package naming conventions in order to avoid the possibility of two published packages having the same name. The naming conventions describe how to create unique package names, so that packages that are widely distributed will have unique namespaces. This allows packages to be separately, easily and automatically installed and catalogued. In general, a package name begins with the top level domain name of the organization and then the organization's domain and then any sub domains listed in reverse order. The

31

organization can then choose a specific name for their package. Package names should be all lowercase characters whenever possible. For example, if an organization in Canada called My Soft creates a package to deal with fractions, naming the package ca.mysoft.fractions distinguishes the fractions package from another similar package created by another company. If a US company named My Soft also creates a fractions package, but names it us.mysoft.fractions, then the classes in these two packages are defined in a unique and separate namespace. [edit] Core packages in J2SE 6.0 java.lang java.util java.io java.math java.nio java.net basic language functionality and fundamental types collection data structure classes file operations multiprecision arithmetics the New I/O framework for Java networking operations, sockets, DNS lookups, ...

java.security key generation, encryption and decryption java.sql java.awt Java Database Connectivity (JDBC) to access databases basic hierarchy of packages for native GUI components

javax.swing hierarchy of packages for platform-independent rich GUI components

Many implementations of Java use a hierarchical file system to manage source and class files.

32

Import

Packages

Access. java -- access to predefined packages user defined packages

33

The Java Program: Access.java 1 // Access.java -- illustrate access to predefined packages 2 // No import statement required for package "java.lang" 3 public class Access { 4 5 6 7 8 9 10 11 12 13 14 System.out.println (Math.PI); } } // class "Math" is also defined in package "java.lang" // "PI" is a constant class variable of type "double" public static void main (String args []) { // class "System" is defined in package "java.lang" // "out" is class variable of type "java.io.PrintStream" // "println" is an overloaded method in class "PrintStream"

The Java Program: Pack.java 1 // Pack.java -- illustrate the use of user-defined packages 2 import tools.*; 3 public class Pack { 4 5 6 7 8 9 10 }
}

public static void main (String args []) { tools.Hammer claw = new tools.Hammer(); tools.Wrench crescent = new tools.Wrench(); claw.id (); crescent.id ();

tools.Hammer.java

34

The Java Program: tools/Hammer. java 1 // Hammer.java -- part of tools package illustrating packages 2 package tools; 3 public class Hammer { 4 public void id () {System.out.println ("Hammer") ;} 5}
o

tools.Wrench.java

The Java Program: tools/Wrench.java 1 // Wrench.java -- part of tools package illustrating packages 2 package tools; 3 public class Wrench { 4 public void id () {System.out.println ("Wrench") ;} 4}
o

javac Pack.java tools/Hammer.java tools/Wrench.java java Pack

Access specifiers

private -- accessible only in the class no modifier, so-called "package" access -- accessible only in the same package protected -- accessible (inherited) by subclasses, and accessible by code in same package

public -- accessible anywhere the class is accessible, and inherited by subclasses

Notice that private protected is not syntactically legal.

35

Summary of access to fields in Java access by the class itself a subclass in same package non-subclass in same package a subclass in other package non-subclass in other package Example

private "package" protected public yes no no no no yes yes yes no no yes yes yes yes no yes yes yes yes yes

One/Main. java

The Java Program: one/Main. java 1 package one; 2 3 import another.Unrelated; 4 // Can't import "another.AlsoSub"; it is not public 5 // Can't import "one.Unrelated"; same name as in "another" 6 public class Main { 11 12 13 14 15 17 18 19 20} 21 class Sub extends Main { 22 23 private int d = a; private int e = z; // can access "package" variables // can access protected variables of "Main"
36

int a, b, c; public int x;

private int y; protected int z; Unrelated u; one.Unrelated uu; // can access "one.Unrelated"

public static void main (String args[]) { }

24 } 25 class AlsoUnrelated { 26 27 28 29 }

private Main m = new Main(); private int f = m.a; // can access "package" variables private int g = m.z; // can access protected variables of "Main"

another/Unrelated.java

The Java Program: another/Unrelated.java 1 packages another; 2 import one.Main; 3 class AlsoSub extends Main { 4 5 6} 7 public class Unrelated { 8 9 private Main m = new Main(); private int h = m.x; // can only access public methods of "Main" private int i = x; // can access public methods of "Main" private int j = z; // can access protected variables of "Main"

10 }

one/Unrelated.java

The Java Program: one/Unrelated.java 1 // Same name as the class in package "another"! 2 package one; 3 class Unrelated { 4 } javac one/Main.java another/Unrelated.java java one.Main Classes in the same package can be in different directories. dir1/dir2/A/B/C.class
37

dir3/A/B/D.class java -classpath dir1/dir2:dir3 ... Packages are simple-minded in Java and dangerous. You can accidentally use the wrong classes. So if you misspell a class name and there happens to be a class by that name in the same directory, Java will not detect it. import java.util.*; // imports java.util.Date import java.sql.*; // also import java.util.Date public class Collision { Hash Map m; Date d; }

38

Chapter

INTERFACE
Objectives

Need for Interface Java Interface Separation of Interface from Implementation Using Interface as Type

What Is an Interface?

As you've already learned, objects define their interaction with the outside world through the methods that they expose. Methods form the object's interface with the outside world; the buttons on the front of your television set, for example, are the interface between you and the electrical wiring on the other side of its plastic casing. You press the "power" button to turn the television on and off. In its most common form, an interface is a group of related methods with empty bodies. A bicycle's behavior, if specified as an interface, might appear as follows: Interface Bicycle {

Void change Cadence (int new Value);

39

void change Gear (int new Value);

Void speedup(int increment);

void applyBrakes (int decrement); } To implement this interface, the name of your class would change (to ACME Bicycle, for example), and you'd use the implements keyword in the class declaration: Class ACME Bicycle implements Bicycle {

// remainder of this class implemented as before

} Implementing an interface allows a class to become more formal about the behavior it promises to provide. Interfaces form a contract between the class and the outside world, and this contract is enforced at build time by the compiler. If your class claims to implement an interface, all methods defined by that interface must appear in its source code before the class will successfully compile. Programming with Interfaces in Java In Java an interface is similar to an abstract class in that its members are not implemented. In interfaces, _none_ of the methods are implemented. There is no code at all associated with an interface. For example, from my personal library: public interface Comparable { boolean less(Object m); boolean greater(Object m); boolean lessEqual (Object m); boolean greaterEqual (Object m); }

40

All instance methods are implicitly public and abstract. You can mark them as such, but are discouraged from doing so as the marking is considered obsolete practice. The interfaces themselves need not be public and several interfaces in the standard libraries are not public and thus used only internally. An interface creates a protocol that classes may implement. Note that one can extend an interface (to get a new interface) just as you can extend a class. One can actually extend several interfaces. Interfaces thus enjoy the benefits of multiple inheritances. (Classes do not.) There are almost no disadvantages to multiple inheritance of interface (small name conflict problems are one exception). There are large disadvantages to multiple inheritance of implementation as in C++. These include efficiency considerations as well as the semantic difficulty of determining just what code will be executed in some circumstances. The Polynomial class that implements Comparable will need to implement all of the functions declared in the interface. public class Polynomial implements Comparable { ... boolean less (Object m) {. . .} boolean greater(Object m){ . . . } boolean lessEqual (Object m) { . . . } boolean greater Equal (Object m) {. . . }

Polynomial multiply (Polynomial P) {. . .} ... } A class may choose to implement any number of interfaces. A class that implements an interface must provide bodies for all methods of that interface. Also, I expect that an abstract class can choose to implement part of an interface leaving the rest for non-abstract subclasses. I can't find this in the documentation, however. Anyone that needs to know can, of course, construct a simple example and try it to see if the compilers accept it. I call this

41

technique "probing" and use it often when I'm not sure about how something works. In Java, with its more complete definition than other languages, this should be an even more valuable technique, since compilers should differ very little. The usefulness of interfaces goes far beyond simply publishing protocols for other programmers. Any function can have parameters that are of interface type. Any object from a class that implements the interface may be passed as an argument. class Foo { Vector bar (Vector v, Comparable c) {...} ... } One can apply bar to a Vector and a Polynomial, since Polynomial implements Comparable. The body of instance method (member function) bar will only be able to apply Comparable methods to parameter c. Dynamic Binding assures that the actual class methods for the object passed will be applied. Other instance methods of objects passed, such as the multiply method from Polynomial cannot be used within bar. Therefore the bar function is polymorphic in its second parameter as many actual types may be passed for this value. Note that classes implementing Comparable don't need to be otherwise related to each other. This means that a class that uniformly uses an interface type for some method parameters, behaves very much like a class template in C++. We can also have variables (not just parameters) of type Comparable, or in general, of any interface type. These variables may refer to any object from any class that implements the interface. These variables may have only members defined within the interface applied to them however. They may also be passed to methods that name that interface as a parameter type. Note that interface declarations never declare variables, though they may declare constants. You can actually use variable syntax for defining them (with initializes, of course). You don't need to say "static final" though you may. They will be constants in any case.

42

If you have a variable of some interface type and you know that it refers to an object of a specific class, say Polynomial, then you can cast it to Polynomial. A run-time check will be inserted to guarantee correctness. If you need to check the type of any reference, you can use the instance of operator. Use of instance of should be rare. If you find yourself using it often, you haven't yet absorbed object-oriented programming, and the power of the dynamic binding principle. If you cast a reference incorrectly, then at runtime the ClassCastException will be thrown. Note that cast can never make an incorrect program correct. It won't fix up problems. It simply affirms what must otherwise be true. Notice that C++ templates are a form of _implicit_ interface. When you define a class template in C++ and then use the template parameter in the body of the class, the uses of that name impose requirements on the actual arguments that can be used to instantiate the template. If you apply operator< to such a parameter, then the actual argument needs to support operator<. In Java we have the advantage that such interfaces are explicit. We name the interface and the interface defines a list of requirements that the objects must implement. Note, however, that adhering to an interface requires saying that the class implements it in its class header. Adherence to interfaces is, therefore, explicit rather than implicit as with C++ templates. A method in Java that has a parameter of interface type is nearly the same as a function template in C++. A class that uses interfaces to type any variables or parameters behaves very similarly to a class template in C++. Just think of the interface names as if they were template arguments. If a "template" puts _no_ restrictions on an object, then just use type Object rather than an interface type. One of the very important interfaces in Java (in package java.util) is interface Enumeration { boolean hasMoreElements (); Object } If an object implements Enumeration, we can use the object to control a while loop. Enumeration e =...
43

next Element();

while (e.hasMoreElements ()) doSomethingWith (e.nextElement ()); Collection objects like stacks and hashtables return enumeration objects so that we can process all elements of the collection with a while loop without needing access to the internal storage mechanism of the collection. Vector, for example has an instance method public final synchronized Enumeration elements (); That returns an enumeration over the elements of the Vector. Enumerations are called iterators in other languages. To implement an enumeration properly, you must guarantee that each call of next Element will return an element of the collection not yet returned, and that hasMoreElements will be true if and only if not all elements have yet been returned by next Element. Therefore to print all elements in Vector V, you can write Enumeration e = V.elements(); while (e.hasMoreElements ()) println ("Value is + e.nextElement ()); When you create a new container type (like Vector) you also create a class that implements Enumeration so that users can get access to the elements of the container for such processes. Summary A class can extend one other class and implement any number of interfaces. An interface can extend any number of interfaces. When a class implements an interface it implements all of the methods declared in that interface. You can have variables and parameters of an interface type. You can also cast these as needed. Note that one mechanism in Java (interfaces) supports most of the functionality of two mechanisms of C++ (templates and multiple inheritances). One of the designers of C++ has stated that if templates had been added to C++ earlier then multiple inheritances probably

44

would not have been seen as needed. This is because templates and multiple inheritances can be used to solve many of the same problems. Separating interface and implementation There have been various proposals for separating an interface from an implementation. For example the language Java supports this separation An Interface defines a set of method signatures, while a Class defines the structure and behavior of its instances. A class may match several interfaces, if it implements the methods defined by each interface. Classes are insatiable, while interfaces are not. In contrast, we propose a different separation of interface and implementation, based on the properties supported by the context relation. A base class defines the structure and interface (method signatures) of its instances, and may provide implementations for the interface as well. If an implementation of a method is not provided directly in the base class, the implementation may be separately given in one or more classes that are context-related to the base class. In contrast to the notion of Interface in Java, a base class that defines only interfaces may still be instantiated. To prevent a method resolution error at run-time, we require an implementation of the method to either exist directly in the base class or to exist in some context-related class that is declared to be the default implementation. In C++, dynamic binding is required when a virtual method is invoked through a pointer. In a language such as Java all methods are assumed virtual (unless declared final) and all objects are referenced through pointers. The context relation allows method update, not just for subclasses but for the base class itself. Dynamic binding occurs regardless of whether a method is invoked through a pointer or variable. We propose the use of context tables, a dynamic version of static virtual function tables, to avoid pointer chasing through lists of context objects. The use of the tables will support fast method lookup. For simplicity, we assume all methods to be context-updatable, although it would be possible to add a keyword to limit dynamic lookup such as the final keyword in Java. A met object protocol (MOP) is an interface that allows a programmer to customize properties of the programming language, such as adding persistence and concurrency A

45

reflective programming language is one which supports such customizations, allowing the program to reason about its own execution state and alters behavior accordingly The context relation and context objects can be implemented using reflection. A Sample Interface, Relatable Consider an interface that defines how to compare the size of objects.
public interface Relatable {

// this (object calling isLargerThan) and // other must be instances of the same class // returns 1, 0, -1 if this is greater // than, equal to, or less than other public int isLargerThan(Relatable other);

If you want to be able to compare the size of similar objects, no matter what they are, the class that instantiates them should implement Relatable. Any class can implement Relatable if there is some way to compare the relative "size" of objects instantiated from the class. For strings, it could be number of characters; for books, it could be number of pages; for students, it could be weight; and so forth. For planar geometric objects, area would be a good choice (see the RectanglePlus class that follows), while volume would work for three-dimensional geometric objects. All such classes can implement the isLargerThan() method. If you know that a class implements Relatable, then you know that you can compare the size of the objects instantiated from that class. Implementing the Relatable Interface Here is the Rectangle class that was presented in the Creating Objects section, rewritten to implement Relatable.

46

public class RectanglePlus implements Relatable { public int width = 0; public int height = 0; public Point origin;

// four constructors public RectanglePlus() { origin = new Point(0, 0); } public RectanglePlus(Point p) { origin = p; } public RectanglePlus(int w, int h) { origin = new Point(0, 0); width = w; height = h; } public RectanglePlus(Point p, int w, int h) { origin = p; width = w; height = h; }

// a method for moving the rectangle public void move(int x, int y) { origin.x = x; origin.y = y; }

// a method for computing the area of the rectangle public int getArea() { return width * height; }

// a method to implement Relatable public int isLargerThan(Relatable other) { RectanglePlus otherRect = (RectanglePlus)other; if (this.getArea() < otherRect.getArea())

47

return -1; else if (this.getArea() > otherRect.getArea()) return 1; else return 0; } }

Because RectanglePlus implements Relatable, the size of any two RectanglePlus objects can be compared. Using an Interface as a Type

When you define a new interface, you are defining a new reference data type. You can use interface names anywhere you can use any other data type name. If you define a reference variable whose type is an interface, any object you assign to it must be an instance of a class that implements the interface. As an example, here is a method for finding the largest object in a pair of objects, for any objects that are instantiated from a class that implements Relatable:
public Object findLargest(Object object1, Object object2) { Relatable obj1 = (Relatable)object1; Relatable obj2 = (Relatable)object2; if ( (obj1).isLargerThan(obj2) > 0) return object1; else return object2; }

By casting object1 to a Relatable type, it can invoke the isLargerThan method. If you make a point of implementing Relatable in a wide variety of classes, the objects instantiated from any of those classes can be compared with the findLargest() method provided that both objects are of the same class. Similarly, they can all be compared with the following methods:

48

public Object findSmallest(Object object1, Object object2) { Relatable obj1 = (Relatable)object1; Relatable obj2 = (Relatable)object2; if ( (obj1).isLargerThan(obj2) < 0) return object1; else return object2; }

public boolean isEqual(Object object1, Object object2) { Relatable obj1 = (Relatable)object1; Relatable obj2 = (Relatable)object2; if ( (obj1).isLargerThan(obj2) == 0) return true; else return false; }

These methods work for any "relatable" objects, no matter what their class inheritance is. When they implement Relatable, they can be of both their own class (or superclass) type and a Relatable type. This gives them some of the advantages of multiple inheritance, where they can have behavior from both a superclass and an interface.

Rewriting Interfaces

Consider an interface that you have developed called DoIt:


public interface DoIt { void doSomething(int i, double x); int doSomethingElse(String s); }

Suppose that, at a later time, you want to add a third method to DoIt, so that the interface now becomes:
public interface DoIt {

void doSomething(int i, double x); int doSomethingElse(String s); boolean didItWork(int i, double x, String s); 49

If you make this change, all classes that implement the old DoIt interface will break because they don't implement the interface anymore. Programmers relying on this interface will protest loudly. Try to anticipate all uses for your interface and to specify it completely from the beginning. Given that this is often impossible, you may need to create more interfaces later. For example, you could create a DoItPlus interface that extends DoIt:
public interface DoItPlus extends DoIt {

boolean didItWork(int i, double x, String s);

Now users of your code can choose to continue to use the old interface or to upgrade to the new interface.

50

Chapter

THREADING
Objectives

Understanding Threads Thread_Ex Class Synchronization Locks and Grained Locks Semaphores

JAVA THREADS Threads are an integral to the Java language. If you remember, a paint () method is not invoked directly by the applet but by a thread in the interpreter. A thread is similar to a process but that multiple threads run in the same address space as the application. Multiple threads can be used to run different parts of your program simultaneously. If your computer does not have multi-processors then the threads really do not run concurrently. But the idea is that while the screen is waiting for some input some other process can be running. For instance, in Java the garbage collector thread runs in the background. Whenever the program is waiting on something, Java might invoke the garbage collector to free up space. In Java, every thread begins by executing a run () method in a particular object. Run () is declared to be public, takes no arguments, has no return value, and is not allowed to throw

51

any exceptions. Any class can implement a run () method by declaring that the class implements the Runnable interface. Java also simplifies the synchronization of threads. The structures used for this simplification is based on monitors, which is a popular scheme developed by C.A.R. Hoare. A monitor is a lock that only one thread can access at a time. If one thread holds the lock, then the other threads must wait for that thread to finish in order acquiring the lock. A SIMPLE JAVA THREADS EXAMPLE In this section of the tutorial, we are going the play with threads. The first example that we will be examining is called thread_ex.java. In this example, we will spawn three threads:

1. Counter 0 thread 2. Counter 1 thread 3. Update thread

The counter threads are threads that simply count from 1 to 50. The update thread is a thread that will handle updating the display. We will have two numbers display and two buttons displayed (one for each counter thread). If the user clicks on a button, that corresponding thread will run incrementing the count. If the user clicks on that same button again, the counting will stop and that thread will be suspended. The button is a toggle switch for the thread to run or not run. After a count of 50 for each counter has been reached, the application is done. THE THREAD_EX APPLET Counter class source thread_ex class source

THE COUNTER CLASS


1: class Counter extends Thread { 2: /* This class simply runs a thread to count to 50 */
52

3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14 :}

public int count = 1;

public void run() { while (count < 50) { try { sleep(1000); } catch (InterruptedException e) { } count++; } stop (); }

As you can see this class is very simple. If you first look at Line 1, you will notice that the Counter class inherits from the Thread class. Since it inherits from a Thread, the only method that needs to be created is the run () method. Line 6 says to keep running while the count is less than or equal to 50. Line 7 is the form of exception handling used in Java. Since the sleep () method throws an InterruptedException, this exception must be caught whenever the sleep () method is applied. To catch the exception the try {} catch () {} clause should be used. Line 8 will cause the thread to sleep for the specified number of milliseconds. Sleeping causes the currently executing thread to temporarily cease execution for the specified amount of time. The thread does not lose ownership of any monitors. Line 10 increments the count by one. Finally, line 12 calls the stop () method to stop the thread from running once it has reached 50. THE THREAD_EX CLASS THE CLASS AND INSTANCE VARIABLES

1: public class thread_ex extends java.applet.Applet implements Runnable { 2: /* APPLET */

53

3: 4: 5: 7: 8:

final static int NUMCOUNTERS = 2; final static int SPACING = 23; Counter [] counters = new Counter [NUMCOUNTERS]; Thread update Thread = null; boolean [] clicked = new boolean [NUMCOUNTERS];

Line 1 inherits from the Applet class like the previous examples, but it also implements the Runnable interface. This interface is the interface that allows the user to use threads. Line 3 declares an int called NUMCOUNTERS which is declared a class variable by the modifier static. It is also declared to be unchangeable (a constant) by the modifier final. NUMCOUNTERS is the number of instances of the Counter class. Line 4 handles the spacing between the two threads displayed. Line 6 declares an array of counters, while line 7 declares a Thread called the update Thread to be initially null. The update Thread will be the thread used to handle the applet events. init() method

1: 2: 3: 4: 5: 6: 7: 8: 10: 11: 12: 13: 14: 15:

public void init () { /* Make new counters */ for (int i = 0; i < NUMCOUNTERS; i++) { counters[i] = new Counter(); counters[i].setPriority (2); clicked[i] = false; } /* Make updateThread for drawing */ if (updateThread == null) { updateThread = new Thread (this, "Count Program"); updateThread.setPriority (NUMCOUNTERS+2); } /* Make on/off buttons for threads */ for (int i = 0; i< NUMCOUNTERS; i++) {
54

16: 17: 18: 19: } }

add (new Button (("ON/OFF " + i)));

Line 5 sets the individual counters thread priority to 2. Line 10 checks to see if the updateThread has been created already. If it has not, then a new thread is created with the call in Line 11. The first parameter is the Runnable target, and the second parameter is the name you want to associate with the thread. Line 12 sets the priority for the updateThread to NUMCOUNTERS+2 (in this case 4), which is greater than the counters thread. This greater priority means that the updateThread will be scheduled for execution before the counters threads. check_button method

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:

void check_button (String bname) {

// Start updateThread if (!updateThread.isAlive()) updateThread.start ();

for (int i = 0; i < NUMCOUNTERS; i++) { if (bname.equals("ON/OFF " + i)) { // Start that buttons thread if (!counters[i].isAlive ()) { // Never started so start it counters[i].start (); } else if (clicked[i] == false) { // Button hit so resume
55

17: 18: 19: 20: 21: 22: 23: 24: 25: 26: } } } } }

counters[i].resume ();

else if (clicked[i] == true) { // Button hit so turn off counters[i].suspend ();

clicked[i] =!clicked[i]; // Say button was hit

Line 4 uses the isAlive () method call to test if the updateThread has started. If it has not, it is started by using the start () method. The start () method will call the run () method defined for that thread along with setting up some other information. Line 13 runs the counters start () method if the counters thread is not alive. Else if the button was clicked upon resume the suspended thread as stated in lines 15-18. Lines 19-22 will suspend the running counters thread. RUN () METHOD

public void run () {

while (updateThread == Thread.currentThread()) { // Repaint until count > 50 for (int i = 0; i < NUMCOUNTERS; i++) { if (counters[i].count <= 50) { repaint(); try { updateThread.sleep(1000); } catch (InterruptedException e) {}
56

} else counters[i].stop(); } for (int i = 0; i < NUMCOUNTERS; i++) { if (counters[i] != null) if (counters[i].count > 50) stop(); } } }

The run () method is the method invoked whenever the start () is applied to a thread. All the run () method does in this example is handle the repainting of the applet. It sleeps for every 1000 milliseconds to give the counters threads a chance to run. Then when both counters have reached 50 the threads are stopped. STOP () METHOD

public void stop () { // Stops all threads for (int i = 0; i< NUMCOUNTERS; i++) { if (counters[i].isAlive ()) { Counters[i] = null; } } if (updateThread.isAlive ()) { updateThread = null; } }

This method simply will stop all the running threads. If the user decides to hit the Back button on the browser, you most likely do not want the threads to still be running. The stop ()
57

method does this procedure by setting the threads to null if they are still alive. The stop () method is invoked whenever the applet is scrolled out of view, or the page the applet is no longer being looked at. Locks Most applications require threads to communicate and synchronize their behavior to one another. The simplest way to accomplish this task in a Java program is with locks. To prevent multiple accesses, threads can acquire and release a lock before using resources. Imagine a lock on the copy machine for which only one worker can possess a key at a time. Without the key, use of the machine is impossible. Locks around shared variables allow Java threads to quickly and easily communicate and synchronize. A thread that holds a lock on an object knows that no other thread will access that object. Even if the thread with the lock is preempted, another thread cannot acquire the lock until the original thread wakes up, finishes its work, and releases the lock. Threads that attempt to acquire a lock in use go to sleep until the thread holding the lock releases it. After the lock is freed, the sleeping thread moves to the ready-to-run queue. In Java programming, each object has a lock; a thread can acquire the lock for an object by using the synchronized keyword. Methods, or synchronized blocks of code, can only be executed by one thread at a time for a given instantiation of a class, because that code requires obtaining the object's lock before execution. Continuing with our copier analogy, to avoid clashing copiers, we can simply synchronize access to the copier resource, allowing only one worker access at a time, as shown in the following code sample. We achieve this by having methods (in the Copier object) that modify the copier state be declared as synchronized methods. Workers that need to use a Copier object have to wait in line because only one thread per Copier object can be executing synchronized code.
class CopyMachine {

public synchronized void makeCopies(Document d, int nCopies) { //only one thread executes this at a time }

58

public void loadPaper() { //multiple threads could access this at once!

synchronized(this) { //only one thread accesses this at a time //feel free to use shared resources, overwrite members, etc. } }

} Fine-grain locks Often, using a lock at the object level is too coarse. Why lock up an entire object, disallowing access to any other synchronized method for only brief access to shared resources? If an object has multiple resources, it's unnecessary to lock all threads out of the whole object in order to have one thread use only a subset of the thread's resources. Because every object has a lock, we can use dummy objects as simple locks, as shown here:
class FineGrainLock {

MyMemberClass x, y; Object xlock = new Object(), ylock = new Object();

public void foo() { synchronized(xlock) { //access x here }

//do something here - but don't use shared resources

synchronized(ylock) { //access y here } }

59

public void bar() { synchronized(xlock) { synchronized(ylock) { //access both x and y here } } //do something here - but don't use shared resources } }

These methods need not be synchronized at the method level by declaring the whole method with the synchronized keyword; they are using the member locks, not the object-wide lock that a synchronized method acquires. Semaphores Frequently, several threads will need to access a smaller number of resources. For example, imagine a number of threads running in a Web server answering client requests. These threads need to connect to a database, but only a fixed number of available database connections are available. How can you assign a number of database connections to a larger number of threads efficiently? One way to control access to a pool of resources (rather than just with a simple one-thread lock) is to use what is known as a counting semaphore. A counting semaphore encapsulates managing the pool of available resources. Implemented on top of simple locks, a semaphore is a thread-safe counter initialized to the number of resources available for use. For example, we would initialize a semaphore to the number of database connections available. As each thread acquires the semaphore, the number of available connections is decremented by one. Upon consumption of the resource, the semaphore is released, incrementing the counter. Threads that attempt to acquire a semaphore when all the resources managed by the semaphore are in use simply block until a resource is free. A common use of semaphores is in solving the "consumer-producer problem." This problem occurs when one thread is completing work that another thread will use. The consuming thread can only obtain more data after the producing thread finishes generating it. To use a
60

semaphore in this manner, you create a semaphore with the initial value of zero and have the consuming thread block on the semaphore. For each unit of work completed, the producing thread signals (releases) the semaphore. Each time a consumer consumes a unit of data and needs another, it attempts to acquire the semaphore again, resulting in the value of the semaphore always being the number of units of completed work ready for consumption. This approach is more efficient than having a consuming thread wake up, check for completed work, and sleep if nothing is available. Though semaphores are not directly supported in the Java language, they are easily implemented on top of object locks. A simple implementation follows:

class Semaphore { private int count; public Semaphore(int n) { this.count = n; }

public synchronized void acquire() { while(count == 0) { try { wait(); } catch (InterruptedException e) { //keep trying } } count--; }

public synchronized void release() { count++; notify(); //alert a thread that's blocking on this semaphore }

61

Chapter

EXCEPTION HANDLING MECHANISM

Objectives

Understanding Exception Handling Pre-defined Exceptions User-defined Exceptions

Exceptions When a program violates the semantic constraints of the Java programming language, the Java virtual machine signals this error to the program as an exception. An example of such a violation is an attempt to index outside the bounds of an array. Some programming languages and their implementations react to such errors by peremptorily terminating the program; other programming languages allow an implementation to react in an arbitrary or unpredictable way. Neither of these approaches is compatible with the design goals of the Java platform: to provide portability and robustness. Instead, the Java programming language specifies that an exception will be thrown when semantic constraints are violated and will cause a non-local transfer of control from the point where the exception occurred to a point that can be specified by the programmer. An exception is said to be thrown from the point where it occurred and is said to be caught at the point to which control is transferred. Programs can also throw exceptions explicitly, using throw statements

62

Explicit use of throw statements provides an alternative to the old-fashioned style of handling error conditions by returning funny values, such as the integer value -1 where a negative value would not normally be expected. Experience shows that too often such funny values are ignored or not checked for by callers, leading to programs that are not robust, exhibit undesirable behavior, or both. Every exception is represented by an instance of the class Throw able or one of its subclasses; such an object can be used to carry information from the point at which an exception occurs to the handler that catches it. Handlers are established by catch clauses of try statements. During the process of throwing an exception, the Java virtual machine abruptly completes, one by one, any expressions, statements, method and constructor invocations, initializes, and field initialization expressions that have begun but not completed execution in the current thread. This process continues until a handler is found that indicates that it handles that particular exception by naming the class of the exception or a super class of the class of the exception. If no such handler is found, then the method uncaught Exception is invoked for the Thread Group that is the parent of the current thread-thus every effort is made to avoid letting an exception go unhandled. The exception mechanism of the Java platform is integrated with its synchronization model, so that locks are released as synchronized statements and invocations of synchronized methods complete abruptly. This chapter describes the different causes of exceptions It details how exceptions are checked at compile time and processed at run time. A detailed example is then followed by an explanation of the exception hierarchy. The Causes of Exceptions An exception is thrown for one of three reasons:

An abnormal execution condition was synchronously detected by the Java virtual machine. Such conditions arise because:

63

evaluation of an expression violates the normal semantics of the language, such as an integer divide by zero, as summarized in 15.6

o o

an error occurs in loading or linking part of the program (12.2, 12.3) some limitation on a resource is exceeded, such as using too much memory

These exceptions are not thrown at an arbitrary point in the program, but rather at a point where they are specified as a possible result of an expression evaluation or statement execution.

A throw statement was executed. An asynchronous exception occurred either because:


o o

the method stop of class Thread was invoked an internal error has occurred in the virtual machine

Exceptions are represented by instances of the class Throwable and instances of its subclasses. These classes are, collectively, the exception classes. Compile-Time Checking of Exceptions A compiler for the Java programming language checks, at compile time, that a program contains handlers for checked exceptions, by analyzing which checked exceptions can result from execution of a method or constructor. For each checked exception which is a possible result, the throws clause for the method or constructor must mention the class of that exception or one of the super classes of the class of that exception. This compile-time checking for the presence of exception handlers is designed to reduce the number of exceptions which are not properly handled. The unchecked exceptions classes are the class Runtime Exception and its subclasses, and the class Error and its subclasses. All other exception classes are checked exception classes. The Java API defines a number of exception classes, both checked and unchecked. Additional exception classes, both checked and unchecked, may be declared by programmers. for a description of the exception class hierarchy and some of the exception classes defined by the Java API and Java virtual machine.

64

The checked exception classes named in the throws clause are part of the contract between the implementor and user of the method or constructor. The throws clause of an overriding method may not specify that this method will result in throwing any checked exception which the overridden method is not permitted, by its throws clause, to throw. When interfaces are involved, more than one method declaration may be overridden by a single overriding declaration. In this case, the overriding declaration must have a throws clause that is compatible with all the overridden declarations. Static initializes, class variable initializes, and instance initializes or instance variable initializes within named classes and interfaces, must not result in a checked exception; if one does, a compile-time error occurs. No such restriction applies to instance initializes or instance variable initializes within anonymous classes. Why Errors are not checked Those unchecked exception classes which are the error classes (Error and its subclasses) are exempted from compile-time checking because they can occur at many points in the program and recovery from them is difficult or impossible. A program declaring such exceptions would be cluttered, pointlessly. Why Runtime Exceptions are not checked The runtime exception classes (Runtime Exception and its subclasses) are exempted from compile-time checking because, in the judgment of the designers of the Java programming language, having to declare such exceptions would not aid significantly in establishing the correctness of programs. Many of the operations and constructs of the Java programming language can result in runtime exceptions. The information available to a compiler, and the level of analysis the compiler performs, are usually not sufficient to establish that such run-time exceptions cannot occur, even though this may be obvious to the programmer. Requiring such exception classes to be declared would simply be an irritation to programmers. For example, certain code might implement a circular data structure that, by construction, can never involve null references; the programmer can then be certain that a Null

65

PointerException cannot occur, but it would be difficult for a compiler to prove it. The theorem-proving technology that is needed to establish such global properties of data structures is beyond the scope of this specification. Handling of an Exception When an exception is thrown, control is transferred from the code that caused the exception to the nearest dynamically-enclosing catch clause of a try statement that handles the exception. A statement or expression is dynamically enclosed by a catch clause if it appears within the try block of the try statement of which the catch clause is a part, or if the caller of the statement or expression is dynamically enclosed by the catch clause. The caller of a statement or expression depends on where it occurs:

If within a method, then the caller is the method invocation expression that was executed to cause the method to be invoked.

If within a constructor or an instance initialize or the initialize for an instance variable, then the caller is the class instance creation expression or the method invocation of new Instance that was executed to cause an object to be created.

If within a static initializer or an initializer for a static variable, then the caller is the expression that used the class or interface so as to cause it to be initialized.

Whether a particular catch clause handles an exception is determined by comparing the class of the object that was thrown to the declared type of the parameter of the catch clause. The catch clause handles the exception if the type of its parameter is the class of the exception or a super class of the class of the exception. Equivalently, a catch clause will catch any exception object that is an instance of the declared parameter type. The control transfer that occurs when an exception is thrown causes abrupt completion of expressions and statements until a catch clause is encountered that can handle the exception; execution then continues by executing the block of that catch clause. The code that caused the exception is never resumed.

66

If no catch clause handling an exception can be found, then the current thread (the thread that encountered the exception) is terminated, but only after all finally clauses have been executed and the method uncaught Exception has been invoked for the Thread Group that is the parent of the current thread. In situations where it is desirable to ensure that one block of code is always executed after another, even if that other block of code completes abruptly, a try statement with a finally clause may be used. If a try or catch block in a try-finally or try-catch-finally statement completes abruptly, then the finally clause is executed during propagation of the exception, even if no matching catch clause is ultimately found. If a finally clause is executed because of abrupt completion of a try block and the finally clause itself completes abruptly, then the reason for the abrupt completion of the try block is discarded and the new reason for abrupt completion is propagated from there. The exact rules for abrupt completion and for the catching of exceptions are specified in detail with the specification of each statement in and for expressions in Exceptions are Precise Exceptions are precise: when the transfer of control takes place, all effects of the statements executed and expressions evaluated before the point from which the exception is thrown must appear to have taken place. No expressions, statements, or parts thereof that occur after the point from which the exception is thrown may appear to have been evaluated. If optimized code has speculatively executed some of the expressions or statements which follow the point at which the exception occurs, such code must be prepared to hide this speculative execution from the user-visible state of the program. Handling Asynchronous Exceptions Most exceptions occur synchronously as a result of an action by the thread in which they occur, and at a point in the program that is specified to possibly result in such an

67

exception. An asynchronous exception is, by contrast, an exception that can potentially occur at any point in the execution of a program. Proper understanding of the semantics of asynchronous exceptions is necessary if highquality machine code is to be generated. Asynchronous exceptions are rare. They occur only as a result of:

An invocation of the stop methods of class Thread or Thread Group An internal error in the Java virtual machine

The stop methods may be invoked by one thread to affect another thread or all the threads in a specified thread group. They are asynchronous because they may occur at any point in the execution of the other thread or threads. An Internal Error is considered asynchronous. The Java platform permits a small but bounded amount of execution to occur before an asynchronous exception is thrown. This delay is permitted to allow optimized code to detect and throw these exceptions at points where it is practical to handle them while obeying the semantics of the Java programming language. A simple implementation might poll for asynchronous exceptions at the point of each control transfer instruction. Since a program has a finite size, this provides a bound on the total delay in detecting an asynchronous exception. Since no asynchronous exception will occur between control transfers, the code generator has some flexibility to reorder computation between control transfers for greater performance. Like all exceptions, asynchronous exceptions are precise An Example of Exceptions Consider the following example: class TestException extends Exception { TestException () {super () ;} TestException (String s) {super(s) ;}

68

} class Test { public static void main (String[] args) { for (int i = 0; i < args.length; i++) { try { Thrower (args[i]); System.out.println ("Test \"" + args[i] + "\" didn't throw an exception"); } catch (Exception e) { System.out.println ("Test \"" + args[i] + "\" threw a " + e.getClass () + "\n } } } static int thrower (String s) throws TestException { try { if (s.equals("divide")) { int i = 0; return i/i; } if (s.equals("null")) { s = null; return s.length(); } if (s.equals("test")) throw new TestException("Test message"); return 0; } finally { System.out.println("[thrower(\"" + s + "\") done]"); with message: + e.getMessage ());

69

} } } If we execute the test program, passing it the arguments: divide null not test it produces the output: [thrower ("divide") done] Test "divide" threw a class java.lang.ArithmeticException with message: / by zero [thrower ("null") done] Test "null" threw a class java.lang.NullPointerException with message: null [thrower ("not") done] Test "not" didn't throw an exception [thrower ("test") done] Test "test" threw a class TestException With message: Test message

This example declares an exception class Test Exception. The main method of class Test invokes the thrower method four times, causing exceptions to be thrown three of the four times. The try statement in method main catches each exception that the thrower throws. Whether the invocation of thrower completes normally or abruptly, a message is printed describing what happened. The declaration of the method thrower must have a throws clause because it can throw instances of Test Exception, which is a checked exception class. A compile-time error would occur if the throws clause were omitted. Notice that the finally clause is executed on every invocation of thrower, whether or not an exception occurs, as shown by the "[thrower (...) done]" output that occurs for each invocation.

70

The Exception Hierarchy The possible exceptions in a program are organized in a hierarchy of classes, rooted at class Throw able, a direct subclass of Object. The classes Exception and Error are direct subclasses of Throwable. The class Runtime Exception is a direct subclass of Exception.

Programs can use the pre-existing exception classes in throw statements, or define additional exception classes, as subclasses of Throwable or of any of its subclasses, as appropriate. To take advantage of the Java platform's compile-time checking for exception handlers, it is typical to define most new exception classes as checked exception classes, specifically as subclasses of Exception that are not subclasses of Runtime Exception. The class Exception is the super class of all the exceptions that ordinary programs may wish to recover from. The class Runtime Exception is a subclass of class Exception. The subclasses of Runtime Exception are unchecked exception classes. The subclasses of Exception other than Runtime Exception are all checked exception classes. The class Error and its subclasses are exceptions from which ordinary programs are not ordinarily expected to recover. See the Java API specification for a detailed description of the exception hierarchy. The class Error is a separate subclass of Throwable, distinct from Exception in the class hierarchy, to allow programs to use the idiom: } catch (Exception e) { To catch all exceptions from which recovery may be possible without catching errors from which recovery is typically not possible. Loading and Linkage Errors

The Java virtual machine throws an object that is an instance of a subclass of Linkage Error when a loading, linkage, preparation, verification or initialization error occurs.

71

Virtual Machine Errors The Java virtual machine throws an object that is an instance of a subclass of the class Virtual Machine Error when an internal error or resource limitation prevents it from implementing the semantics of the Java programming language. See The Java Virtual Machine Specification Second Edition for the definitive discussion of these errors. User Defined Exceptions class BoringLectureException extends Exception {

public BoringLectureException () { Super (); }

public BoringLectureException (String errorMessage) { super (error Message); } }

class NewExceptionTest {

public static void dullSpeaker () throws BoringLectureException { // Code can go here throw new BoringLectureException (Change Topics); }

public static void main (String args [])

try { dullSpeaker ();

72

} catch (BoringLectureException e) {

System.err.println( "Error: Time to " + e.getMessage() ); } } }

Output Error: Time to Change Topics Try/Catch Statement and Predefined Exceptions The Java Program: Pre.java 1 // Pre.java -- try/catch statement and predefined exceptions 2 public class Pre 3{ 4 5 6 7 8 9 11 12 13 14 15 double d = Double.valueOf (argv [0]).doubleValue (); /* Correctly formated, but extreme values are set to Infinite by "valueOf". Numbers that are very close to zero are set to 0.0. */ System.out.println (d); } catch (ArrayIndexOutOfBoundsException e) { System.out.println ("An argument is required."); return; public static void main (String argv[]) { try {

16 } catch (NumberFormatException e) {

17 18

System.out.println ("The argument must be a real number."); return;


73

19 20 21 } }

The Java Program: Trace.java 1 // Trace.java 2 3 Use "java -Djava.compiler=NONE Trace" to see line numbers in stack trace.

4 import java.io.FileInputStream; 5 Import java.io.FileNotFoundException; 6 import java.io.IOException; 7 public class Trace { 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 } } } public static void main (String args[]) { try { final int n = Integer.valueOf(args[0]).intValue(); FileInputStream in = new FileInputStream (args[n]); in = null; in.close (); } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace (System.err); } catch (NumberFormatException e) { e.printStackTrace (System.err); } catch (FileNotFoundException e) { e.printStackTrace (System.err); } catch (IOException e) { e.printStackTrace (System.err); } catch (NullPointerException e) { e.printStackTrace (System.err);

74

The Java Program: Reraise.java

1 // Reraise.java 2 3 Use "java -Djava.compiler=NONE Trace" to see line numbers in stack trace.

4 import java.util.NoSuchElementException; 5 import java.io.FileNotFoundException; 6 public class Reraise { 7 8 9 10 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ArrayIndexOutOfBoundsException, NumberFormatException, NoSuchElementException, or FileNotFoundException public static void main (String argv[]) throws Exception { try { switch (0) { case 0: throw new ArrayIndexOutOfBoundsException ("darn!"); case 1: throw new NumberFormatException ("merde!"); case 2: throw new NoSuchElementException ("Scheie!"); case 3: throw new FileNotFoundException ("mierda!"); } // Not reached } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace (System.err); throw e; } catch (NumberFormatException e) { e.printStackTrace (System.err); throw e; } catch (NoSuchElementException e) { e.printStackTrace (System.err); throw e; } catch (FileNotFoundException e) { e.printStackTrace (System.err);
75

30 31 32 33 } } }

throw e;

76

Chapter

JDBC - JAVA DATABASE CONNECTIVITY


Objectives

Understanding JDBC Know about Connectivity Drivers Connecting to Database Structuring Statements Storing data to Database Retrieving data from Database

What is JDBC?
Java Database Connectivity (JDBC) is a programming framework for Java developers writing programs that access information stored in databases, spreadsheets, and flat files. JDBC is commonly used to connect a user program to a "behind the scenes" database, regardless of what database management software is used to control the database. In this way, JDBC is cross-platform. This article will provide an introduction and sample code that demonstrates database access from Java programs that use the classes of the JDBC API, which is available for free download from Sun's site.

77

A database that another program links to is called a data source. Many data sources, including products produced by Microsoft and Oracle, already use a standard called Open Database Connectivity (ODBC). Many legacy C and Perl programs use ODBC to connect to data sources. ODBC consolidated much of the commonality between database management systems. JDBC builds on this feature, and increases the level of abstraction. JDBC-ODBC bridges have been created to allow Java programs to connect to ODBC-enabled database software This article assumes that readers already have a data source established and are moderately familiar with the Structured Query Language (SQL), the command language for adding records, retrieving records, and other basic database manipulations. See Hoffman's tutorial on SQL if you are a beginner or need some refreshing Using a JDBC driver Regardless of data source location, platform, or driver (Oracle, Microsoft, etc.), JDBC makes connecting to a data source less difficult by providing a collection of classes that abstract details of the database interaction. Software engineering with JDBC is also conducive to module reuse. Programs can easily be ported to a different infrastructure for which you have data stored (whatever platform you choose to use in the future) with only a driver substitution. As long as you stick with the more popular database platforms (Oracle, Informix, Microsoft, MySQL, etc.), there is almost certainly a JDBC driver written to let your programs connect and manipulate data. You can download a specific JDBC driver from the manufacturer of your database management system (DBMS) or from a third party (in the case of less popular open source products).The JDBC driver for your database will come with specific instructions to make the class files of the driver available to the Java Virtual Machine, which your program is going to run. JDBC drivers use Java's built-in Driver Manager to open and access a database from within your Java program. To begin connecting to a data source, you first need to instantiate an object of your JDBC driver. This essentially requires only one line of code, a command to the Driver Manager,
78

telling the Java Virtual Machine to load the byte code of your driver into memory, where its methods will be available to your program. The String parameter below is the fully qualified class name of the driver you are using for your platform combination: Connecting to your database To actually manipulate your database, you need to get an object of the Connection class from your driver. At the very least, your driver will need a URL for the database and parameters for access control, which usually involves standard password authentication for a database account. As you may already be aware, the Uniform Resource Locator (URL) standard is good for much more than telling your browser where to find a web page: http://www.vusports.com/index.html The URL for our example driver and database looks like this: jdbc: mysql: //db_server:3306/contacts/ Even though these two URLs look different, they are actually the same in form: the protocol for connection, machine host name and optional port number, and the relative path of the resource. Your JDBC driver will come with instructions detailing how to form the URL for your database. It will look similar to our example. You will want to control access to your data, unless security is not an issue. The standard least common denominator for authentication to a database is a pair of strings, an account and a password. The account name and password you give the driver should have meaning within your DBMS, where permissions should have been established to govern access privileges. Our example JDBC driver uses an object of the Properties class to pass information through the Driver Manager, which yields a Connection object:

79

Properties props = new Properties (); props.setProperty ("user", "contacts"); props.setProperty ("password", "blackbook"); Connection con = DriverManager.getConnection ( "jdbc: mysql: //localhost:3306/contacts/", props); Now that we have a Connection object, we can easily pass commands through it to the database, taking advantage of the abstraction layers provided by JDBC.

Structuring statements Databases are composed of tables, which in turn are composed of rows. Each database table has a set of rows that define what data types are in each record. Records are also stored as rows of the database table with one row per record. We use the data source connection created in the last section to execute a command to the database. We write commands to be executed by the DBMS on a database using SQL. The syntax of a SQL statement, or query, usually consists of an action keyword, a target table name, and some parameters. For example: INSERT INTO songs VALUES ( "Jesus Jones", "Right Here, Right Now"); INSERT INTO songs VALUES ( "Def Leppard", "Hysteria"); These SQL queries each added a row of data to table "songs" in the database. Naturally, the order of the values being inserted into the table must match the order of the corresponding columns of the table, and the data types of the new values must match the data types of the corresponding columns. For more information about the supported data types in your DBMS, consult your reference material.

80

To execute an SQL statement using a Connection object, you first need to create a Statement object, which will execute the query contained in a String. Statement stmt = con.createStatement (); String query = ... // define query stmt.executeQuery (query); Example: Parsing a text file into a database table In the course of modernizing a record keeping system, you encounter a flat file of data that was created long before the rise of the modern relational database. Rather than type all the data from the flat file into the DBMS, you may want to create a program that reads in the text file, inserting each row into a database table, which has been created to model the original flat file structure. In this case, we examine a very simple text file. There are only a few rows and columns, but the principle here can be applied and scaled to larger problems. There are only a few steps:

Open a connection to the database. Loop until the end of the file:
o o o

Read a line of text from the flat file. Parse the line of text into the columns of the table. Execute a SQL statement to insert the record.

Here is the code of the example program: import java.io.*; import java.sql.*; import java.util.*;

public class TextToDatabaseTable { private static final String DB = "contacts", TABLE_NAME = "records", HOST = "jdbc:mysql://db_lhost:3306/",
81

ACCOUNT = "account", PASSWORD = "nevermind", DRIVER = "org.gjt.mm.mysql.Driver", FILENAME = "records.txt";

public static void main (String [] args) { try {

// connect to db Properties props = new Properties (); props.setProperty ("user", ACCOUNT); props.setProperty ("password", PASSWORD);

Class.forName (DRIVER).newInstance (); Connection con = DriverManager.getConnection ( HOST + DB, props); Statement stmt = con.createStatement ();

// open text file Buffered Reader in = new Buffered Reader ( new FileReader(FILENAME));

// read and parse a line String line = in.readLine (); while (line != null) {

StringTokenizer tk = new StringTokenizer (line); String first = tk.nextToken (), last = tk.nextToken (), email = tk.nextToken (), phone = tk.nextToken ();

82

// execute SQL insert statement String query = "INSERT INTO " + TABLE_NAME; query += " VALUES (" + quote (first) + ", "; query += quote (last) + ", "; query += quote (email) + ", "; query += quote (phone) + ");"; stmt.executeQuery (query);

// prepare to process next line line = in.readLine (); } in. close (); }

catch (Exception e) { e.printStackTrace (); } }

// protect data with quotes private static String quote (String include) { return ("\"" + include + "\""); } } Processing a result set Perhaps even more often than inserting data, you will want to retrieve existing information from your database and use it in your Java program. The usual way to implement this is with another type of SQL query, which selects a set of rows and columns from your

83

database and appears very much like a table. The rows and columns of your result set will be a subset of the tables you queried, where certain fields match your parameters. For example: SELECT title FROM songs WHERE artist="Def Leppard"; This query returns: title Hysteria The boxed portion above is a sample result set from a particular database program. In a Java program, this SQL statement can be executed in the same way as in the insert example, but additionally, we must capture the results in a Result Set object. Statement stmt = concrete Statement (); String query = "SELECT FROM junk; // define query Result Set answers = stmt.executeQuery (query); The JDBC version of a query result set has a cursor that initially points to the row just before the first row. To advance the cursor, use the next () method. If you know the names of the columns from your result set, you can refer to them by name. You can also refer to the columns by number, starting with 1. Usually you will want to get access all of the rows of your result set, using a loop as in the following code segment: while (answers.next ()) { String name = answers.getString("name"); int number = answers.getInt ("number"); // do something interesting } All database tables have Meta data that describe the names and data types of each column; result sets are the same way. You can use the Result Set Meta Data class to get the column count and the names of the columns, like so: Result Set Meta Data meta = answers.getMetaData ();
84

String [] colNames = new String [meta.getColumnCount ()]; for (int col = 0; col < colNames.length; col++) colNames [col] = meta.getColumnName (col + 1); Example: Printing a database table We choose to write a simple software tool to show the rows and columns of a database table. In this case, we are going to query a database table for all its records, and display the result set to the command line. We could also have created a graphical front end made of Java Swing components. Notice that we do not know anything except the URL and authentication information to the database table we are going to display. Everything else is determined from the ResultSet and its meta data. Comments in the code explain the actions of the program. Here is the code of the example program: import java.sql.*; import java.util.*;

public class DatabaseTableViewer { private static final String DB = "contacts", TABLE_NAME = "records", HOST = "jdbc:mysql://db_host:3306/", ACCOUNT = "account", PASSWORD = "nevermind", DRIVER = "org.gjt.mm.mysql.Driver";

public static void main (String [] args) { try {

// authentication properties
85

Properties props = new Properties (); props.setProperty ("user", ACCOUNT); props.setProperty ("password", PASSWORD);

// load driver and prepare to access Class.forName (DRIVER).newInstance (); Connection con = DriverManager.getConnection ( HOST + DB, props); Statement stmt = con.createStatement ();

// execute select query String query = "SELECT * FROM " + TABLE_NAME + ";"; ResultSet table = stmt.executeQuery (query);

// determine properties of table ResultSetMetaData meta = table.getMetaData (); String [] colNames = new String [meta.getColumnCount ()]; Vector [] cells = new Vector [colNames.length]; for (int col = 0; col < colNames.length; col++) { colNames [col] = meta.getColumnName (col + 1); cells [col] = new Vector (); }

// hold data from result set while (table.next ()) { for (int col = 0; col < colNames.length; col++) { Object cell = table.getObject (colNames [col]); cells [col].add (cell); } }

86

// print column headings for (int col = 0; col < colNames.length; col++) System.out.print (colNames [col].toUpperCase () + "\t"); System.out.println ();

// print data row-wise while (!cells [0].isEmpty ()) { for (int col = 0; col < colNames.length; col++) System.out.print (cells [col].remove (0).toString () + "\t"); System.out.println (); } }

// exit more gently catch (Exception e) { e.printStackTrace (); } } }

87

Você também pode gostar