Basic Java Material
Basic Java Material
Contents:
1. Introduction to Java and OOC
2. The Language Package
3. The Utilities Package
4. I/O Package
5. Multithreading
6. GUI and Applet Programming
7. Networking
8. JDBC
1
.
Table of Contents
6. Multithreading ……………………………………………………………..115
2
.
3
.
CHAPTER-1: INTRODUCTION TO JAVA
Java, a Web programming technology, had altered the course of software
development, especially in regard to the Internet. Java sports all features necessary
for extending the Web in ways previously impossible. Java was designed with issues
such as portability and reliability in mind. Because of Java, the entire concept of the
Web and what it can do is being redefined.
Java Buzzwords:
SIMPLICITY
OBJECT-ORIENTED NATURE
The notion of object in Java is implemented by its class construct. In fact, it is not
possible to write a Java program that does something meaningful without using the
class construct. Java language comes with very powerful set of pre-defined classes
with a hierarchy level.
DISTRIBUTED NATURE
ARCHITECTURALLY NEUTRAL
The Java Compiler does not produce the machine language instructions that make
up the executable Java Program. The Java Compiler DOES NOT generate a .exe
file. Instead the compiler produces an intermediate code called as 'byte code'. Java
byte code is an architecturally neutral representation of the program, that is, it is
independent of any processor type or machine architecture. These byte codes are
read by the Java interpreter and the same is executed using an internal model of an
abstract machine. The Java Interpreter and the implementation of this abstract
machine are called the JAVA VIRTUAL MACHINE.
SECURE LANGUAGE
Before any Java program is interpreted, the Java runtime system performs a byte-
code verification to ensure that the program is not violating the system integrity. Also,
programs loaded from the net are loaded in a separate name space than the local
classes. This prevents any other program to affect the system classes.
4
.
MULTITHREADED LANGUAGE
HelloWorld Application
Save this into a file called HelloWorld.java using any text editor. It is very important to
call the file HelloWorld.java, because the compiler expects the file name to match the
class identifier.
Type
at a command prompt.
The javac program creates a file called HelloWorld.class from the HelloWorld.java
file. Inside this file (HelloWorld.class) is text known as bytecodes which can be run by
the Java interpreter.
The input to the interpreter is nothing but the name of the class that has the main
method.
Hello World!!
Understanding HelloWorld
Declaring a class
The first task when creating any Java program is to create a class. Look at the first
line of the HelloWorld application:
5
.
Here, ClassName is the name of the program you are writing. In addition,
ClassName must correspond to the file name. Next, notice the little curly brace ({)
that is located after the class declaration. If you look at the end of the class, there is
also a closing brace (}). The braces tell the compiler where your class will begin and
end. Any code between those two braces is considered to be in the HelloWorld class.
This line declares what is known as the main method. Methods are essentially mini-
programs. Each method performs some of the tasks of a complete program. The
main method is the most important one with respect to applications, because it is the
place that all Java applications start. For instance, when you run java HelloWorld, the
Java interpreter starts at the first line of the main method.
System.out.println("Hello World!!");
You can replace any of the text within the quotation marks ("") with any text that you
would like.
The System.out line is run because, when the application starts up, the interpreter
looks at the first line of code (namely the printout) and executes it. If you place any
other code there, it runs that code instead.
There is one minor variation on println which is also readily used: print("Hello
World!!"). The difference between println and print is that print does not add a
carriage return at the end of the line, so any subsequent printouts are on the same
line.
Access Specifiers :
The first option for a method is the access specifier. Access specifiers are used to
restrict access to the method. Regardless of what the access specifier is, though, the
method is accessible from any other method in the same class.
public
The public modifier is the most relaxed modifier possible for a method. By specifying
a method as public it becomes accessible to all classes regardless of their lineage or
their package. In other words, a public method is not restricted in any way.
6
.
protected
The second possible access modifier is protected. Protected methods can be
accessed by any class within the current package, but are inaccessible to any class
outside the package.
default
The next access modifier that can be applied to a class is that of default. Default
methods are accessible only to the current class and any classes that extend from it.
If you fail to specify an access modifier, the method is considered default.
private
private is the highest degree of protection that can be applied to a method. A private
method is only accessible by those methods in the same class. Even classes that
extend from the current class do not have access to a private class.
Method Modifiers
Method modifiers enable you to set properties for the method, such as where it will
be visible and how subclasses of the current class will interact with it.
static
Placing the static modifier in front of a method or variable declaration makes it
common to all object references of that class. While non-static methods can also
operate with static variables, static methods can only deal with static variables and
static methods.
abstract
Abstract methods are simply methods that are declared, but are not implemented in
the current class. The responsibility of defining the body of the method is left to
subclasses of the current class.
final
By placing the keyword final in front of the method declaration, you prevent any
subclasses of the current class from overriding the given method. This ability
enhances the degree of insulation of your classes, you can ensure that the
functionality defined in this method will never be altered in any way.
Note:
Neither static methods nor class constructors can be declared to be abstract.
Furthermore, you should not make abstract methods final, because doing so
prevents you from overriding the method.
native
Native methods are methods that you want to use, but do not want to write in Java.
Native methods are most commonly written in C++, and can provide several benefits
such as faster execution time. Like abstract methods, they are declared simply by
placing the modifier native in front of the method declaration and by substituting a
semicolon for the method body.
synchronized
By placing the keyword synchronized in front of a method declaration, you can
prevent data corruption that may result when two methods attempt to access the
same piece of data at the same time. While this may not be a concern for simple
programs, once you begin to use threads in your programs, this may become a
serious problem.
7
.
Modified HelloWorld
In the above HelloWorld program, the print method was called inside the same class.
The following example creates a separate PrintWorld object that has a print method
and any other class can invoke this method to print the necessary result.
class PrintWorld
{
String data_member;
public PrintWorld(String line)
{
data_member = new String(line);
}
public void printMe()
{
System.out.println(data_member);
}
}
is used to construct the class PrintWorld. Quite simply, the line tells the compiler to
allocate memory for an instance of the class and points variable to the new section of
memory. In the process of doing this, the compiler also calls the class's constructor
method and passes the appropriate parameters to it
p_world is the object to the class PrintWorld. This class has a data member,
data_member and a method printMe().
In the construction phase of the class, the argument of the constructor is assigned to
the data member. And later when the printMe() method is called, this data member
value is retrieved and printed.
ReadHello.java
8
.
{
public static void main (String args[]
{
int inChar =’0’;
System.out.println("Enter a Character:");
try
{
inChar = System.in.read();
System.out.println("You entered " + inChar);
}
catch (IOException e)
{
System.out.println("Error reading from user");
}
}
}
You've probably already noticed that there is a lot more to this code than there was to
the last one. Let’s first compile the program.
Enter a Character:
A
You entered 65
inChar = System.in.read();
System.in.read() is a method that takes a look at the character that the user enters. It
then performs what is known as a return on the value. A value that is returned by a
method is then able to be used in an expression. In the case of ReadHello, a variable
called inChar is set to the value which is returned by the System.in.read() method.
In the next line, the value of the inChar variable is added to the System.out string. By
adding the variable into the string, you can see the results of your work. It's not
actually necessary to use a variable. If you prefer, you can print it out directly in the
second System.out line, by changing it to
Now, notice that the program displays a number instead of a character for what you
entered. This is because the read() method of System.in returns an integer, not an
actual character. The number corresponds to what is known as the ASCII character
set.
To convert the number that is returned from System.in into a character, you need to
do what is known as a cast. Casting effectively converts a given data type to another
one.
---
inChar =(char) System.in.read();
---
9
.
Notice the characters before System.in.read().The (char) causes the integer to be
changed into a character.
When a method states that it will throw an exception, it is your responsibility to only
try to perform that method, and if it throws the exception, you need to catch it. See
the line of code right after the catch phase. If there is an error while reading, an
exception called an IOException is thrown. When that happens, the code in the catch
block is called.
KEYWORDS
Inheritance is a feature of OOP programming that enables us inherit all the common
features of a parent class onto a child class, it's not necessary to reinvent the object
every time. When new classes inherit the properties of another class, they are
referred to as child classes or subclasses. The class from which they are derived is
then called a parent or super class.
class BaseClass
{
public BaseClass()
{
System.out.println("Base Class Constructor Called");
}
10
.
}
/*
DerivedClass extends or inherits the property
of the BaseClass
*/
class DerivedClass extends BaseClass
{
public DerivedClass()
{
System.out.println("Derived Class Constructed");
}
}
By looking at the output, you can find that, when the child class is constructed, the
parent class constructor is invoked first.
INTERFACES
Interfaces are Java's substitute for C++'s feature of multiple inheritance, the practice
of allowing a class to have several super classes. While it is often desirable to have a
class inherit several sets of properties, for several reasons the creators of Java
decided not to allow multiple inheritance. Java classes, however, can implement
several interfaces, thereby enabling you to create classes that build upon other
objects without the problems created by multiple inheritance.
The syntax for creating an interface is extremely similar to that for creating a class.
However, there are a few exceptions. The most significant difference is that none of
the methods in your interface may have a body.
An Interface Example
11
.
{
public int getPrice(int id)
{
if (id == 1)
return(5);
else
return(10);
}
The Declaration
Public Interfaces
By default, interfaces may be implemented by all classes in the same package. But if
you make your interface public, you allow classes and objects outside of the given
package to implement it as well.
The rules for an interface name are identical to those for classes.
In keeping with the OOP practice of inheritance, Java interfaces may also extend
other interfaces as a means of building larger interfaces upon previously developed
code.
Interfaces cannot extend classes. There are a number of reasons for this, but
probably the easiest to understand is that any class, which the interface would be
extending would have its method bodies defined. This violates the "prime directive" of
interfaces.
The main purposes of interfaces are to declare abstract methods that will be defined
in other classes. As a result, if you are dealing with a class that implements an
interface, you can be assured that these methods will be defined in the class. While
12
.
this process is not overly complicated, there is one important difference that should
be noticed. An interface method consists of only a declaration.
Methods in Interface
Note that unlike normal method declarations in classes, declarations in interfaces are
immediately followed by a semicolon.
All methods in interfaces are public by default, regardless of the presence or absence
of the public modifier. This is in contrast to class methods which default to friendly.
It's actually illegal to use any of the other standard method modifiers (including
native, static, synchronized, final, private, protected, or private protected) when
declaring a method in an interface.
Variables in Interfaces
While all fields will be created as public, final, and static, you do not need to explicitly
state this in the field declaration. All fields default to public, static and final regardless
of the presence of these modifiers.
It is, however, a good practice to explicitly define all fields in interfaces as public,
final, and static to remind yourself (and other programmers) of this fact.
Implementing an interface.
In order to fulfill the requirements of implementing the Product interface, the class
must override the getPrice(int) method.
Overriding Methods
ENCAPSULATION
Another benefit of enclosing data and methods in classes is the OOP characteristic
of encapsulation—the ability to isolate and insulate information effectively from the
rest of your program.
POLYMORPHISM
Finally, the allure of the OOP approach to creating self-sustaining modules is further
enhanced by the fact that children of a given class are still considered to be of the
same "type" as the parent. This feature, called polymorphism, enables you to perform
the same operation on different types of classes as long as they share a common
13
.
trait. While the behavior of each class might be different, you know that the class will
be able to perform the same operation as its parent because it is of the same family
tree
class Sample {
public Sample() {
System.out.println("Sample Constructor Called"); }
public void overloadMe() {
System.out.println("Overload Method Invoked"); }
public void overloadMe(String str) {
System.out.println(str);
}
}
Output:
Here, though the method overloadMe is the same, it throws different ouput based on
its invocation. This is termed as method overloading.
Primitive types
Reference types
Java has eight primitive types, each with its own purpose and use:
Type Description
• boolean - These have values of either true or false.
14
.
• short - 16-bit 2s-compliment integer with values between -2^15 and 2^15-1
(-32,768 to 32,767)
• char 16-bit Unicode characters. For alpha-numerics, these are the same as
ASCII with the high byte set to 0. The numerical values are unsigned 16-bit
values are between 0 and 65535.
• int 32-bit 2s-compliment integer with values between -231 and 231-1
(-2,147,483,648 to 2,147,483,647)
• long 64-bit 2s-compliment integer with values between -263 and 263-1
(-9223372036854775808 to 9223372036854775807)
• float 32-bit single precision floating point numbers using the IEEE 754-1985
standard (+/- about 1039)
• double 64-bit double precision floating point numbers using the IEEE 754-1985
standard. (+/- about 10317)
VARIABLES
You can create any variable in Java in the same way as was just shown:
• As with every other line of code in Java, terminate the line with a semicolon
example:
int number = 0;
boolean value = false;
There are several rules that must be obeyed when creating an identifier:
The first character of an identifier must be a letter. After that, all subsequent
characters can be letters or numerals. The underscore (_) and the dollar sign ($) may
be used as any character in an identifier, including the first one. Identifiers are case-
sensitive and language-sensitive.
HelloWorld
counter
HotJava$
ioc_Queue3
15
.
Examples of Illegal Identifiers
9HelloWorld
count&add
Hot Java
65536
OPERATORS
Operators are used to change the value of a particular object. They are described
here in several related categories.
Description Operator
Increment ++
Decrement --
Negation -
Bitwise complement ~
class Negation {
public static void main (String args[]) {
int x = 8;
System.out.println(“x = “ + x);
int y = -x;
System.out.println(“y = “ + y);
}
16
.
class BitwiseComplement {
public static void main (String args[]) {
int x = 8;
System.out.println(“x = “ + x);
int y = ~x;
System.out.println(“y = “ + y);
}
}
ARITHMETIC OPERATORS
Arithmetic operators act on pairs of integers.
Description Operator
Addition +
Subtraction -
Multiplication *
Division /
Modulus %
Bitwise AND &
Bitwise OR |
Bitwise XOR ^
Left Shift <<
Right Shift >>
Zero-Fill Right Shift >>>
Shift Operators
The left-shift, right-shift, and zero-fill-right-shift operators (<<, >>, and >>>) shift the
individual bits of an integer by a specified integer amount. The following are some
examples of how these operators are used:
x << 3;
y >> 7;
z >>> 2;
class Shift {
public static void main (String args[]) {
int x = 7;
System.out.println(“x = “ + x);
System.out.println(“x >> 2 = “ + (x >> 2));
System.out.println(“x << 1 = “ + (x << 1));
System.out.println(“x >>> 1 = “ + (x >>> 1));
}
}
17
.
Relational Operators
The last group of integer operators is the relational operators, which all operate on
integers but return a type boolean.
Description Operator
Less Than <
Greater Than >
Less Than Or Equal To <=
Greater Than Or Equal To >=
Equal To ==
Not Equal To !=
ASSIGNMENT OPERATORS
The simplest assignment operator is the standard assignment operator. This operator
is often known as the gets operator, because the value on the left gets the value on
the right.
= assignment operator
The arithmetic assignment operators provide a shortcut for assigning a value. When
the previous value of a variable is a factor in determining the value that you want to
assign, the arithmetic assignment operators are often more efficient:
Description Operator
Simple =
Addition +=
Subtraction -=
Multiplication *=
Division /=
Modulus %=
AND &=
OR |=
XOR ^=
BOOLEAN OPERATORS
Boolean operators act on Boolean types and return a Boolean result. The Boolean
operators are listed
Description Operator
Evaluation AND &
Evaluation OR |
Evaluation XOR ^
Logical AND &&
Logical OR D="I228" NAME="I228"> ||
Negation !
Equal To ==
Not Equal To !=
Conditional ?:
CONDITIONAL OPERATOR
18
.
In this syntax, expression1 must produce a Boolean value. If this value is true, then
expression2 is evaluated, and its result is the value of the conditional. If expression1
is false, then expression3 is evaluated, and its result is the value of the conditional.
x=7
CONTROL FLOW
Control flow is the heart of any program. Control flow is the ability to adjust (control)
the way that a program progresses (flows). By adjusting the direction that a computer
takes, the programs that you build become dynamic. Without control flow, programs
would not be able to do anything more than several sequential operations.
IF STATEMENTS
if (expression)
if_statement;
else
else_statement;
ITERATION STATEMENTS
while
do
for
continue
break
WHILE STATEMENTS
while (expression)
19
.
statement;
DO WHILE LOOP
do
statement;
while (expression)
FOR LOOP
SWITCH STATEMENTS
switch (expression){
BREAK STATEMENTS
The sub-statement blocks of loops and switch statements can be broken out of by
using the break statement.
RETURN STATEMENTS
A return statement passes control to the caller of the method, constructor, or static
initializer containing the return statement. If the return statement is in a method that
is not declared void, it may have a parameter of the same type as the method.
ARRAYS
An array is simply a way to have several items in a row. If you have data that can be
easily indexed, arrays are the perfect means to represent them.
There are two ways to do this: place a pair of brackets after the variable type, or
place brackets after the identifier name.
int MyIntArray[];
int[] MyIntArray;
20
.
Examples of declaring arrays.
There are several additional points about arrays you need to know:
Indexing of arrays starts with 0. In other words, the first element of an array is
MyArray[0], not MyArray[1].
COMMENTS
Traditional
C++ style
javadoc
Traditional Comments
A traditional comment is a C-style comment that begins with a slash-star (/*) and
ends with a star-slash (*/).
The second style of comment begins with a slash-slash (//) and ends when the
current source code line ends. These comments are especially useful for describing
the intended meaning of the current line of code.
javadoc Comments
The final style of comment in Java is a special case of the first. It has the properties
mentioned previously, but the contents of the comment may be used in automatically
generated documentation by the javadoc tool. javadoc comments are opened with
/**, and they are closed with */. By using these comments in an appropriate manner,
you will be able to use JavaDoc to automatically create documentation pages
LITERALS
There are several different types of literal. In fact, there are five major types of literal,
in the Java language:
Boolean
Character
Floating-point
Integer
String
Integer Literal
Example
int j=0;
21
.
long GrainOfSandOnTheBeachNum=1L;
short Mask1=0x007f;
String FirstName = "Ernest";
char TibetanNine = '\u1049'
boolean UniverseWillExpandForever = true;
ESCAPE CHARACTERS
ERROR-HANDLING CLASSES
22
.
In the above example, the array size is 5, but we are trying to access the 6th element.
As this is a runtime error, an exception is caught and the catch block is executed.
Suppose there is some action that you absolutely must do, no matter what happens.
Usually, this is to free some external resource after acquiring it, to close a file after
opening it, or something similar. In exception handling, the finally block is executed
no matter whether an exception is thrown or not.
Output:
Requested Index Not found
--------End---------
The Throwable class is the superclass of all errors and exceptions in the Java
language. Only objects that are instances of this class (or of one of its subclasses)
are thrown by the Java Virtual Machine or can be thrown by the Java throw
statement. Similarly, only this class or one of its subclasses can be the argument
type in a catch clause. A Throwable class contains a snapshot of the execution stack
of its thread at the time it was created. It can also contain a message string that gives
more information about the error.
The class Exception and its subclasses are a form of Throwable that indicates
conditions that a reasonable application might want to catch.
try {
do {
input = (char)System.in.read();
data = data + input;
}while(input!='\n');
}
catch(Exception err) {
System.out.println("Unable to obtain system input");
}
23
.
try {
index = Integer.parseInt(data.trim());
The exception classes for the last two exceptions are NumberFormatException and
ArrayIndexOutOfBoundsException respectively. So the try block encapsulating the
parsing of the input and searching of the index has two catch blocks to handle these
exceptions in their own different way.
Invalid Index
--------End---------
If input is an integer, but index is out of range in the array, then output is
24
.
Packages:
Java provides a mechanism for partitioning the classname into more manageable
chunks. This mechanism is the package. The package is both a naming and a
visibility comttrol mechanism. Classes can be defined inside a package that are not
accessible by code outside the package. Class members can also be defined that
are only exposed to other members of the same package. This is achieved with the
help of package and access protection.
Access Protection:
Java provides many levels of protection to allow fine-grained control over the
visibility of the variables and methods within classes, subclasses and packages.
Packages add another dimension to access control.
Classes and packages are both means of encapsulating and containing the
namespace and scope of variables and methods. Packages act as containers for
classes and other subordinate packages. Classes act as containers for data and
code. The class is Java’s smallest unit of abstraction.
The three access specifiers, private, public and protected provide a variety of ways
to produce tha many levels of access required by these categories.
public - Anything declared public can be accessed from anywhere
private – Anything declared private cannot be seen outsideof its class.
protected – If an element has to be seen outside the current package but only to
classes that subclass your class directly.
Defining Package:
package mypackage;
25
.
E.g. package com.first
package com.first.one;
public BaseClass()
{
System.out.println("Inside Constructor of Base Class"); }
public void display()
{
System.out.println("Value of x(default) is "+x);
System.out.println("Value of x(private) is
"+x_pri);
System.out.println("Value of x(protected) is
"+x_pro);
System.out.println("Value of x(public) is "+x_pub);
}
}
package com.first.one;
26
.
package com.first.one;
System.out.println("\n****************TestBaseClass***********
****\n");
TestBaseClass test=new TestBaseClass();
}
}
package com.first.two;
import com.first.one.BaseClass;
27
.
public static void main(String arg[])
{
BaseClassNew bcn=new BaseClassNew();
}
}
package com.first.two;
import com.first.one.*;
28
.
The Language
Package - java.lang
29
.
CHAPTER-2: THE LANGUAGE PACKAGE – java.lang
The Java language package, which is also known as java.lang, provides classes that
make up the core of the Java language. The language package contains classes at
the lowest level of the Java class libraries. For example, the Object class, which all
classes are derived from, is located in the language package.
It’s impossible to write a Java program without dealing with at least a few of the
elements of the language package. The most important classes contained in the
language package follow:
The Object class is the super class for all classes in Java. Because all classes are
derived from Object, the methods defined in Object are shared by all classes. These
results in a core set of methods that all Java classes are guaranteed to support.
Object includes methods for making copies of an object, testing objects for equality,
and converting the value of an object to a string.
• Boolean
• Character
• Double
• Float
• Integer
• Long
• Number
Type wrappers are also useful because many of Java’s utility classes require classes
as parameters, not simple types. Type wrappers and simple types are not
interchangeable.
30
.
try {
do {
temp = (char)System.in.read();
input = input + temp;
}while(temp != '\n');
data = new Double(input);
}
catch(Exception err)
{
System.out.println("Exception ...");
System.exit(0);
}
31
.
System.out.println("rint : " + (Math.rint(d_data)));
System.out.println("round : " + (Math.round(d_data)));
System.out.println("Random Number : " + (Math.random()));
}
}
STRING CLASSES
For various reasons (mostly security related), Java implements text strings as
classes, rather than forcing the programmer to use character arrays. The two Java
classes that represent strings are String and StringBuffer. The String class is useful
for working with constant strings that can’t change in value or length. The
StringBuffer class is used to work with strings of varying value and length.
The String class represents character strings. All string literal in Java programs, such
as "abc", are implemented as instances of this class. Strings are constant; their
values cannot be changed after they are created. String buffers support mutable
strings. Because String objects are immutable they can be shared. For example:
is equivalent to:
System.out.println("abc");
String cde = "cde";
System.out.println("abc" + cde);
String c = "abc".substring(2,3);
The class String includes methods for examining individual characters of the
sequence, for comparing strings, for searching strings, for extracting substrings, and
for creating a copy of a string with all characters translated to uppercase or to
lowercase. The Java language provides special support for the string concatenation
operator ( + ), and for conversion of other objects to strings. String concatenation is
implemented through the StringBuffer class and its append method. String
conversions are implemented through the method toString(), defined by Object and
inherited by all classes in Java.
32
.
System.out.println("Extracting character...");
for(int index=0;index<length;index++)
{
char temp = str.charAt(index);
System.out.println(temp);
}
A string buffer implements a mutable sequence of characters. String buffers are safe
for use by multiple threads. The methods are synchronized where necessary so that
all the operations on any particular instance behave as if they occur in some serial
order.
String buffers are used by the compiler to implement the binary string concatenation
operator +. For example, the code:
x = "a" + 4 + "c"
x = new StringBuffer().append("a").append(4).append("c").toString()
The principal operations on a StringBuffer are the append and insert methods, which
are overloaded so as to accept data of any type. Each effectively converts a given
datum to a string and then appends or inserts the characters of that string to the
string buffer. The append method always adds these characters at the end of the
buffer; the insert method adds the characters at a specified point.
For example, if z refers to a string buffer object whose current contents are "start",
then the method call z.append("le") would cause the string buffer to contain "startle",
whereas z.insert(4, "le") would alter the string buffer to contain "starlet".
Every string buffer has a capacity. As long as the length of the character sequence
contained in the string buffer does not exceed the capacity, it is not necessary to
allocate a new internal buffer array. If the internal buffer overflows, it is automatically
made larger.
33
.
sb1.append(true);
System.out.println("Value of StringBuffer = " + sb1);
//Appending a character
sb1.append('c');
System.out.println("Value of StringBuffer = " + sb1);
34
.
The Runtime class provides direct access to the runtime environment. An example of
a run-time routine is the freeMemory method, which returns the amount of free
system memory available.
THREAD CLASSES
Java is a multithreaded environment and provides various classes for managing and
working with threads. Following are the classes and interfaces used in conjunction
with multithreaded programs:
• Thread
• ThreadDeath
• ThreadGroup
• Runnable
35
.
The Thread class is used to create a thread of execution in a program. The
ThreadDeath class is used to clean up after a thread has finished execution. As its
name implies, the ThreadGroup class is useful for organizing a group of threads.
Finally, the Runnable interface provides an alternate means of creating a thread
without subclassing the Thread class.
CLASS CLASSES
Java provides two classes for working with classes: Class and ClassLoader. The
Class class provides runtime information for a class, such as the name, type, and
parent superclass. Class is useful for querying a class for runtime information, such
as the class name. The ClassLoader class provides a means to load classes into the
runtime environment. ClassLoader is useful for loading classes from a file or for
loading distributed classes across a network connection.
36
.
37
.
CHAPTER- 3: THE UTILITIES PACKAGE – java.util
The Java utilities, package, which is also known as java.util, provides various
classes that perform different utility functions. The utilities package includes a
class for working with dates, a set of data structure classes, a class for
generating random numbers, and a string tokenizer class, among others. The
most important classes contained in the utilities package follow:
Calendar is an abstract base class for converting between a Date object and a set of
integer fields such as YEAR, MONTH, DAY, HOUR, and so on. (A Date object
represents a specific instant in time with millisecond precision. See java.util.Date for
information about the Date class.)
38
.
Subclasses of Calendar interpret a Date according to the rules of a specific calendar
system. The JDK provides one concrete subclass of Calendar: GregorianCalendar.
Future subclasses could represent the various types of lunar calendars in use in
many parts of the world.
Like other locale-sensitive classes, Calendar provides a class method, getInstance,
for getting a generally useful object of this type. Calendar's getInstance method
returns a GregorianCalendar object whose time fields have been initialized with the
current date and time:
import java.util.Calendar;
calendar.add(Calendar.DATE,50);
date = calendar.get(Calendar.DATE);
month = calendar.get(Calendar.MONTH);
year = calendar.get(Calendar.YEAR);
39
.
providing a stream of pseudo-random numbers. A slot machine program is a
good example of one that would make use of the Random class.
import java.util.*;
public class RandomExample {
public static void main(String args[]) {
Random random = new Random();
System.out.println("Random number(int):" +
(random.nextInt()));
}
}
The string tokenizer class allows an application to break a string into tokens. The
tokenization method is much simpler than the one used by the StreamTokenizer
class. The StringTokenizer methods do not distinguish among identifiers, numbers,
and quoted strings, nor do they recognize and skip comments. The set of delimiters
40
.
(the characters that separate tokens) may be specified either at creation time or on a
per-token basis. An instance of StringTokenizer behaves in one of two ways,
depending on whether it was created with the returnTokens flag having the value true
or false:
The following is one example of the use of the tokenizer. The code:
this
is
a
test
import java.util.StringTokenizer;
try
{
do
{
temp = (char)System.in.read();
input = input + temp;
}while(temp != '\n');
}
catch(Exception err){}
input = input.trim();
System.out.println("Printing tokens...");
StringTokenizer tokenizer = new StringTokenizer(input);
System.out.println("Number of tokens :
"+(tokenizer.countTokens()));
41
.
while(tokenizer.hasMoreTokens())
{
System.out.println(tokenizer.nextToken());
}
}
}
This class implements a hashtable, which maps keys to values. Any non-null object
can be used as a key or as a value. To successfully store and retrieve objects from a
hashtable, the objects used as keys must implement the hashCode method and the
equals method. An instance of Hashtable has two parameters that affect its
efficiency: its capacity and its load factor. The load factor should be between 0.0 and
1.0. When the number of entries in the hashtable exceeds the product of the load
factor and the current capacity, the capacity is increased by calling the rehash
method. Larger load factors use memory more efficiently, at the expense of larger
expected time per lookup. If many entries are to be made into a Hashtable, creating it
with a sufficiently large capacity may allow the entries to be inserted more efficiently
than letting it perform automatic rehashing as needed to grow the table. This
example creates a hashtable of numbers. It uses the names of the numbers as keys:
Integer n = (Integer)numbers.get("two");
if (n != null) {
System.out.println("two = " + n);
}
import java.util.*;
for(int index=0;index<days.length;index++)
{
Integer pos = new Integer(index);
hash.put(pos,days[index]);
}
42
.
import java.util.*;
while(!stack.empty())
{
obj = stack.pop();
System.out.println(obj.toString());
}
}
}
The Vector class implements a growable array of objects. Like an array, it contains
components that can be accessed using an integer index. However, the size of a
Vector can grow or shrink as needed to accommodate adding and removing items
after the Vector has been created.
43
.
increases in chunks the size of capacityIncrement. An application can increase the
capacity of a vector before inserting a large number of components; this reduces the
amount of incremental reallocation.
import java.util.*;
while(enum.hasMoreElements())
{
System.out.println(enum.nextElement().toString());
}
store.trimToSize();
}
}
44
.
The Collections Framework
The collections framework is a unified architecture for representing and manipulating
collections, allowing them to be manipulated independently of the details of their
representation. It reduces programming effort while increasing performance. It allows
for interoperability among unrelated APIs, reduces effort in designing and learning
new APIs, and fosters software reuse. The framework is based on six collection
interfaces. It includes implementations of these interfaces, and algorithms to
manipulate them.
Introduction
The 1.2 release of the Java platform includes a new collections framework. A
collection is an object that represents a group of objects. A collections framework is a
unified architecture for representing and manipulating collections, allowing them to be
manipulated independently of the details of their representation.
The primary advantages of a collections framework are that it:
• Reduces the effort required to learn APIs by eliminating the need to learn
multiple ad hoc collection APIs.
45
.
• Abstract Implementations - Partial implementations of the collection
interfaces to facilitate custom implementations.
• Array Utilities - Utility functions for arrays of primitives and reference objects.
Not, strictly speaking, a part of the Collections Framework, this functionality is
being added to the Java platform at the same time and relies on some of the
same infrastructure.
Collection Interfaces
There are six collection interfaces. The most basic interface is Collection. Three
interfaces extend Collection: Set, List, and SortedSet. The other two collection
interfaces, Map and SortedMap, do not extend Collection, as they represent
mappings rather than true collections. However, these interfaces contain collection-
view operations, which allow them to be manipulated as collections.
Collection
The Collection interface is the root of the collection hierarchy. A
Collection represents a group of objects, known as its elements. Some
Collection implementations allow duplicate elements and others do
not. Some are ordered and others unordered. The JDK doesn't
provide any direct implementations of this interface: It provides
implementations of more specific subinterfaces like Set and List. This
interface is the least common denominator that all collections
implement. Collection is used to pass collections around and
manipulate them when maximum generality is desired.
46
.
Set
A Set is a collection that cannot contain duplicate elements. As you
might expect, this interface models the mathematical set abstraction. It
is used to represent sets like the cards comprising a poker hand, the
courses making up a student's schedule, or the processes running on
a machine.
List
A List is an ordered collection (sometimes called a sequence). Lists
can contain duplicate elements. The user of a List generally has
precise control over where in the List each element is inserted. The
user can access elements by their integer index (position). If you've
used Vector, you're already familiar with the general flavor of List.
Map
A Map is an object that maps keys to values. Maps cannot contain
duplicate keys: Each key can map to at most one value. If you've used
Hashtable, you're already familiar with the general flavor of Map.
The last two core collection interfaces (SortedSet and SortedMap) are merely sorted
versions of Set and Map
Object Ordering
There are two ways to order objects: The Comparable interface
provides automatic natural order on classes that implement it, while
the Comparator interface gives the programmer complete control over
object ordering. Note that these are not core collection interfaces, but
underlying infrastructure.
All of the modification methods in the collection interfaces are labeled optional. Some
implementations may not perform one or more of these operations, throwing a
runtime exception (UnsupportedOperationException) if they are attempted.
Implementations must specify in their documentation which optional operations they
support. Several terms are introduced to aid in this specification:
47
.
• Lists that guarantee that their size remains constant even though the
elements may change are referred to as fixed-size. Lists that are not fixed-
size are referred to as variable-size.
Some implementations may restrict what elements (or in the case of Maps, keys and
values) may be stored. Possible restrictions include requiring elements to:
• Be of a particular type.
• Be non-null.
Collection Implementations
Class that implement the collection interfaces typically have names of the form
<Implementation-style><Interface>. The general-purpose implementations are
summarized in the table below:
Implementations
Hash Table Resizable Array Balanced Tree Linked List
Set HashSet TreeSet
Interfaces List ArrayList LinkedList
Map HashMap TreeMap
The general-purpose implementations support all of the optional operations in the
collection interfaces, and have no restrictions on the elements they may contain.
Design Goals
The main design goal was to produce an API that was reasonably small, both in size,
and, more importantly, in "conceptual weight." It was critical that the new functionality
not seem alien to current Java programmers; it had to augment current facilities,
rather than replacing them. At the same time, the new API had to be powerful enough
to provide all the advantages described above.
To keep the number of core interfaces small, the interfaces do not attempt to capture
such subtle distinctions as mutability, modifiability, resizability. Instead, certain calls in
the core interfaces are optional, allowing implementations to throw an
UnsupportedOperationException to indicate that they do not support a specified
48
.
optional operation. Of course, collection implementers must clearly document which
optional operations are supported by an implementation.
To keep the number of methods in each core interface small, an interface contains a
method only if either:
It was critical that all reasonable representations of collections interoperate well. This
included arrays, which cannot be made to implement the Collection interface directly
without changing the language. Thus, the framework includes methods to allow
collections to be dumped into arrays, arrays to be viewed as collections, and maps to
be viewed as collections.
49
.
Input/Output package –
java.io
50
.
CHAPTER – 4 :THE I/O PACKAGE - java.io
The Java I/O package, also known as java.io, provides classes with support for
reading and writing data to and from different input and output devices, including
files. The I/O package includes classes for inputting streams of data, outputting
streams of data, working with files, and tokenizing streams of data. The most
important classes contained in the I/O package follows:
FILE CLASSES
Files are the most widely used method of data storage in computer systems. Java
supports files with two different classes: File and RandomAccessFile. The File class
provides an abstraction for files that takes into account system-dependent features.
The File class keeps up with information about a file including the location where it is
stored and how it can be accessed. The File class has no methods for reading and
writing data to and from a file; it is only useful for querying and modifying the
attributes of a file. In actuality, you can think of the File class data as representing a
filename, and the class methods as representing operating system commands that
act on filenames.
The RandomAccessFile class provides a variety of methods for reading and writing
data to and from a file. RandomAccessFile contains many different methods for
reading and writing different types of information, namely the data type wrappers.
Instances of this class represent the name of a file or directory on the host file
system. A file is specified by a pathname, which can either be an absolute pathname
or a pathname relative to the current working directory. The pathname must follow
the naming conventions of the host platform.
The File class is intended to provide an abstraction that deals with most of the
machine dependent complexities of files and pathnames in a machine-independent
fashion.
Note that whenever a filename or path is used it is assumed that the host's file
naming conventions are used.
import java.io.*;
import java.util.Date;
51
.
if(args.length == 0)
{
System.out.println("Usage : java FileExample
<filename>");
System.exit(0);
}
String filename = args[0];
try
{
File file = new File(filename);
System.out.println("File Exists : "+file.exists());
Instances of this class support both reading and writing to a random access file. An
application can modify the position in the file at which the next read or write occurs.
This class provides a sense of security by offering methods that allow specified mode
accesses of read-only or read-write to files.
import java.io.*;
public class RandomAccessExample {
public static void main(String args[]) {
if(args.length != 2) {
System.out.println("Usage : ");
System.out.println("java RandomAccessExample <sourcefile>
<destfile>");
System.exit(0);
}
String sourcefile = args[0];
String destinfile = args[1];
String data = "";
try
{
File srcfile = new File(sourcefile);
File destfile = new File(destinfile);
RandomAccessFile srcrdm = new RandomAccessFile(srcfile,"r");
RandomAccessFile dstrdm =new RandomAccessFile(destfile,"rw");
52
.
Java uses input streams to handle reading data from an input source. An input
source can be a file, a string, memory, or anything else that contains data. The input
stream classes follow:
• InputStream
• BufferedInputStream
• ByteArrayInputStream
• DataInputStream
• FileInputStream
• FilterInputStream
• LineNumberInputStream
• PipedInputStream
• PushbackInputStream
• SequenceInputStream
• StringBufferInputStream
The InputStream class is an abstract class that serves as the base class for all input
streams. The InputStream class defines an interface for reading streamed bytes of
data, finding out the number of bytes available for reading, and moving the stream
position pointer, among other things. All the other input streams provide support for
reading data from different types of input devices.
• OutputStream
• BufferedOutputStream
• ByteArrayOutputStream
53
.
• DataOutputStream
• FileOutputStream
• FilterOutputStream
• PipedOutputStream
• PrintStream
The OutputStream class is an abstract class that serves as the base class for all
output streams. OutputStream defines an interface for writing streamed bytes of data
to an output source. All the other output streams provide support for writing data to
different output devices. Data written by an output stream is formatted to be read by
an input stream.
The class implements a buffered input stream. By setting up such an input stream,
an application can read bytes from a stream without necessarily causing a call to the
underlying system for each byte read. The data is read by blocks into a buffer;
subsequent reads can access the data directly from the buffer.
import java.io.*;
class BufferedExample {
public static void main (String args[]) {
BufferedInputStream in = new
BufferedInputStream(System.in);
byte buf[] = new byte[10];
try {
in.read(buf, 0, 10);
}
catch (Exception e) {
System.out.println(“Error: “ + e.toString()); }
String s = new String(buf, 0); System.out.println(s);
} }
The class implements a buffered output stream. By setting up such an output stream,
an application can write bytes to the underlying output stream without necessarily
causing a call to the underlying system for each byte written. The data is written into
a buffer, and then written to the underlying stream if the buffer reaches its capacity,
the buffer output stream is closed, or the buffer output stream is explicity flushed.
import java.io.*;
class WriteStuff
{
public static void main (String args[])
{
// Copy the string into a byte array
String s = new String(“Dance, spider!\n”);
54
.
byte[] buf = new byte[64];
s.getBytes(0, s.length(), buf, 0);
A data input stream lets an application read primitive Java data types from an
underlying input stream in a machine-independent way. An application uses a data
output stream to write data that can later be read by a data input stream.
Data input streams and data output streams represent Unicode strings in a format
that is a slight modification of UTF-8.
All characters in the range '\u0001' to '\u007F' are represented by a single byte:
0bits 0-7
The null character '\u0000' and characters in the range '\u0080' to '\u07FF' are
represented by a pair of bytes:
The two differences between this format and the "standard" UTF-8 format are the
following:
• The null byte '\u0000' is encoded in 2-byte format rather than 1-byte, so that the
encoded strings never have embedded nulls.
• Only the 1-byte, 2-byte, and 3-byte formats are used.
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;
55
.
A file input stream is an input stream for reading data from a File or from a
FileDescriptor.
import java.io.*;
class ReadFile
{
public static void main (String args[])
{
byte buf[] = new byte[64];
try
{
FileInputStream in = new FileInputStream(“Grocery.txt”);
in.read(buf, 0, 64);
}
catch (Exception e)
{
System.out.println(“Error: “ + e.toString());
}
String s = new String(buf, 0);
System.out.println(s);
}}
56
.
THE FILEOUTPUTSTREAM CLASS
This class allows an application to create an input stream in which the bytes read are
supplied by the contents of a byte array. Applications can also read bytes from a
string by using a StringBufferInputStream.
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
57
.
System.out.println("size: "+outStream.size());
ByteArrayInputStream inStream;
inStream = new
ByteArrayInputStream(outStream.toByteArray());
int inBytes = inStream.available();
System.out.println("inStream has "+inBytes+" available
bytes");
byte inBuf[] = new byte[inBytes];
int bytesRead = inStream.read(inBuf,0,inBytes);
System.out.println(bytesRead+" bytes were read");
System.out.println("They are: "+new String(inBuf));
}
}
This class implements an output stream in which the data is written into a byte array.
The buffer automatically grows as data is written to it. The data can be retrieved
using toByteArray() and toString().
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.IOException;
58
.
THE LINENUMBERREADER CLASS
import java.io.LineNumberReader;
import java.io.FileReader;
import java.io.BufferedWriter;
import java.io.IOException;
This class is an input stream filter that provides a buffer into which data can be
"unread." An application may unread data at any time by pushing it back into the
buffer, as long as the buffer has sufficient room. Subsequent reads will read all of the
pushed-back data in the buffer before reading from the underlying input stream.
This functionality is useful when a fragment of code should read an indefinite number
of data bytes that are delimited by particular byte values. After reading the
terminating byte the code fragment can push it back, so that the next read operation
on the input stream will re-read that byte.
import java.io.PushbackInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
for(int i=0;i<s.length();++i)
outStream.write(s.charAt(i));
59
.
System.out.println("outstream: "+outStream);
System.out.println("size: "+outStream.size());
ByteArrayInputStream inByteArray;
inByteArray = new
ByteArrayInputStream(outStream.toByteArray());
PushbackInputStream inStream;
inStream = new PushbackInputStream(inByteArray);
The sequence input stream class allows an application to combine several input
streams serially and make them appear as if they were a single input stream. Each
input stream is read from, in turn, until it reaches the end of the stream. The
sequence input stream class then closes that stream and automatically switches to
the next input stream.
import java.io.FileInputStream;
import java.io.SequenceInputStream;
import java.io.IOException;
while (!eof)
{
60
.
int c = inStream.read();
if(c == -1) eof = true;
else
{
System.out.print((char) c);
++byteCount;
}
}
System.out.println(byteCount+" bytes were read");
inStream.close();
f1.close();
f2.close();
}
}
The StreamTokenizer class takes an input stream and parses it into "tokens",
allowing the tokens to be read one at a time. The parsing process is controlled by a
table and a number of flags that can be set to various states. The stream tokenizer
can recognize identifiers, numbers, quoted strings, and various comment styles.
Each byte read from the input stream is regarded as a character in the range '\u0000'
through '\u00FF'. The character value is used to look up five possible attributes of the
character: white space, alphabetic, numeric, string quote, and comment character.
Each character can have zero or more of these attributes.
A typical application first constructs an instance of this class, sets up the syntax
tables, and then repeatedly loops calling the nextToken method in each iteration of
the loop until it returns the value TT_EOF.
import java.io.StreamTokenizer;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
61
.
StreamTokenizer inStream = new StreamTokenizer(inData);
inStream.commentChar('#');
boolean eof = false;
do
{
int token=inStream.nextToken();
switch(token)
{
case inStream.TT_EOF:
System.out.println("EOF encountered.");
eof = true;
break;
case inStream.TT_EOL:
System.out.println("EOL encountered.");
break;
case inStream.TT_WORD:
System.out.println("Word: "+inStream.sval);
break;
case inStream.TT_NUMBER:
System.out.println("Number: "+inStream.nval);
break;
default:
System.out.println((char) token+" encountered.");
if(token=='!') eof=true;
}
} while(!eof);
}
}
62
.
THE PIPEDINPUTSTREAM CLASS
A piped input stream is the receiving end of a communications pipe. Two threads can
communicate by having one thread send data through a piped output stream and
having the other thread read the data through a piped input stream.
import java.io.*;
piped_out.write(store,0,store.length);
piped_in.read(p_store,0,p_store.length);
System.out.println("----------End----------");
}
catch(Exception err)
{
System.out.println("Exception in accessing file");
}
}
}
63
.
THE PRINTSTREAM CLASS
Print values and objects to an output stream, using the platform's default character
encoding to convert characters into bytes.
If automatic flushing is enabled at creation time, then the stream will be flushed each
time a line is terminated or a newline character is written.
Methods in this class never throw I/O exceptions. Client code may inquire as to
whether any errors have occurred by invoking the checkError method.
Note: This class is provided primarily for use in debugging, and for compatibility with
existing code; new code should use the PrintWriter class.
When traversing a graph, an object may be encountered that does not support the
Serializable interface. In this case the NotSerializableException will be thrown and
will identify the class of the non-serializable object.
Classes that require special handling during the serialization and deserialization
process must implement special methods with these exact signatures:
The writeObject method is responsible for writing the state of the object for its
particular class so that the corresponding readObject method can restore it. The
default mechanism for saving the Object's fields can be invoked by calling
out.defaultWriteObject. The method does not need to concern itself with the state
belonging to its superclasses or subclasses. State is saved by writing the individual
fields to the ObjectOutputStream using the writeObject method or by using the
methods for primitive data types supported by DataOutput.
64
.
The readObject method is responsible for reading from the stream and restoring the
classes fields. It may call in.defaultReadObject to invoke the default mechanism for
restoring the object's non-static and non-transient fields. The defaultReadObject
method uses information in the stream to assign the fields of the object saved in the
stream with the correspondingly named fields in the current object. This handles the
case when the class has evolved to add new fields. The method does not need to
concern itself with the state belonging to its superclasses or subclasses. State is
saved by writing the individual fields to the ObjectOutputStream using the writeObject
method or by using the methods for primitive data types supported by DataOutput.
Externalization allows a class to specify the methods to be used to write the object's
contents to a stream and to read them back. The Externalizable interface's
writeExternal and readExternal methods are implemented by a class to give the class
complete control over the format and contents of the stream for an object and its
supertypes. These methods must explicitly coordinate with the supertype to save its
state.
Object Serialization uses the Serializable and Externalizable interfaces. Object
persistence mechanisms may use them also. Each object to be stored is tested for
the Externalizable interface. If the object supports it, the writeExternal method is
called. If the object does not support Externalizable and does implement Serializable
the object should be saved using ObjectOutputStream.
When an Externalizable object is to be reconstructed, an instance is created using
thepublic no-arg constructor and the readExternal method called. Serializable objects
are restored by reading them from an ObjectInputStream.
ObjectInputStream ensures that the types of all objects in the graph created from the
stream match the classes present in the Java Virtual Machine. Classes are loaded as
required using the standard mechanisms.
Primitive data types can be read from the stream using the appropriate method on
DataInput.
The default deserialization mechanism for objects restores the contents of each field
to the value and type it had when it was written. Fields declared as transient or static
are ignored by the deserialization process. References to other objects cause those
65
.
objects to be read from the stream as necessary. Graphs of objects are restored
correctly using a reference sharing mechanism. New objects are always allocated
when deserializing, which prevents existing objects from being overwritten.
Implementing the Serializable interface allows object serialization to save and restore
the entire state of the object and it allows classes to evolve between the time the
stream is written and the time it is read. It automatically traverses references
between objects, saving and restoring entire graphs. Serializable classes that require
special handling during the serialization and deserialization process should
implement both of these methods:
The readObject method is responsible for reading and restoring the state of the
object for its particular class using data written to the stream by the corresponding
writeObject method. The method does not need to concern itself with the state
belonging to its superclasses or subclasses. State is restored by reading data from
the ObjectInputStream for the individual fields and making assignments to the
appropriate fields of the object. Reading primitive data types is supported by
DataInput.
Serialization does not read or assign values to the fields of any object that does not
implement the java.io.Serializable interface. Subclasses of Objects that are not
serializable can be serializable. In this case the non-serializable class must have a
no-arg constructor to allow its fields to be initialized. In this case it is the responsibility
of the subclass to save and restore the state of the non-serializable class. It is
frequently the case that the fields of that class are accessible (public, package, or
protected) or that there are get and set methods that can be used to restore the
state.
Any exception that occurs while deserializing an object will be caught by the
ObjectInputStream and abort the reading process.
66
.
Implementing the Externalizable interface allows the object to assume complete
control over the contents and format of the object's serialized form. The methods of
the Externalizable interface, writeExternal and readExternal, are called to save and
restore the objects state. When implemented by a class they can write and read their
own state using all of the methods of ObjectOutput and ObjectInput. It is the
responsibility of the objects to handle any versioning that occurs.
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Date;
outStream.writeObject(t1);
outStream.writeObject(t2);
outStream.writeObject(t3);
outStream.writeObject(t4);
outStream.close();
outFile.close();
System.out.println(inStream.readObject());
System.out.println(inStream.readObject());
System.out.println(inStream.readObject());
System.out.println(inStream.readObject());
inStream.close();
inFile.close();
file.delete();
}
}
67
.
class TestClass1 implements Serializable
{
boolean b;
int i;
char c;
double d;
String s;
TestClass1(boolean b,int i,char c,double d,String s)
{
this.b = b;
this.i = i;
this.c = c;
this.d = d;
this.s = s;
}
68
.
Only objects that support the java.io.Serializable interface can be written to streams.
The class of each serializable object is encoded including the class name and
signature of the class, the values of the object's fields and arrays, and the closure of
any other objects referenced from the initial objects.
The method writeObject is used to write an object to the stream. Any object, including
Strings and arrays, is written with writeObject. Multiple objects or primitives can be
written to the stream. The objects must be read back from the corresponding
ObjectInputstream with the same types and in the same order as they were written.
Primitive data types can also be written to the stream using the appropriate methods
from DataOutput. Strings can also be written using the writeUTF method.
The default serialization mechanism for an object writes the class of the object, the
class signature, and the values of all non-transient and non-static fields. References
to other objects (except in transient or static fields) cause those objects to be written
also. Multiple references to a single object are encoded using a reference sharing
mechanism so that graph of objects can be restored to the same shape as when the
original was written.
Classes that require special handling during the serialization and deserialization
process must implement special methods with these exact signatures:
The writeObject method is responsible for writing the state of the object for its
particular class so that the corresponding readObject method can restore it. The
method does not need to concern itself with the state belonging to the object's
superclasses or subclasses. State is saved by writing the individual fields to the
ObjectOutputStream using the writeObject method or by using the methods for
primitive data types supported by DataOutput.
Serialization does not write out the fields of any object that does not implement the
java.io.Serializable interface. Subclasses of Objects that are not serializable can be
serializable. In this case the non-serializable class must have a no-arg constructor to
allow its fields to be initialized. In this case it is the responsibility of the subclass to
save and restore the state of the non-serializable class. It is frequently the case that
69
.
the fields of that class are accessible (public, package, or protected) or that there are
get and set methods that can be used to restore the state.
BufferedReader in
= new BufferedReader(new InputStreamReader(System.in));
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
70
.
THE OUTPUTSTREAMWRITER CLASS
71
.
72
.
CHAPTER – 5: APPLET PROGRAMMING
An applet is a small program that is intended not to be run on its own, but rather to be
embedded inside another application. The Applet class must be the superclass of
any applet that is to be embedded in a Web page or viewed by the Java Applet
Viewer. The Applet class provides a standard interface between applets and their
environment.
HelloWorld Applet
One of the simplest applets is the HelloWorld applet, the source code for which is
shown below. Right away you should see that the applet HelloWorld is quite different
from the HelloWorld application.
HelloApplet.java
import java.applet.Applet;
import java.awt.Graphics;
public class HelloApplet extends Applet
{
public void paint (Graphics g)
{
g.drawString ("Hello World!",0,50);
}
}
When you created the HelloWorld application , you ran them using the Java
interpreter. Applets, however, don't run from the command line; they are executed
within a browser. To get the applet into the browser, you need to embed what are
known as HTML tags into an HTML file. The HTML file can then be read into a
browser.
The simplest HTML file for the HelloApplet class is shown.
<HTML>
<BODY>
<APPLET CODE="HelloApplet.class" WIDTH ="200" HEIGHT="200">
</APPLET>
</BODY>
</HTML>
With Java files, it is necessary that the file name be the same as the class file. This is
not necessary with the HTML file. In fact, a single HTML file can contain several
<APPLET> tags.
73
.
Using AppletViewer
Now, to run the applet, the JDK includes a very simplified version of a browser called
Appletviewer. Appletviewer looks for <APPLET> tags in any given HTML file and
opens a new window for each of them.
To run the HelloApplet program using Appletviewer, on the command line type:
appletviewer HelloApplet.html
The first thing to notice are the top two lines of the code:
import java.applet.Applet;
import java.awt.Graphics;
The import statement is a new one. Often it is necessary or easier to use the
contents of a class file, which have already been created, rather than try to reproduce
that work yourself. The import statement enables you to use these other classes. If
you are familiar with the C/C++ #include declaration, the import statement works in
somewhat the same way.
In the case of the HelloApplet program, there are two classes that are used other
than HelloApplet. The first is the java.applet.Applet class. The Applet class contains
all the information that is specific to applets. In fact, in order for any class to be run in
a browser as an applet, it must extend java.applet.Applet.
The second class that is imported into HelloApplet is the java.awt.Graphics class.
java.awt.Graphics contains all kinds of tools for drawing things to the screen. In fact,
the screen is treated as a Graphics object.
You may have noticed that there is a slight difference between this class declaration
for the HelloApplet class. HelloApplet extends Applet. extends is the keyword for
saying that a class should be entered into that class hierarchy. In fact, a class that
extends another class is placed at the bottom of the existing chain.
You may think this is harping the issue, but it's important: all applets must extend
java.applet.Applet. However, because you imported the Applet class, you can simply
call it Applet. If you had not imported java.applet.Applet, you could still have extended
it using the full name:
74
.
Applet Methods—paint
The next item to notice about the HelloApplet class versus HelloWorld is that
HelloApplet doesn't have a main method. Instead, this applet only has a paint
method. How is this possible?
The answer lies in the fact that the applets don't start up themselves. They are being
added to an already running program (the browser). The browser has a predefined
means for getting each applet to do what it wants. It does this by calling methods that
it knows the Applet has. One of these is paint.
The paint method is called any time the browser needs to display the applet on the
screen, so you can use the paint method to display anything. The browser helps out
by passing a Graphics object to the paint method. This object gives the paint method
a way to display items directly to the screen.
The next line shows an example of using the Graphics object to draw text to the
screen:
The paint method is not the only method that the browser calls of the applet. You can
override any of these other methods just like you did for the paint method in the
HelloWorld example.
When the applet is loaded, the browser calls the init() method. This method is only
called once no matter how many times you return to the same Web page.
After the init() method, the browser first calls the paint() method. This means that if
you need to initialize some data before you get into the paint() method, you should
do so in the init() method.
Next, the start() method is called. The start() method is called every time an applet
page is accessed. This means that if you leave a Web page and then click the Back
button, the start() method is called again. However, the init() method is not.
When you leave a Web page (say, by clicking a link), the stop() method is called.
Finally, when the browser exits all together the destroy() method is called.
Notice that unlike the paint(Graphics g) method, the init(), start(), stop(), and
destroy() methods do not take any parameters between the parentheses.
75
.
This interface corresponds to an applet's environment: the document containing the
applet and the other applets in the same document. The methods in this interface
can be used by an applet to obtain information about its environment.
When an applet is first created, an applet stub is attached to it using the applet's
setStub method. This stub serves as the interface between the applet and the
browser environment or applet viewer environment in which the application is
running.
The AudioClip interface is a simple abstraction for playing a sound clip. Multiple
AudioClip items can be playing at the same time, and the resulting sound is mixed
together to produce a composite.
76
.
THE ABSTRACT WINDOW TOOLKIT
Object
Compone Container
nt
Panel
Button
Window
TextCompone TextField
ntt Frame Dialog
Choice TextArea
List
MenuCompone
nt
MenuItem Menu
MenuBar
77
.
A Simple AWT Applet
import java.awt.*;
import java.applet.Applet;
It is not important at this point to understand exactly what every line means. Instead,
try to get a general feel for what is going on. The example is doing the following:
1. A Button component is created with the label, Click Me!
2. The Button is added to the container (in this case an applet).
For a program with a user interface that produces output, there is surprisingly little
code here. Almost all the real work of handling the user interface is hidden behind the
scenes. If you are using basic components, it’s relatively easy to keep things simple.
However, if you want to extend the functionality of the basic components, the
complexity of your code increases.
When a component is created, it usually is added to a container. A container is simply
an area of the screen in which components (and even other containers) can be
placed. This can go on endlessly: A component is added to a container, which is
added to another container, and so on. We will, in fact, be doing just this in the
calculator example at the end of the chapter.
This flexibility is one of the biggest advantages of programming the AWT. In an
object-oriented programming environment, it makes sense to think of the user
interface as actual objects and concentrate on relationships between objects. This is
exactly what the AWT lets you do.
Components
Components are the building blocks from which all programs using the AWT are built.
There are many other classes to handle the components and the interactions
between them, but if it’s on the screen, it’s a component.
This enables us to say a number of things about all components:
• All components have a screen position and a size
• All components have a foreground and background color
• Components are either enabled or disabled
• There is a standard interface for components to handle events
AWT components can be conceptually broken down into three major categories:
Interface components
78
.
Containers
Containers encompass areas in which components can be placed. This allows
groups of components to be grouped together to form a more cohesive object to be
manipulated. A Panel is an example of this type of component.
Windows
Windows are a very special case of the Component class. All other components are
added onto a container that already exists, whereas a Window is an actual, separate
window with a completely new area to create an interface upon. Normally with applet
programming, windows are not used. Dialogs and Frames are examples of this type
of component.
The java.awt.Graphics classThe Graphics class is the abstract base class for all
graphics contexts that allow an application to draw onto components that are realized
on various devices, as well as onto off-screen images.
A Graphics object encapsulates state information needed for the basic rendering
operations that Java supports. This state information includes the following
properties:
Coordinates are infinitely thin and lie between the pixels of the output device.
Operations, which draw the outline of a figure, operate by traversing an infinitely thin
path between pixels with a pixel-sized pen that hangs down and to the right of the
anchor point on the path. Operations, which fill a figure operate by filling the interior
of that infinitely thin path. Operations, which render horizontal text render the
ascending portion of character glyphs entirely above the baseline coordinate.
The graphics pen hangs down and to the right from the path it traverses. This has the
following implications:
• If you draw a figure that covers a given rectangle, that figure occupies one extra
row of pixels on the right and bottom edges as compared to filling a figure that is
bounded by that same rectangle.
• If you draw a horizontal line along the same y coordinate as the baseline of a line
of text, that line is drawn entirely below the text, except for any descends.
All coordinates, which appear as arguments to the methods of this Graphics object
are considered relative to the translation origin of this Graphics object prior to the
invocation of the method. All rendering operations modify only pixels, which lie within
the area bounded by both the current clip of the graphics context and the extents of
the component used to create the Graphics object. All drawing or writing is done in
the current color, using the current paint mode, and in the current font.
79
.
import java.awt.Graphics;
import java.applet.Applet;
g.drawRect(60,60,10,10);
g.drawOval(90,60,10,10);
g.drawRoundRect(120,60,10,10,6,6);
g.fillRect(60,90,10,10);
g.fillOval(90,90,10,10);
g.fillRoundRect(120,90,10,10,6,6);
}
}
This class encapsulates colors using the RGB format. In RGB format, the red, blue,
and green components of a color are each represented by an integer in the range 0-
255. The value 0 indicates no contribution from this primary color. The value 255
indicates the maximum intensity of this color component.
Although the Color class is based on the three-component RGB model, the class
provides a set of convenience methods for converting between RGB and HSB colors.
import java.awt.*;
import java.applet.Applet;
g.drawRect(60,60,10,10);
g.setColor(Color.red);
g.drawOval(90,60,10,10);
80
.
g.drawRoundRect(120,60,10,10,6,6);
g.setColor(c_green);
g.fillRect(60,90,10,10);
g.fillOval(90,90,10,10);
g.fillRoundRect(120,90,10,10,6,6);
}
}
import java.awt.*;
import java.applet.Applet;
Note to subclassers: Since many of these methods form closed mutually recursive
loops, you must take care that you implement at least one of the methods in each
such loop in order to prevent infinite recursion when your subclass is used. In
particular, the following is the minimal suggested set of methods to override in order
to ensure correctness and prevent infinite recursion (though other subsets are
equally feasible):
• getAscent()
• getDescent()
• getLeading()
• getMaxAdvance()
81
.
• charWidth(char ch)
• charsWidth(char data[], int off, int len)
When an application asks AWT to place a character at the position (x, y), the
character is placed so that its reference point is put at that position. The reference
point specifies a horizontal line called the baseline of the character. In normal
printing, the baselines of characters should align.
If the current character is placed with its reference point at the position (x, y), and the
character's advance width is w, then the following character is placed with its
reference point at the position (x + w, y). The advance width is often the same as the
width of character's bounding box, but need not be so. In particular, oblique and italic
fonts often have characters whose top-right corner extends slightly beyond the
advance width.
The java.awt.LabelClass
A Label object is a component for placing text in a container. A label displays a single
line of readonly text. The text can be changed by the application, but a user cannot
edit it directly.
This class creates a labeled button. The application can cause some action to
happen when the button is pushed.
The gesture of clicking on a button with the mouse is associated with one instance of
ActionEvent, which is sent out when the mouse is both pressed and released over
the button. If an application is interested in knowing when the button has been
pressed but not released, as a separate gesture, it can specialize
processMouseEvent, or it can register itself as a listener for mouse events by calling
addMouseListener. Both of these methods are defined by Component, the abstract
superclass of all components.
82
.
If an application wants to perform some action based on a button being pressed and
released, it should implement ActionListener and register the new listener to receive
events from this button, by calling the button's addActionListener method. The
application can make use of the button's action command as a messaging protocol.
In addition, the class defines methods that are used to maintain a current
selection from the text. The text selection, a substring of the component's text,
is the target of editing operations. It is also referred to as the selected text.
A check box is a graphical component that can be in either an "on" (true) or "off"
(false) state. Clicking on a check box changes its state from "on" to "off," or from "off"
to "on."
The button labeled one is in the "on" state, and the other two are in the "off" state. In
this example, the states of the three check boxes are set independently.
Alternatively, several check boxes can be grouped together under the control of a
single object, using the CheckboxGroup class. In a check box group, at most one
button can be in the "on" state at any given time. Clicking on a check box to turn it on
forces any other check box in the same group that is on into the "off" state.
Exactly one check box button in a CheckboxGroup can be in the "on" state at any
given time. Pushing any button sets its state to "on" and forces any other button that
is in the "on" state into the "off" state.
The following code example produces a new check box group, with three check
boxes:
83
.
CheckboxGroup cbg = new CheckboxGroup();
add(new Checkbox("one", cbg, true));
add(new Checkbox("two", cbg, false));
add(new Checkbox("three", cbg, false));
The java.awt.Choice
The Choice class presents a popup menu of choices. The current choice is displayed
as the title of the menu.
import java.awt.*;
import java.applet.Applet;
Clicking on an item that isn't selected selects it. Clicking on an item that is already
selected deselects it. In the preceding example, only one item from the scrolling list
can be selected at a time, since the second argument when creating the new
scrolling list is false. Selecting an item causes any other selected item to be
automatically deselected.
84
.
Beginning with Java 1.1, the Abstract Window Toolkit sends the List object all
MOUSE, keyboard, and focus events that occur over it. (The old AWT event model is
being maintained only for backward compatibility, and its use is discouraged.)
When an item is selected or deselected, AWT sends an instance of Item Event to the
list. When the user double-clicks on an item in a scrolling list, AWT sends an instance
of ActionEvent to the list following the item event. AWT also generates an action
event when the user presses the return key while an item in the list is selected.
If an application wants to perform some action based on an item in this list being
selected or activated, it should implement ItemListener or ActionListener as
appropriate and register the new listener to receive events from this list.
For multiple selection scrolling lists, it is considered a better user interface to use an
external gesture (such as clicking on a button) to trigger the action.
A Canvas component represents a blank rectangular area of the screen onto which
the application can draw or from which the application can trap input events from the
user.
An application must subclass the Canvas class in order to get useful functionality
such as creating a custom component. The paint method must be overridden in order
to perform custom graphics on the canvas.
import java.awt.*;
public MyCanvas() {
this(100,100);
}
85
.
g.fillRect(0, 0, getSize().width-1, getSize().height-
1);
}
}
Alternatively, a scroll bar can represent a range of values. For example, if a scroll bar
is used for scrolling through text, the width of the "bubble" or "thumb" can represent
the amount of text that is visible. Here is an example of a scroll bar that represents a
range:
The value range represented by the bubble is the visible range of the scroll bar. The
horizontal scroll bar in this example could be created with code like the following:
Note that the maximum value above, 255, is the maximum value for the scroll bar's
bubble. The actual width of the scroll bar's track is 255 + 64. When the scroll bar is
set to its maximum value, the left side of the bubble is at 255, and the right side is at
255 + 64.
Normally, the user changes the value of the scroll bar by making a gesture with the
mouse. For example, the user can drag the scroll bar's bubble up and down, or click
in the scroll bar's unit increment or block increment areas. Keyboard gestures can
also be mapped to the scroll bar. By convention, the Page Up and Page Down keys
are equivalent to clicking in the scroll bar's block increment and block decrement
areas.
When the user changes the value of the scroll bar, the scroll bar receives an instance
of AdjustmentEvent. The scroll bar processes this event, passing it along to any
registered listeners.
Any object that wishes to be notified of changes to the scroll bar's value should
implement AdjustmentListener, an interface defined in the package java.awt.event.
Listeners can be added and removed dynamically by calling the methods
addAdjustmentListener and removeAdjustmentListener.
The AdjustmentEvent class defines five types of adjustment event, listed here:
AdjustmentEvent.TRACK is sent out when the user drags the scroll bar's bubble.
AdjustmentEvent.UNIT_INCREMENT is sent out when the user clicks in the left
arrow of a horizontal scroll bar, or the top arrow of a vertical scroll bar, or makes the
equivalent gesture from the keyboard.
86
.
AdjustmentEvent.UNIT_DECREMENT is sent out when the user clicks in the right
arrow of a horizontal scroll bar, or the bottom arrow of a vertical scroll bar, or makes
the equivalent gesture from the keyboard. AdjustmentEvent.BLOCK_INCREMENT is
sent out when the user clicks in the track, to the left of the bubble on a horizontal
scroll bar, or above the bubble on a vertical scroll bar. By convention, the Page Up
key is equivalent, if the user is using a keyboard that defines a Page Up key.
AdjustmentEvent.BLOCK_DECREMENT is sent out when the user clicks in the track,
to the right of the bubble on a horizontal scroll bar, or below the bubble on a vertical
scroll bar. By convention, the Page Down key is equivalent, if the user is using a
keyboard that defines a Page Down key.
The JDK 1.0 event system is supported for backward compatibility, but its use with
newer versions of JDK is discouraged. The fives types of adjustment event
introduced with JDK 1.1 correspond to the five event types that are associated with
scroll bars in previous JDK versions. The following list gives the adjustment event
type, and the corresponding JDK 1.0 event type it replaces.
A container class, which implements automatic horizontal and/or vertical scrolling for
a single child component. The display policy for the scrollbars can be set to:
1.as needed : scrollbars created and shown only when needed by scrollpane
2.always : scrollbars created and always shown by the scrollpane
3.never : scrollbars never created or shown by the scrollpane
The state of the horizontal and vertical scrollbars is represented by two objects (one
for each dimension) which implement the Adjustable interface. The API provides
methods to access those objects such that the attributes on the Adjustable object
(such as unitIncrement, value, etc.) can be manipulated.
If the scrollbar display policy is defined as "never", then the scrollpane can still be
programmatically scrolled using the setScrollPosition() method and the scrollpane will
move and clip the child's contents appropriately. This policy is useful if the program
needs to create and manage its own adjustable controls.
The initial size of this container is set to 100x100, but can be reset using setSize().
87
.
Insets are used to define any space used by scrollbars and any borders created by
the scroll pane. getInsets() can be used to get the current value for the insets. If the
value of scrollbarsAlwaysVisible is false, then the value of the insets will change
dynamically depending on whether the scrollbars are currently visible or not.
import java.applet.Applet;
import java.awt.*;
public class ScrollpaneTest extends Applet
{
public void init()
{
ScrollPane pane;
XCanvas xcan;
setLayout(new BorderLayout());
pane = new ScrollPane();
add("Center", pane);
xcan = new XCanvas();
xcan.setSize(200, 200);
pane.add(xcan);
}
}
import java.awt.*;
class InsetTest
{
public static void main (String[] args)
{
88
.
win.add( new Button("Four") );
win.pack();
win.show();
System.out.println( win.insets() );
}
}
class MyFrame extends Frame
{
public Insets insets() { return new Insets(100, 2, 2, 2); }
//public Insets insets() { return new Insets(25, 100, 2,
2); }
// public Insets insets() { return new Insets(25, 2, 100,
2); }
//public Insets insets() { return new Insets(25, 2, 2, 100); }
}
EVENT HANDLING
An event is a communication from the outside world to the program that something
has occurred. The following are a few basic event types:
• Mouse clicks
When the mouse button is clicked while positioned over a component.
• Mouse movement
The mouse is moved over a component, many events are sent to the
component informing it what coordinates in the component the mouse has
moved to.
• Action events
A component that an action can be performed upon is used, an Action event
is created by default and the owner of the component (usually the container in
which the component is placed) is notified that something happened.
One of the most important things to understand about the AWT is how events are
handled. Without events, your application will not be able to respond to user actions.
import java.awt.*;
import java.applet.Applet;
add(bt_submit);
89
.
add(tf_data);
}
public boolean action(Event evt, Object obj)
{
if(evt.target == bt_submit)
{
tf_data.setText("Button Clicked");
}
return true;
}
}
All event handlers have a form similar to this. They accept a parameter of type Event
that provides detailed information about the event. Second, they return a Boolean
value-indicating True if the event was handled or False if it was not.
if (evt.target == bt_submit) {
Here the target of the event is being checked to see whether or not it is the button.
Because evt.target and hiButton are both objects, we can check to see if they are the
same objects.
tf_data.setText(“Button Clicked”)
Because the button was clicked, we change the textfield to reflect that.
return true;
}
else
return false;
Finally, if the event was handled, return true or else return false. This is an important
concept to keep in mind: The event handler keeps searching for a method that will
accept the Event. Accepting the Event is signaled by returning true.
90
.
When would you want to use other methods than action()? The answer is that when
you actually want to change the behavior of a component (as opposed to just using
the component as is was originally designed) action() isn’t quite enough. It only
reports events that are essential to the utility of the component, such as a mouse
click on a button.
Let’s add new behavior to the previous example.
import java.awt.*;
import java.applet.Applet;
Button hiButton;
add(hiButton);
hiButton.setLabel(“Go Away!”);
return true;
hiButton.setLabel(“Stay Away!”);
return true;
if (evt.target == hiButton) {
hiButton.setLabel(“Clicked!”);
return true;
else
return false;
91
.
}
Now, whenever the mouse moves over the applet, the user is informed that perhaps
clicking on the button isn’t such a good idea. This is a fundamentally different
behavior than the previous example. Before, we were using a button in a completely
standard manner. Here, we wished to change that functionality. This is important to
remember—otherwise, you might end up sub-classing components where you don’t
need to, making your program slower and more difficult to understand and maintain.
handleEvent() or action()
Generally, a combination of action() and the other built-in event handlers will do the
job nicely. For those times when you want to take complete control of the process
yourself, handleEvent() is available.
handleEvent() has advantages and disadvantages. On the positive side, you have
complete control. On the negative side, you have complete control. This means that
you must be very careful overriding the default handleEvent() or your application can
become buggy and confusing very quickly.
For example, let’s say you overrode handleEvent() in your class for whatever reason,
but you had used mouseEnter() earlier in the development of the program, as shown
in the following:
92
.
return super.handleEvent(evt);
This has the benefit of keeping all of the functionality of the old handleEvent() while
letting you manipulate things first. Note, however, that you can also override
handleEvent() to remove functionality, in which case you wouldn’t want to call the
parent’s handleEvent(). It’s all up to you.
Delivering Events
Occasionally the ability of the program to manufacture its own events comes in quite
handy. Although it may seem strange to fake an event, in reality it makes the design
of a program much simpler.
For example, if you were designing a calculator you might decide to write an event
handler in the main container that deciphers the action events from the button, as
follows:
public boolean action(Event evt, obj What) {
if (evt.target == oneKey)
}
...
}
However, it might make sense to add the ability to handle keyboard input, because a
user of the calculator would expect that functionality from a calculator. Although you
could just copy the code from the action() handler to a new keyDown() handler, you
would then have two copies of the same code in the same program to maintain and
keep track of. The solution is to deliver your own event. A simple event can be
created with the following form:
Where target is the Object that you would like the event delivered to, id is an integer
representing the event type, and obj is an arbitrary argument to append to the event
if there is extra information that you would like the handler to receive. Then, to deliver
the event, you just need to call deliverEvent() as follows:
deliverEvent(aEvent);
So, in the previous example, you could add another handler that does the following:
93
.
Now you can manage the rest of the program without worrying about handling
keyboard input differently—the same event is generated whether the button is clicked
or the correspond
When a component loses the input focus, the lostFocus() method of that component
is called, as follows:
It is not uncommon for a program to desire to keep the focus. For example, if a text-
entry field were being used to display output rather than to accept input, you probably
would not want it to be able to receive the focus. Using a text-entry field to display
output enables you to take advantage of the field’s text-handling abilities. In that
case, the requestFocus() method exists, as shown in the following:
94
.
Event Handling in JDK 1.1 is through listeners. Listeners are provided as interfaces
to enable multiple listeners to a class and also to enable the class to provide a
specific definition. . In Java 1.1 and later versions, the Event class is maintained only
for backwards compatibilty
import java.awt.Button;
import java.applet.Applet;
import java.awt.event.*;
Methods
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
setLayout(new BorderLayout());
95
.
// A plain scrollbar that delegates to the applet.
Scrollbar sbar1 = new Scrollbar();
sbar1.addAdjustmentListener(this);
add(sbar1, "West");
// A subclass that handles its own adjustment events
SelfScrollbar sbar2 = new SelfScrollbar();
add(sbar2, "East");
}
public void adjustmentValueChanged(AdjustmentEvent e)
{
System.out.println("Scrollbar #1: " + e.getValue());
}
}
class SelfScrollbar extends Scrollbar
{
public SelfScrollbar()
{
enableEvents(AWTEvent.ADJUSTMENT_EVENT_MASK);
}
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
96
.
public SelfTextArea() {
enableEvents(AWTEvent.FOCUS_EVENT_MASK);
}
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
97
.
public SelfChoice() {
enableEvents(AWTEvent.ITEM_EVENT_MASK);
}
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
98
.
}
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
99
.
e.getX() + "," + e.getY());
}
100
.
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
101
.
AWT LAYOUTS
import java.awt.*;
102
.
import java.applet.Applet;
public class myButtons extends Applet {
Button button1, button2, button3;
FlowLayout flow;
A flow layout lets each component assume its natural (preferred) size.
A border layout lays out a container, arranging and resizing its components to fit in
five regions: North, South, East, West, and Center. When adding a component to a
container with a border layout, use one of these five names, for example:
The components are laid out according to their preferred sizes and the constraints of
the container's size. The North and South components may be stretched horizontally;
the East and West components may be stretched vertically; the Center component
may stretch both horizontally and vertically to fill any space left over.
Here is an example of five buttons in an applet laid out using the BorderLayout layout
manager:
import java.awt.*;
import java.applet.Applet;
public class buttonDir extends Applet {
public void init() {
setLayout(new BorderLayout());
add("North", new Button("North"));
add("South", new Button("South"));
add("East", new Button("East"));
103
.
add("West", new Button("West"));
add("Center", new Button("Center"));
}
}
A border layout lays out a container, arranging and resizing its components to fit in
five regions: North, South, East, West, and Center. When adding a component to a
container with a border layout, use one of these five names, for example:
The components are laid out according to their preferred sizes and the constraints of
the container's size. The North and South components may be stretched horizontally;
the East and West components may be stretched vertically; the Center component
may stretch both horizontally and vertically to fill any space left over.
Here is an example of five buttons in an applet laid out using the BorderLayout layout
manager:
import java.awt.*;
import java.applet.Applet;
public class buttonDir extends Applet {
public void init() {
setLayout(new BorderLayout());
add("North", new Button("North"));
add("South", new Button("South"));
add("East", new Button("East"));
add("West", new Button("West"));
add("Center", new Button("Center"));
}
}
104
.
The container is divided into equalsized rectangles, and one component is placed in
each rectangle.
For example, the following is an applet that lays out six buttons into three rows and
two columns:
import java.awt.*;
import java.applet.Applet;
public class ButtonGrid extends Applet
{
public void init()
{
setLayout(new GridLayout(3,2));
add(new Button("1"));
add(new Button("2"));
add(new Button("3"));
add(new Button("4"));
add(new Button("5"));
add(new Button("6"));
}
}
To use a grid bag layout effectively, you must customize one or more of the
GridBagConstraints objects that are associated with its components. You customize
a GridBagConstraints object by setting one or more of its instance variables:
gridx, gridy
Specifies the cell at the upper left of the component's display area, where the
upperleftmost cell has address gridx = 0, gridy = 0. Use
GridBagConstraints.RELATIVE (the default value) to specify that the component be
just placed just to the right of (for gridx) or just below (for gridy) the component that
was added to the container just before this component was added.
105
.
gridwidth, gridheight
Specifies the number of cells in a row (for gridwidth) or column (for gridheight) in the
component's display area. The default value is 1. Use
GridBagConstraints.REMAINDER to specify that the component be the last one in its
row (for gridwidth) or column (for gridheight). Use GridBagConstraints.RELATIVE to
specify that the component be the next to last one in its row (for gridwidth) or column
(for gridheight).
fill
Used when the component's display area is larger than the component's requested
size to determine whether (and how) to resize the component. Possible values are
GridBagConstraints.NONE (the default), GridBagConstraints.HORIZONTAL (make
the component wide enough to fill its display area horizontally, but don't change its
height), GridBagConstraints.VERTICAL (make the component tall enough to fill its
display area vertically, but don't change its width), and GridBagConstraints.BOTH
(make the component fill its display area entirely).
ipadx, ipady
Specifies the component's internal padding within the layout, how much to add to the
minimum size of the component. The width of the component will be at least its
minimum width plus (ipadx * 2) pixels (since the padding applies to both sides of the
component). Similarly, the height of the component will be at least the minimum
height plus (ipady * 2) pixels.
insets
Specifies the component's external padding, the minimum amount of space between
the component and the edges of its display area.
anchor
Used when the component is smaller than its display area to determine where (within
the display area) to place the component. Valid values are
GridBagConstraints.CENTER (the default), GridBagConstraints.NORTH,
GridBagConstraints.NORTHEAST, GridBagConstraints.EAST,
GridBagConstraints.SOUTHEAST, GridBagConstraints.SOUTH,
GridBagConstraints.SOUTHWEST, GridBagConstraints.WEST, and
GridBagConstraints.NORTHWEST.
weightx, weighty
Used to determine how to distribute space, which is important for specifying resizing
behavior. Unless you specify a weight for at least one component in a row (weightx)
and column (weighty), all the components clump together in the center of their
container. This is because when the weight is zero (the default), the GridBagLayout
object puts any extra space between its grid of cells and the edges of the container.
106
.
import java.awt.*;
public class Gridbag extends java.applet.Applet
{
public void init()
{
GridBagLayout gb = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
Button b;
setLayout(gb);
// gbc.fill= GridBagConstraints.HORIZONTAL;
gbc.anchor= GridBagConstraints.NORTHWEST;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.gridx = 0;
gbc.gridy = 0;
b = new Button("First");
gb.setConstraints(b, gbc);
add(b);
b = new Button("Second");
gbc.gridx = 1;
gbc.gridwidth = 2;
gb.setConstraints(b, gbc);
add(b);
b = new Button("Third");
gbc.gridx = 3;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gb.setConstraints(b, gbc);
add(b);
b = new Button("Fourth");
gbc.gridy++;
gbc.gridx = 0;
gb.setConstraints(b, gbc);
add(b);
b = new Button("Fifth");
gbc.gridwidth = 1;
gbc.gridy++;
gb.setConstraints(b, gbc);
add(b);
b = new Button("Sixth");
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.gridx = 2;
gb.setConstraints(b, gbc);
add(b);
}
}
107
.
Java Foundation Classes
To address the shortcomings of the AWT, the Java Foundation Classes (JFC) were
developed. JFC 1.2 is an extension of the AWT, not a replacement of it. The JFC
visual components extend the AWT container class. So the methods in the
Component & Container classes are still valid.
Swing
Pluggable Look-and-feel(PL&F)
Drag and Drop
Accessibility
2D
Swing
108
.
Swing components allow for efficient graphical user interface development. Swing
Components are lightweight components. The major difference between lightweight
and heavyweight components is that a lightweight component can have transparent
pixels while a heavyweight component is always opaque. By taking advantage of
transparent pixels, a lightweight component can appear to be non-rectangular, while
a heavyweight component must always be rectangular. A mouse event occuring in a
lightweight component falls through to its parent component, while a mouse event in
a heavyweight component doesnot propagate through its parent component. Swing
Components are Java Bean Compliant.
Application Code
JFC Java 2D
AWT
Accessibility
109
.
The Swing component toolkit consists of over 250 pure Java classes and 75
interfaces contained in more than 10 packages. Swing consists of UI and non-UI
classes. UI components descend from the Jcomponent class. Non-UI classes consist
of the event related classes.
javax.swing
javax.swing.border
javax.swing.text
javax.swing.table
javax.swing.event
javax.swing.undo
javax.swing.plaf etc
import javax.swing.*;
import java.awt.*;
public class HelloSwing extends JFrame {
JLabel text;
public HelloSwing(String title) {
super(title);
text = new JLabel("Hello Swing");
getContentPane().add(BorderLayout.NORTH,text);
pack();
setVisible(true);
}
public static void main(String arg[])
{
HelloSwing swing = new HelloSwing("First
Swing");
}
}
An Insight :
The line "import javax.swing.*" denotes the package that includes all the swing APIs.
The JLabel & JFrame are swing components extending JComponent. The method
"getContentPane()" returns reference to a JContainer on which the components can
be added.
Applying Border:
The statement,
110
.
Other Borders
MatteBorder
LineBorder
EtchedBorder
EmptyBorder
BevelBorder
SoftBevelBorder
CompoundBorder
----
JPanel panel2, panel3;
panel2 = new JPanel();
panel3 = new JPanel();
panel2.setBorder(BorderFactory.createLineBorder(Color.red,5));
Icon imgIcon = new ImageIcon("dot.gif");
MatteBorder matte = new MatteBorder(imgIcon);
panel3.setBorder(matte);
----
Creating an ImageButton
---
Icon imgIcon = new ImageIcon(“dot.gif”);
JButton imgBtn = new JButton(“Click here”,imgIcon);
---
111
.
Example Using JProgressBar
import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;
import java.awt.event.*;
public class UsingProgressBar extends JFrame implements
ActionListener,Runnable {
JButton start;
JProgressBar progress;
int count = 0;
Thread t = null;
first.add(a_first); first.add(b_first);
second.add(a_second);
second.add(b_second);
main.add(first); main.add(second);
112
.
Using JTable
row.addElement("COMXpert");
row.addElement("ASP & COM");
data.addElement(row);
column.addElement("Course Name");
column.addElement("Course Contents");
JTable table = new JTable(data,column);
//code to add the table
113
.
This displays a table as follows:
Using Toolitp
The statement,
setTooltipText(String text)
is used to set the tooltip for a JComponent
Example
JButton btn = new JButton(“Submit”);
btn.setTooltipText(“Click here “);
KeyStroke Handling :
//code to be added
tf_data.registerKeyboardAction(this,KeyStroke.getKeyStroke
(KeyEvent.VK_ESCAPE,0),JComponent.WHEN_FOCUSED);
The above statement listens to the Escape key press on the textfield tf_data and
performs the code stated in the actionPerformed block of the ActionListener
---
Image img = getToolkit().getImage("duke.gif");
Cursor cr = getToolkit().createCustomCursor
(img, new Point(16,16), "crosshair cursor");
btn.setCursor(cr);
---
creates a cursor with image of a duke. This cursor is made visible when the mouse is
moved over the button.
114
.
Multithreading
115
.
CHAPTER – 6 : MULTITHREADING
Threads are a relatively recent invention in the computer science world. Although
processes, their larger parent, have been around for decades, threads have only
recently been accepted into the mainstream. What’s odd about this is that they are
extremely valuable, and programs written with them are noticeably better, even to the
casual user. In fact, some of the best individual, Herculean efforts over the years
have involved implementing a threads-like facility by hand to give a program a more
friendly feel to its users.
Imagine that you’re using your favorite text editor on a large file. When it starts up,
does it need to examine the entire file before it lets you edit? Does it need to make a
copy of the file? If the file is huge, this can be a nightmare. Wouldn’t it be nicer for it
to show you the first page, enabling you to begin editing, and somehow (in the
background) complete the slower tasks necessary for initialization? Threads allow
exactly this kind of within-the-program parallelism.
Perhaps the best example of threading (or lack of it) is a Web browser. Can your
browser download an indefinite number of files and Web pages at once while still
enabling you to continue browsing? While these pages are downloading, can your
browser download all the pictures, sounds, and so forth in parallel, interleaving the
fast and slow download times of multiple Internet servers? HotJava can do all of
these things—and more—by using the built-in threading of the Java language.
Depending on your experience with operating systems and with environments within
those systems, you may or may not have run into the concept of threads. Let’s start
from the beginning with some definitions.
When a program runs, it starts executing, runs its initialization code, calls methods or
procedures, and continues running and processing until it’s complete or until the
program is exited. That program uses a single thread—where the thread is a single
locus of control for the program.
Multithreading, as in Java, enables several different execution threads to run at the
same time inside the same program, in parallel, without interfering with each other.
Here’s a simple example. Suppose you have a long computation near the start of a
program’s execution. This long computation may not be needed until later on in the
program’s execution—it’s actually tangential to the main point of the program, but it
needs to get done eventually. In a single-threaded program, you have to wait for that
computation to finish before the rest of the program can continue running. In a
multithreaded system, you can put that computation into its own thread, enabling the
rest of the program to continue running independently.
Using threads in Java, you can create an applet so that it runs in its own thread, and
it will happily run all by itself without interfering with any other part of the system.
Using threads, you can have lots of applets running at once on the same page.
Depending on how many you have, you may eventually exhaust the system so that
all of them will run slower, but all of them will run independently.
Even if you don’t have lots of applets, using threads in your applets is good Java
programming practice. The general rule of thumb for well-behaved applets:
Whenever you have any bit of processing that is likely to continue for a long time
(such as an animation loop, or a bit of code that takes a long time to execute), put it
in a thread.
116
.
Thread Scheduling
The part of the system that decides the real-time ordering of threads is called the
scheduler.
You might wonder exactly what order your threads will be run in, and how you can
control that order. Unfortunately, the current implementations of the Java system
cannot precisely answer the former, though with a lot of work, you can always do the
latter.
Normally, any scheduler has two fundamentally different ways of looking at its job:
nonpreemptive scheduling and preemptive time-slicing.
Note:
With non-preemptive scheduling, the scheduler runs the current thread forever,
requiring that thread explicitly to tell it when it is safe to start a different thread.
With preemptive time-slicing, the scheduler runs the current thread until it has used
up a certain tiny fraction of a second, and then “preempts” it, suspend()s it, and
resume()s another thread for the next tiny fraction of a second.
The current Java release does not precisely specify the behavior of its scheduler.
Threads can be assigned priorities, and when a choice is made between several
threads that all want to run, the highest-priority thread wins. However, among threads
that are all the same priority, the behavior is not well-defined. In fact, the different
platforms on which Java currently runs have different behaviors—some behaving
more like a preemptive scheduler and some more like a non-preemptive scheduler.
117
.
How do you create an applet that uses threads? There are several things you need
to do.
There are four modifications you need to make to create an applet that uses threads:
• Change the signature of your applet class to include the word implements
Runnable.
• Include an instance variable to hold this applet’s thread.
• Modify your start() method to do nothing but spawn a thread and start it
running.
• Create a run() method that contains the actual code that starts your applet
running.
Step 1:
The first change is to the first line of your class definition.
You need to change it to the following:
The Runnable interface should be implemented by any class whose instances are
intended to be executed by a thread. The class must define a method of no
arguments called run. This interface is designed to provide a common protocol for
objects that wish to execute code while they are active. For example, Runnable is
implemented by class Thread. Being active simply means that a thread has been
started and has not yet been stopped. In addition, Runnable provides the means for
a class to be active while not subclassing Thread. A class that implements Runnable
can run without subclassing Thread by instantiating a Thread instance and passing
itself in as the target. In most cases, the Runnable interface should be used if you are
only planning to override the run() method and no other Thread methods. This is
important because classes should not be subclassed unless the programmer intends
on modifying or enhancing the fundamental behavior of the class.
Step 2:
The second step is to add an instance variable to hold this applet’s thread. Call it
anything you like; it’s a variable of the type Thread (Thread is a class in java.lang, so
you don’t have to import it):
Thread runner:
118
.
Step 3:
Third, add a start() method or modify the existing one so that it does nothing but
create a new thread and start it running. Here’s a typical example of a start() method:
public void start()
{
if (runner == null);
{
runner = new Thread(this);
runner.start();
}
}
Step 4 :
If you modify start() to do nothing but spawn a thread, where does the body of your
applet go? It goes into a new method, run(), which looks like this:
public void run() {
}
run() can contain anything you want to run in the separate thread: initialization code,
the actual loop for your applet, or anything else that needs to run in its own thread.
You also can create new objects and call methods from inside run(), and they’ll also
run inside that thread. The run method is the real heart of your applet.
Step 5:
Finally, now that you’ve got threads running and a start method to start them, you
should add a stop() method to suspend execution of that thread (and therefore
whatever the applet is doing at the time) when the reader leaves the page. stop(), like
start(), is usually something along these lines:
public void stop()
{
if (runner != null)
{
runner.stop();
runner = null;
}
}
Setting the variable to null makes the Thread object it previously contained available
for garbage collection so that the applet can be removed from memory after a certain
amount of time. If the reader comes back to this page and this applet, the start
method creates a new thread and starts up the applet once again.
Now you have a well-behaved applet that runs in its own thread.
119
.
The Problem with Parallelism
If threading is so wonderful, why doesn’t every system have it? Many modern
operating systems have the basic primitives needed to create and run threads, but
they are missing a key ingredient. The rest of their environment is not thread-safe.
Imagine that you are in a thread, one of many, and each of you is sharing some
important data managed by the system. If you were managing that data, you could
take steps to protect it but the system is managing it. Now visualize a piece of code
in the system that reads some crucial value, thinks about it for a while, and then adds
1 to the value:
if (crucialValue > 0)
{
... // think about what to do
crucialValue += 1;
}
Remember that any number of threads may be calling upon this part of the system at
once. The disaster occurs when two threads have both executed the if test before
either has incremented the crucialValue. In that case, the value is clobbered by them
both with the same crucialValue + 1, and one of the increments has been lost. This
may not seem so bad to you, but imagine instead that the crucial value affects the
state of the screen as it is being displayed. Now, unfortunate ordering of the threads
can cause the screen to be updated incorrectly. In the same way, mouse or keyboard
events can be lost, databases can be inaccurately updated, and so forth.
This disaster is inescapable if any significant part of the system has not been written
with threads in mind. Therein lies the barrier to a mainstream threaded environment
—the large effort required to rewrite existing libraries for thread safety. Luckily, Java
was written from scratch with this is mind, and every Java class in its library is
thread-safe. Thus, you now have to worry only about your own synchronization and
thread-ordering problems, because you can assume that the Java system will do the
right thing.
• The exit method of class Runtime has been called and the security manager has
permitted the exit operation to take place.
• All threads that are not daemon threads have died, either by returning from the
call to the run method or by performing the stop method.
There are two ways to create a new thread of execution. One is to declare a class to
be a subclass of Thread. This subclass should override the run method of class
120
.
Thread. An instance of the subclass can then be allocated and started. For example,
a thread that computes primes larger than a stated value could be written as follows:
The following code would then create a thread and start it running:
The other way to create a thread is to declare a class that implements the Runnable
interface. That class then implements the run method. An instance of the class can
then be allocated, passed as an argument when creating Thread, and started. The
same example in this other style looks like the following:
The following code would then create a thread and start it running:
Every thread has a name for identification purposes. More than one thread may have
the same name. If a name is not specified when a thread is created, a new name is
generated for it.
Constructors
public Thread()
Allocates a new Thread object. This constructor has the same effect as Thread(null,
null, gname), where gname is a newly generated name. Automatically generated
names are of the form "Thread-"+n, where n is an integer. Threads created this way
must have overridden their run() method to actually do anything.
121
.
import java.lang.*;
class plain01 implements Runnable
{
String name;
plain01() {
name = null;
}
plain01(String s) {
name = s;
}
public void run() {
if (name == null)
System.out.println("A new thread created");
else
System.out.println("A new thread with name "
+ name +
" created");
}
}
class threadtest01 {
public static void main(String args[] ) {
int failed = 0 ;
Thread t1 = new Thread();
if (t1 != null)
System.out.println("new Thread() succeed");
else {
System.out.println("new Thread() failed");
failed++;
}
}
}
122
.
Allocates a new Thread object. This constructor has the same effect as Thread(null,
target, name).
123
.
{
System.out.println("***");
}
}
}
Synchronization
When dealing with multiple threads, consider this: What happens when two or more
threads want to access the same variable at the same time, and at least one of the
Threads wants to change the variable? If they were allowed to do this at will, chaos
would reign. For example, while one thread reads Joe Smith's record, another thread
tries to change his salary (Joe has earned a 50-cent raise). The problem is that this
little change causes the Thread reading the file in the middle of the others update to
see something somewhat random, and it thinks Joe has gotten a $500 raise. That's a
great thing for Joe, but not such a great thing for the company, and probably a worse
thing for the programmer who will lose his job because of it. How do you resolve this?
The first thing to do is declare the method that will change the data and the method
that will read to be synchronized. Java's key word, synchronized, tells the system to
put a lock around a particular method. At most, one thread may be in any
synchronized method at a time.
124
.
Now, while in setVar() the Java VM sets a condition lock, and no other thread will be
allowed to enter a synchronized method, including getVar(), until setVar() has
finished. Because the other threads are prevented from entering getVar(), no thread
will obtain information which is not correct because setVar() is in mid-write.
A thread group represents a set of threads. In addition, a thread group can also
include other thread groups. The thread groups form a tree in which every thread
group except the initial thread group has a parent.
A thread is allowed to access information about its own thread group, but not to
access information about its thread group's parent thread group or any other thread
groups.
Constructors
125
.
The Daemon Property
Threads can be one of two types: either a thread is a user thread or a Daemon
thread.
Daemon thread is not a natural thread, either. You can set off Daemon threads on a
path without ever worrying whether they come back. Once you start a Daemon
thread, you don't need to worry about stopping it. When the thread reaches the end
of the tasks it was assigned, it stops and changes its state to inactive, much like user
threads.
A very important difference between Daemon threads and user threads is that
Daemon Threads can run all the time. If the Java interpreter determines that only
Daemon threads are running, it will exit, without worrying if the Daemon threads have
finished. This is very useful because it enables you to start threads that do things
such as monitoring; they die on their own when there is nothing else running.
Two methods in java.lang.Thread deal with the Daemonic state assigned to a thread:
• isDaemon()
• setDaemon(boolean)
The first method, isDaemon(), is used to test the state of a particular thread.
Occasionally, this is useful to an object running as a thread so it can determine if it is
running as a Daemon or a regular thread. isDaemon() returns true if the thread is a
Daemon, and false otherwise.
The second method, setDaemon(boolean), is used to change the daemonic state of
the thread. To make a thread a Daemon, you indicate this by setting the input value
to true. To change it back to a user thread, you set the Boolean value to false.
An instance of ThreadDeath is thrown in the victim thread when the stop method with
zero arguments in class Thread is called. An application should catch instances of
this class only if it must clean up after being terminated asynchronously. If
ThreadDeath is caught by a method, it is important that it be rethrown so that the
thread actually dies.
The top-level error handler does not print out a message if ThreadDeath is never
caught. The class ThreadDeath is specifically a subclass of Error rather than
Exception, even though it is a "normal occurrence", because many applications catch
all occurrences of Exception and then discard the exception.
126
.
Networking
127
.
CHAPTER – 7: INTRODUCTION TO NETWORKING
TCP/IP Protocols
Three protocols are most commonly used within the TCP/IP scheme and a closer
investigation of their properties is warranted. Understanding how these three
protocols (IP, TCP, and UDP) interact is critical to developing network applications.
IP is the keystone of the TCP/IP suite. All data on the Internet flows through IP
packets, the basic unit of IP transmissions. IP is termed a connectionless, unreliable
protocol. As a connectionless protocol, IP does not exchange control information
before transmitting data to a remote system—packets are merely sent to the
destination with the expectation that they will be treated properly. IP is unreliable
because it does not retransmit lost packets or detect corrupted data. These tasks
must be implemented by higher level protocols, such as TCP.
It is important to realize that these domain names are not used nor understood by IP.
When an application wants to transmit data to another machine on the Internet, it
must first translate the domain name to an IP address using the DNS. A receiving
application can perform a reverse translation, using the DNS to return a domain
name given an IP address. There is not a one-to-one correspondence between IP
addresses and domain names: A domain name can map to multiple IP addresses,
and multiple IP addresses can map to the same domain name.
128
.
THE INETADDRESS CLASS
This class represents an Internet Protocol (IP) address. Applications should use the
methods getLocalHost, getByName, or getAllByName to create a new InetAddress
instance. Transmission Control Protocol (TCP)
Most Internet applications use TCP to implement the transport layer. TCP provides a
reliable, connection-oriented, continuous-stream protocol. The implications of these
characteristics are:
• Reliable. When TCP segments, the smallest unit of TCP transmissions, are
lost or corrupted, the TCP implementation will detect this and retransmit
necessary segments.
Because of these characteristics, it is easy to see why TCP would be used by most
Internet applications. TCP makes it very easy to create a network application, freeing
you from worrying how the data is broken up or about coding error correction
routines. However, TCP requires a significant amount of overhead and perhaps you
might wish to code routines that more efficiently provide reliable transmissions given
the parameters of your application. Furthermore, retransmission of lost data may be
inappropriate for your application, because such information's usefulness may have
expired.
An important addressing scheme which TCP defines is the port. Ports separate
various TCP communications streams which are running concurrently on the same
system. For server applications, which wait for TCP clients to initiate contact, a
specific port can be established from where communications will originate. These
concepts come together in a programming abstraction known as sockets.
129
.
For some applications, UDP is more appropriate than TCP. For instance, with the
Network Time Protocol (NTP), lost data indicating the current time would be invalid
by the time it was retransmitted. In a LAN environment, Network File System (NFS)
can more efficiently provide reliability at the application layer and thus uses UDP.
As with TCP, UDP provides the addressing scheme of ports, allowing for many
applications to simultaneously send and receive datagrams. UDP ports are distinct
from TCP ports. For example, one application can respond to UDP port 512 while
another unrelated service handles TCP port 512.
Most URLs used conform to a general format that follows the following pattern:
scheme-name://host:port/file-info#internal-reference
Scheme-name is a URL scheme such as HTTP, FTP, or Gopher. Host is the domain
name or IP address of the remote system. Port is the port number on which the
service is listening; since most application protocols define a standard port, unless a
non-standard port is being used, the port and the colon which delimits it from the host
is omitted. File-info is the resource requested on the remote system, which often
times is a file. However, the file portion may actually execute a server program and it
usually includes a path to a specific file on the system. The internal-reference is
usually the identifier of a named anchor within an HTML page. A named anchor
allows a link to target a particular location within an HTML page. Usually this is not
used, and this token with the # character that delimits it is omitted.
130
.
the Internet. The java.net package contains the sources of this power, the URL and
URLConnection classes.
In general, a URL can be broken into several parts. The previous example of a URL
indicates that the protocol to use is http (HyperText Transport Protocol) and that the
information resides on a host machine named www.ncsa.uiuc.edu. The information
on that host machine is named demoweb/url-primer.html. The exact meaning of this
name on the host machine is both protocol dependent and host dependent. The
information normally resides in a file, but it could be generated on the fly. This
component of the URL is called the file component, even though the information is
not necessarily in a file.
A URL can optionally specify a "port", which is the port number to which the TCP
connection is made on the remote host machine. If the port is not specified, the
default port for the protocol is used instead. For example, the default port for http is
80. An alternative port could be specified as:
http://www.ncsa.uiuc.edu:8080/demoweb/url-primer.html
http://java.sun.com/index.html#chapter1
This anchor is not technically part of the URL. Rather, it indicates that after the
specified resource is retrieved, the application is specifically interested in that part of
the document that has the tag chapter1 attached to it. The meaning of a tag is
resource specific.
An application can also specify a "relative URL", which contains only enough
information to reach the resource relative to another URL. Relative URLs are
frequently used within HTML pages.
The relative URL need not specify all the components of a URL. If the protocol, host
name, or port number is missing, the value is inherited from the fully specified URL.
The file component must be specified. The optional anchor is not inherited.
131
.
GetURLApp.java
import java.net.URL;
import java.net.MalformedURLException;
import java.io.*;
The abstract class URLConnection is the superclass of all classes that represent a
communications link between the application and a URL. Instances of this class can
be used both to read from and to write to the resource referenced by the URL. In
general, creating a connection to a URL is a multistep process:
openConnection()
connect()
132
.
133
.
What is a Socket?
A socket is a handle to a communications link over the network with another
application. A TCP socket is one that utilizes the TCP protocol, inheriting the behavior
of that transport protocol. Four pieces of information are needed to create a TCP
socket:
Sockets are often used in client-server applications: A centralized service waits for
various remote machines to request specific resources, handling each request as it
arrives. In order for clients to know how to communicate with the server, standard
application protocols are assigned well-known ports. On UNIX operating systems,
ports below 1024 can only be bound by applications with super-user (for example,
root) privileges, and thus for control, these well-known ports lie within this range, by
convention. Some well known ports are shown in the following table.
Port Service
21 FTP
23 Telnet
25 SMTP (Internet Mail Transfer)
79 Finger
80 HTTP
For many application protocols, you can merely use the Telnet application to connect
to the service port and then manually emulate a client. This may help you understand
how client-server communications work.
Client applications must also obtain, or bind, a port to establish a socket connection.
Because the client initiates the communication with the server, such a port number
could conveniently be assigned at runtime. Client applications are usually run by
normal, unprivileged users on UNIX systems, and thus these ports are allocated from
the range above 1024. This convention has held when migrated to other operating
systems, and client applications are generally given a dynamically-allocated port
above 1024.
Because no two applications can bind the same port on the same machine
simultaneously, a socket uniquely identifies a communications link. Realize that a
server may respond to two clients on the same port, since the clients will be on
different systems and/or different ports; the uniqueness of the link's characteristics
are preserved.
Java has a number of classes, which allow you to create socket-based network
applications. The two classes you use include java.net.Socket and
java.net.ServerSocket.
134
.
This class implements server sockets. A server socket waits for requests to come in
over the network. It performs some operation based on that request, and then
possibly returns a result to the requester.
The actual work of the server socket is performed by an instance of the SocketImpl
class. An application can change the socket factory that creates the socket
implementation to configure itself to create sockets appropriate to the local firewall.
ServerExample.java
import java.io.*;
import java.net.*;
import java.util.Date;
135
.
This class implements client sockets (also called just "sockets"). A socket is an
endpoint for communication between two machines. The actual work of the socket is
performed by an instance of the SocketImpl class. An application, by changing the
socket factory that creates the socket implementation, can configure itself to create
sockets appropriate to the local firewall.
import java.io.*;
import java.net.*;
136
.
UDP must be quanticized into small messages that fit within a small packet of a
specific size, although some packets can hold more data than others. When you
send out a message, you can never be certain that you will receive a return
message. Unless you do receive a return message, you have no idea if your
message was received—your message could have been lost en route, the recipient’s
confirmation could have been lost, or the recipient might be ignoring your message.
The postcards you will be exchanging between network programs are referred to as
datagrams. Within a datagram, you can store an array of bytes. A receiving
application can extract this array and decode your message, possibly sending a
return datagram response. As with TCP, you will program in UDP using the socket
programming abstraction. However, UDP sockets are very different from TCP
sockets. Extending the analogy, UDP sockets are much like creating a mailbox. A
mailbox is identified by your address, but you don't construct a new one for each
person to whom you will be sending a message. Instead, you place an address on
the postcard that indicates to whom the message is intended. You place the postcard
in the mailbox and it is (eventually) sent on its way.
When receiving a message, you could potentially wait forever until one arrives in your
mailbox. Once one does, you can read the postcard. Meta-information appears on
the postcard that identifies the sender through the return address. As the previous
analogies suggest, UDP programming involves the following general tasks:
• Creating an appropriately addressed datagram to send.
• Setting up a socket to send and receive datagrams for a particular
application.
• Inserting datagrams into a socket for transmission.
• Waiting to receive datagrams from a socket.
• Decoding a datagram to extract the message, its recipient, and other meta-
information.
This class represents a datagram packet. Datagram packets are used to implement a
connectionless packet delivery service. Each message is routed from one machine to
another based solely on information contained within that packet. Multiple packets
sent from one machine to another might be routed differently, and might arrive in any
order.
import java.net.*;
import java.io.*;
137
.
{
System.out.println("Usage : java DatagramClient <server
address>");
System.exit(0);
}
String address = args[0];
DatagramPacket dgp = null;
DatagramSocket dgs = null;
byte receive[] = new byte[50];
try
{
dgs = new
DatagramSocket(5000,InetAddress.getByName(address));
dgp = new DatagramPacket(receive,receive.length);
dgs.receive(dgp);
System.out.println("data received : "+(new
String(receive)));
dgs.close();
}
catch(Exception err) {
System.out.println("Exception in client");
}
}
}
This class represents a socket for sending and receiving datagram packets. A
datagram socket is the sending or receiving point for a connectionless packet
delivery service. Each packet sent or received on a datagram socket is individually
addressed and routed. Multiple packets sent from one machine to another may be
routed differently, and may arrive in any order.
138
.
139
.
CHAPTER – 8: JDBC
JDBCTM - Connecting Java and Databases
Java Database Connectivity is a standard SQL database access interface,
providing uniform access to a wide range of relational databases. It also provides
a common base on which higher-level tools and interfaces can be built. This
comes with an "ODBC Bridge". The Bridge is a library which implements JDBC in
terms of the ODBC standard C API.
What Is JDBCTM?
JDBCTM is a JavaTM API for executing SQL statements. (As a point of interest, JDBC
is a trademarked name and is not an acronym; nevertheless, JDBC is often thought
of as standing for "Java Database Connectivity".) It consists of a set of classes and
interfaces written in the Java programming language. JDBC provides a standard API
for tool/database developers and makes it possible to write database applications
using a pure Java API.
Using JDBC, it is easy to send SQL statements to virtually any relational database. In
other words, with the JDBC API, it isn't necessary to write one program to access a
Sybase database, another program to access an Oracle database, another program
to access an Informix database, and so on. One can write a single program using the
JDBC API, and the program will be able to send SQL statements to the appropriate
database. And, with an application written in the Java programming language, one
also doesn't have to worry about writing different applications to run on different
platforms. The combination of Java and JDBC lets a programmer write it once and
run it anywhere.
Java, being robust, secure, easy to use, easy to understand, and automatically
downloadable on a network, is an excellent language basis for database applications.
What is needed is a way for Java applications to talk to a variety of different
databases. JDBC is the mechanism for doing this.
JDBC extends what can be done in Java. For example, with Java and the JDBC API,
it is possible to publish a web page containing an applet that uses information
obtained from a remote database. Or an enterprise can use JDBC to connect all its
employees (even if they are using a conglomeration of Windows, Macintosh, and
UNIX machines) to one or more internal databases via an intranet. With more and
more programmers using the Java programming language, the need for easy
database access from Java is continuing to grow.
MIS managers like the combination of Java and JDBC because it makes
disseminating information easy and economical. Businesses can continue to use
their installed databases and access information easily even if it is stored on different
database management systems. Development time for new applications is short.
Installation and version control are greatly simplified. A programmer can write an
application or an update once, put it on the server, and everybody has access to the
latest version. And for businesses selling information services, Java and JDBC offer
a better way of getting out information updates to external customers.
140
.
What Does JDBC Do?
Simply put, JDBC makes it possible to do three things:
The following code fragment gives a basic example of these three steps:
Connection con = DriverManager.getConnection (
"jdbc:odbc:wombat", "login", "password");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (rs.next()) {
int x = getInt("a");
String s = getString("b");
float f = getFloat("c");
}
JDBC Is a Low-level API and a Base for Higher-level APIs
JDBC is a "low-level" interface, which means that it is used to invoke (or "call") SQL
commands directly. It works very well in this capacity and is easier to use than other
database connectivity APIs, but it was designed also to be a base upon which to
build higher-level interfaces and tools. A higher-level interface is "user-friendly," using
a more understandable or more convenient API that is translated behind the scenes
into a low-level interface such as JDBC. At the time of this writing, two kinds of
higher-level APIs are under development on top of JDBC:
3. an embedded SQL for Java. At least one vendor plans to build this. DBMSs
implement SQL, a language designed specifically for use with databases.
JDBC requires that the SQL statements be passed as Strings to Java
methods. An embedded SQL preprocessor allows a programmer to instead
mix SQL statements directly with Java: for example, a Java variable can be
used in a SQL statement to receive or provide SQL values. The embedded
SQL preprocessor then translates this Java/SQL mix into Java with JDBC
calls.
4. a direct mapping of relational database tables to Java classes. JavaSoft and
others have announced plans to implement this. In this "object/relational"
mapping, each row of the table becomes an instance of that class, and each
column value corresponds to an attribute of that instance. Programmers can
then operate directly on Java objects; the required SQL calls to fetch and
store data are automatically generated "beneath the covers." More
sophisticated mappings are also provided, for example, where rows of
multiple tables are combined in a Java class.
As interest in JDBC has grown, more developers have been working on JDBC-based
tools to make building programs easier, as well. Programmers have also been writing
applications that make accessing a database easier for the end user. For example,
an application might present a menu of database tasks from which to choose. After a
task is selected, the application presents prompts and blanks for filling in information
needed to carry out the selected task. With the requested input typed in, the
application then automatically invokes the necessary SQL commands. With the help
of such an application, users can perform database tasks even when they have little
or no knowledge of SQL syntax.
141
.
142
.
database may be located on another machine to which the user is connected via a
network. This is referred to as a client/server configuration, with the user's machine
as the client, and the machine housing the database as the server. The network can
be an intranet, which, for example, connects employees within a corporation, or it can
be the Internet.
Java
Application
Client Machine
JDBC
In the three-tier model, commands are sent to a "middle tier" of services, which then
send SQL statements to the database. The database processes the SQL statements
and sends the results back to the middle tier, which then sends them to the user. MIS
directors find the three-tier model very attractive because the middle tier makes it
possible to maintain control over access and the kinds of updates that can be made
to corporate data. Another advantage is that when there is a middle tier, the user can
employ an easy-to-use higher-level API which is translated by the middle tier into the
appropriate low-level calls. Finally, in many cases the three-tier architecture can
provide performance advantages.
Java Applet or
HTML Browser
Application
Server (Java)
JDBC
DBMS
Until now the middle tier has typically been written in languages such as C or C++,
which offer fast performance. However, with the introduction of optimizing compilers
that translate Java bytecode into efficient machine-specific code, it is becoming
practical to implement the middle tier in Java. This is a big plus, making it possible to
take advantage of Java's robustness, multithreading, and security features. JDBC is
important to allow database access from a Java middle tier.
143
.
SQL Conformance
Structured Query Language (SQL) is the standard language for accessing relational
databases. One area of difficulty is that although most DBMSs (DataBase
Management Systems) use a standard form of SQL for basic functionality, they do
not conform to the more recently-defined standard SQL syntax or semantics for more
advanced functionality. For example, not all databases support stored procedures or
outer joins, and those that do are not consistent with each other. It is hoped that the
portion of SQL that is truly standard will expand to include more and more
functionality. In the meantime, however, the JDBC API must support SQL as it is.
One way the JDBC API deals with this problem is to allow any query string to be
passed through to an underlying DBMS driver. This means that an application is free
to use as much SQL functionality as desired, but it runs the risk of receiving an error
on some DBMSs. In fact, an application query need not even be SQL, or it may be a
specialized derivative of SQL designed for specific DBMSs (for document or image
queries, for example).
A second way JDBC deals with problems of SQL conformance is to provide ODBC-
style escape clauses.
The escape syntax provides a standard JDBC syntax for several of the more
common areas of SQL divergence. For example, there are escapes for date literals
and for stored procedure calls.
For complex applications, JDBC deals with SQL conformance in a third way. It
provides descriptive information about the DBMS by means of the
DatabaseMetaData interface so that applications can adapt to the requirements and
capabilities of each DBMS.
Because the JDBC API will be used as a base API for developing higher-level
database access tools and APIs, it also has to address the problem of conformance
for anything built on it. The designation "JDBC COMPLIANT TM" was created to set a
standard level of JDBC functionality on which users can rely. In order to use this
designation, a driver must support at least ANSI SQL-2 Entry Level. (ANSI SQL-2
refers to the standards adopted by the American National Standards Institute in 1992.
Entry Level refers to a specific list of SQL capabilities.) Driver developers can
ascertain that their drivers meet these standards by using the test suite available with
the JDBC API.
The "JDBC COMPLIANTTM" designation indicates that a vendor's JDBC
implementation has passed the conformance tests provided by JavaSoft. These
conformance tests check for the existence of all of the classes and methods defined
in the JDBC API, and check as much as possible that the SQL Entry Level
functionality is available. Such tests are not exhaustive, of course, and JavaSoft is
not currently branding vendor implementations, but this compliance definition
provides some degree of confidence in a JDBC implementation. With wider and
wider acceptance of the JDBC API by database vendors, connectivity vendors,
Internet service vendors, and application writers, JDBC is quickly becoming the
standard for Java database access.
JavaSoft Framework
JavaSoft provides three JDBC product components as part of the Java Development
Kit (JDK):
• the JDBC driver manager,
• the JDBC driver test suite, and
• the JDBC-ODBC bridge.
The JDBC driver manager is the backbone of the JDBC architecture. It actually is
quite small and simple; its primary function is to connect Java applications to the
correct JDBC driver and then get out of the way.
144
.
The JDBC driver test suite provides some confidence that JDBC drivers will run your
program. Only drivers that pass the JDBC driver test suite can be designated JDBC
COMPLIANTTM.
The JDBC-ODBC bridge allows ODBC drivers to be used as JDBC drivers. It was
implemented as a way to get JDBC off the ground quickly, and long term will provide
a way to access some of the less popular DBMSs if JDBC drivers are not
implemented for them.
JDBC Driver Types
The JDBC drivers that we are aware of at this time fit into one of four categories:
1. JDBC-ODBC bridge plus ODBC driver: The JavaSoft bridge product
provides JDBC access via ODBC drivers. Note that ODBC binary code, and
in many cases database client code, must be loaded on each client machine
that uses this driver. As a result, this kind of driver is most appropriate on a
corporate network where client installations are not a major problem, or for
application server code written in Java in a three-tier architecture.
2. Native-API partly-Java driver: This kind of driver converts JDBC calls into
calls on the client API for Oracle, Sybase, Informix, DB2, or other DBMS.
Note that, like the bridge driver, this style of driver requires that some binary
code be loaded on each client machine.
3. JDBC-Net pure Java driver: This driver translates JDBC calls into a DBMS-
independent net protocol which is then translated to a DBMS protocol by a
server. This net server middleware is able to connect its pure Java clients to
many different databases. The specific protocol used depends on the vendor.
In general, this is the most flexible JDBC alternative. It is likely that all
vendors of this solution will provide products suitable for Intranet use. In order
for these products to also support Internet access, they must handle the
additional requirements for security, access through firewalls, and so on, that
the Web imposes. Several vendors are adding JDBC drivers to their existing
database middleware products.
4. Native-protocol pure Java driver: This kind of driver converts JDBC calls
into the network protcol used by DBMSs directly. This allows a direct call from
the client machine to the DBMS server and is a practical solution for Intranet
access. Since many of these protocols are proprietary, the database vendors
themselves will be the primary source, and several database vendors have
these in progress.
Eventually, we expect that driver categories 3 and 4 will be the preferred way to
access databases from JDBC. Driver categories 1 and 2 are interim solutions where
direct pure Java drivers are not yet available. There are possible variations on
categories 1 and 2 (not shown in the table below) that require a connector, but these
are generally less desirable solutions. Categories 3 and 4 offer all the advantages of
Java, including automatic installation (for example, downloading the JDBC driver with
an applet that uses it).
The following chart shows the four categories and their properties:
145
.
Obtaining JDBC Drivers
The first vendors with Category 3 drivers available were SCO, Open Horizon,
Visigenic, and WebLogic. JavaSoft and Intersolv, a leading database connectivity
vendor, worked together to produce the JDBC-ODBC Bridge and the JDBC Driver
Test Suite.
Connection
A Connection object represents a connection with a database. A connection session
includes the SQL statements that are executed and the results that are returned over
that connection. A single application can have one or more connections with a single
database, or it can have connections with many different databases.
Opening a Connection
The standard way to establish a connection with a database is to call the method
DriverManager.getConnection. This method takes a string containing a URL. The
DriverManager class, referred to as the JDBC management layer, attempts to locate
a driver than can connect to the database represented by that URL. The
DriverManager class maintains a list of registered Driver classes, and when the
method getConnection is called, it checks with each driver in the list until it finds one
that can connect to the database specified in the URL. The Driver method connect
uses this URL to actually establish the connection.
A user can bypass the JDBC management layer and call Driver methods directly.
This could be useful in the rare case that two drivers can connect to a database and
the user wants to explicitly select a particular driver. Normally, however, it is much
easier to just let the DriverManager class handle opening a connection.
The following code exemplifies opening a connection to a database located at the
URL "jdbc:odbc:wombat" with a user ID of "oboy" and "12Java" as the password :
String url = "jdbc:odbc:wombat";
Connection con = DriverManager.getConnection(url, "oboy", "12Java");
URLs in General Use
Since URLs often cause some confusion, we will first give a brief explanation of
URLs in general and then go on to a discussion of JDBC URLs.
A URL (Uniform Resource Locator) gives information for locating a resource on the
Internet. It can be thought of as an address.
The first part of a URL specifies the protocol used to access information, and it is
always followed by a colon. Some common protocols are "ftp", which specifies "file
transfer protocol," and "http," which specifies "hypertext transfer protocol." If the
protocol is "file," it indicates that the resource is in a local file system rather than on
the Internet.
ftp://javasoft.com/docs/JDK-1_apidocs.zip
http://java.sun.com/products/JDK/CurrentRelease
file:/home/haroldw/docs/tutorial.html
The rest of a URL, everything after the first colon, gives information about where the
data source is located. If the protocol is file, the rest of the URL is the path to a file.
For the protocols ftp and http, the rest of the URL identifies the host and may
optionally give a path to a more specific site. For example, below is the URL for the
JavaSoft home page. This URL identifies only the host:
http://www.javasoft.com
By navigating from this home page, one can go to many other pages, one of which is
the JDBC home page. The URL for the JDBC home page is more specific and looks
like this:
http://www.javasoft.com/products/jdbc
146
.
JDBC URLs
A JDBC URL provides a way of identifying a database so that the appropriate driver
will recognize it and establish a connection with it. Driver writers are the ones who
actually determine what the JDBC URL that identifies their particular driver will be.
Users do not need to worry about how to form a JDBC URL; they simply use the URL
supplied with the drivers they are using. JDBC's role is to recommend some
conventions for driver writers to follow in structuring their JDBC URLs.
Since JDBC URLs are used with various kinds of drivers, the conventions are of
necessity very flexible. First, they allow different drivers to use different schemes for
naming databases. The odbc subprotocol, for example, lets the URL contain attribute
values (but does not require them).
Second, JDBC URLs allow driver writers to encode all necessary connection
information within them. This makes it possible, for example, for an applet that wants
to talk to a given database to open the database connection without requiring the
user to do any system administration chores.
Third, JDBC URLs allow a level of indirection. This means that the JDBC URL may
refer to a logical host or database name that is dynamically translated to the actual
name by a network naming system. This allows system administrators to avoid
specifying particular hosts as part of the JDBC name. There are a number of different
network name services (such as DNS, NIS, and DCE), and there is no restriction
about which ones can be used.
The standard syntax for JDBC URLs is shown below. It has three parts, which are
separated by colons:
jdbc:<subprotocol>:<subname>
The three parts of a JDBC URL are broken down as follows:
5. jdbc-the protocol. The protocol in a JDBC URL is always jdbc.
6. <subprotocol>-the name of the driver or the name of a database connectivity
mechanism, which may be supported by one or more drivers. A prominent
example of a subprotocol name is "odbc", which has been reserved for URLs
that specify ODBC-style data source names. For example, to access a
database through a JDBC-ODBC bridge, one might use a URL such as the
following:
jdbc:odbc:fred
In this example, the subprotocol is "odbc", and the subname "fred" is a local
ODBC data source.
If one wants to use a network name service (so that the database
name in the JDBC URL does not have to be its actual name), the
naming service can be the subprotocol. So, for example, one
might have a URL like:
jdbc:dcenaming:accounts-payable
In this example, the URL specifies that the local DCE naming service should
resolve the database name "accounts-payable" into a more specific name
that can be used to connect to the real database.
147
.
address should be included in the JDBC URL as part of the subname and
should follow the standard URL naming convention of
//hostname:port/subsubname
jdbc:dbnet://wombat:356/fred
The "odbc" Subprotocol
The subprotocol odbc is a special case. It has been reserved for URLs that specify
ODBC-style data source names and has the special feature of allowing any number
of attribute values to be specified after the subname (the data source name). The full
syntax for the odbc subprotocol is:
jdbc:odbc:<data-source-name>[;<attribute-name>=<attribute-value>]*
Thus all of the following are valid jdbc:odbc names:
jdbc:odbc:qeor7
jdbc:odbc:wombat
jdbc:odbc:wombat;CacheSize=20;ExtensionCase=LOWER
jdbc:odbc:qeora;UID=kgh;PWD=fooey
Registering Subprotocols
A driver developer can reserve a name to be used as the subprotocol in a JDBC
URL. When the DriverManager class presents this name to its list of registered
drivers, the driver for which this name is reserved should recognize it and establish a
connection to the database it identifies. For example, odbc is reserved for the JDBC-
ODBC Bridge. If there were, for another example, a Miracle Corporation, it might
want to register "miracle" as the subprotocol for the JDBC driver that connects to its
Miracle DBMS so that no one else would use that name.
JavaSoft is acting as an informal registry for JDBC subprotocol names. To register a
subprotocol name, send email to:
jdbc@wombat.eng.sun.com
148
.
database when the statement is executed. Instances of PreparedStatement
extend Statement and therefore include Statement methods. A
PreparedStatement object has the potential to be more efficient than a
Statement object because it has been pre-compiled and stored for future use.
7. CallableStatement- -created by the method prepareCall. CallableStatement
objects are used to execute SQL stored procedures- -a group of SQL
statements that is called by name, much like invoking a function. A
CallableStatement object inherits methods for handling IN parameters from
PreparedStatement; it adds methods for handling OUT and INOUT
parameters.
The following list gives a quick way to determine which Connection method is
appropriate for creating different types of SQL statements:
createStatement method is used for
Transactions
A transaction consists of one or more statements that have been executed,
completed, and then either committed or rolled back. When the method commit or
rollback is called, the current transaction ends and another one begins.
A new connection is in auto-commit mode by default, meaning that when a statement
is completed, the method commit will be called on that statement automatically. In
this case, since each statement is committed individually, a transaction consists of
only one statement. If auto-commit mode has been disabled, a transaction will not
terminate until the method commit or rollback is called explicitly, so it will include all
the statements that have been executed since the last invocation of the commit or
rollback method. In this second case, all the statements in the transaction are
committed or rolled back as a group.
The method commit makes permanent any changes an SQL statement makes to a
database, and it also releases any locks held by the transaction. The method rollback
will discard those changes.
Sometimes a user doesn't want one change to take effect unless another one does
also. This can be accomplished by disabling auto-commit and grouping both updates
into one transaction. If both updates are successful, then the commit method is
called, making the effects of both updates permanent; if one fails or both fail, then the
rollback method is called, restoring the values that existed before the updates were
executed.
Most JDBC drivers will support transactions. In fact, a JDBC-compliant driver must
support transactions. DatabaseMetaData supplies information describing the level of
transaction support a DBMS provides.
Transaction Isolation Levels
If a DBMS supports transaction processing, it will have some way of managing
potential conflicts that can arise when two transactions are operating on a database
149
.
at the same time. A user can specify a transaction isolation level to indicate what
level of care the DBMS should exercise in resolving potential conflicts. For example,
what happens when one transaction changes a value and a second transaction
reads that value before the change has been committed or rolled back? Should that
be allowed, given that the changed value read by the second transaction will be
invalid if the first transaction is rolled back? A JDBC user can instruct the DBMS to
allow a value to be read before it has been committed ("dirty reads") with the
following code, where con is the current connection:
con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);
The higher the transaction isolation level, the more care is taken to avoid conflicts.
The Connection interface defines five levels, with the lowest specifying that
transactions are not supported at all and the highest specifying that while one
transaction is operating on a database, no other transactions may make any changes
to the data read by that transaction. Typically, the higher the level of isolation, the
slower the application executes (due to increased locking overhead and decreased
concurrency between users). The developer must balance the need for performance
with the need for data consistency when making a decision about what isolation level
to use. Of course, the level that can actually be supported depends on the
capabilities of the underlying DBMS.
When a new Connection object is created, its transaction isolation level depends on
the driver, but normally it is the default for the underlying database. A user may call
the method setIsolationLevel to change the transaction isolation level, and the new
level will be in effect for the rest of the connection session. To change the transaction
isolation level for just one transaction, one needs to set it before the transaction
begins and reset it after the transaction terminates. Changing the transaction
isolation level during a transaction is not recommended, for it will trigger an
immediate call to the method commit, causing any changes up to that point to be
made permanent.
DriverManager
The DriverManager class is the management layer of JDBC, working between the
user and the drivers. It keeps track of the drivers that are available and handles
establishing a connection between a database and the appropriate driver. In addition,
the DriverManager class attends to things like driver login time limits and the printing
of log and tracing messages.
For simple applications, the only method in this class that a general programmer
needs to use directly is DriverManager.getConnection. As its name implies, this
method establishes a connection to a database. JDBC allows the user to call the
DriverManager methods getDriver, getDrivers, and registerDriver as well as the
Driver method connect, but in most cases it is better to let the DriverManager class
manage the details of establishing a connection.
Keeping Track of Available Drivers
The DriverManager class maintains a list of Driver classes that have registered
themselves by calling the method DriverManager.registerDriver. All Driver classes
should be written with a static section that creates an instance of the class and then
registers it with the DriverManager class when it is loaded. Thus, a user would not
normally call DriverManager.registerDriver directly; it should be called automatically
by a driver when it is loaded. A Driver class is loaded, and therefore automatically
registered with the DriverManager, in two ways:
8. By calling the method Class.forName. This explicitly loads the driver class.
Since it does not depend on any external setup, this way of loading a driver is
recommended. The following code loads the class acme.db.Driver:
150
.
Class.forName("acme.db.Driver");
If acme.db.Driver has been written so that loading it causes an instance to be
created and also calls DriverManager.registerDriver with that instance as the
parameter (as it should do), then it is in the DriverManager's list of drivers and
available for creating a connection.
9. By adding the driver to the java.lang.System property jdbc.drivers. This is a
list of driver classnames, separated by colons, that the DriverManager class
loads. When the DriverManager class is intialized, it looks for the system
property jdbc.drivers, and if the user has entered one or more drivers, the
DriverManager class attempts to load them. The following code illustrates
how a programmer might enter three driver classes in ~/.hotjava/properties
(HotJava loads these into the system properties list on startup):
jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver;
The first call to a DriverManager method will automatically cause these driver classes
to be loaded.
Note that this second way of loading drivers requires a preset environment that is
persistent. If there is any doubt about that being the case, it is safer to call the
method Class.forName to explicitly load each driver. This is also the method to use to
bring in a particular driver since once the DriverManager class has been initialized, it
will never recheck the jdbc.drivers property list.
In both of the cases listed above, it is the responsibility of the newly-loaded Driver
class to register itself by calling DriverManager.registerDriver. As mentioned above,
this should be done automatically when the class is loaded.
For security reasons, the JDBC management layer will keep track of which class
loader provided which driver. Then when the DriverManager class is opening a
connection, it will use only drivers that come from the local file system or from the
same class loader as the code issuing the request for a connection.
Establishing a Connection
Once the Driver classes have been loaded and registered with the DriverManager
class, they are available for establishing a connection with a database. When a
request for a connection is made with a call to the DriverManager.getConnection
method, the DriverManager tests each driver in turn to see if it can establish a
connection.
It may sometimes be the case that more than one JDBC driver is capable of
connecting to a given URL. For example, when connecting to a given remote
database, it might be possible to use a JDBC-ODBC bridge driver, a JDBC-to-
generic-network-protocol driver, or a driver supplied by the database vendor. In such
cases, the order in which the drivers are tested is significant because the
DriverManager will use the first driver it finds that can successfully connect to the
given URL.
First the DriverManager tries to use each of the drivers in the order they were
registered. (The drivers listed in jdbc.drivers are always registered first.) It will skip
any drivers which are untrusted code, unless they have been loaded from the same
source as the code that is trying to open the connection.
It tests the drivers by calling the method Driver.connect on each one in turn, passing
them the URL that the user originally passed to the method
DriverManager.getConnection. The first driver that recognizes the URL makes the
connection.
At first glance this may seem inefficient, but it requires only a few procedure calls and
string comparisons per connection since it is unlikely that dozens of drivers will be
loaded concurrently.
The following code is an example of all that is normally needed to set up a
connection with a driver such as a JDBC-ODBC bridge driver:
151
.
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //loads the driver
String url = "jdbc:odbc:fred";
DriverManager.getConnection(url, "userID", "passwd");
Statement
A Statement object is used to send SQL statements to a database. There are actually
three kinds of Statement objects, all of which act as containers for executing SQL
statements on a given connection: Statement, PreparedStatement, which inherits
from Statement, and CallableStatement, which inherits from PreparedStatement.
They are specialized for sending particular types of SQL statements: a Statement
object is used to execute a simple SQL statement with no parameters; a
PreparedStatement object is used to execute a precompiled SQL statement with or
without IN parameters; and a CallableStatement object is used to execute a call to a
database stored procedure.
The Statement interface provides basic methods for executing statements and
retrieving results. The PreparedStatement interface adds methods for dealing with IN
parameters; CallableStatement adds methods for dealing with OUT parameters.
Creating Statement Objects
Once a connection to a particular database is established, that connection can be
used to send SQL statements. A Statement object is created with the Connection
method createStatement, as in the following code fragment:
Connection con = DriverManager.getConnection(url, "sunny", "");
Statement stmt = con.createStatement();
The SQL statement that will be sent to the database is supplied as the argument to
one of the methods for executing a Statement object:
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2);
Executing Statements Using Statement objects
The Statement interface provides three different methods for executing SQL
statements, executeQuery, executeUpdate, and execute. The one to use is
determined by what the SQL statement produces.
The method executeQuery is designed for statements that produce a single result
set, such as SELECT statements.
The method executeUpdate is used to execute INSERT, UPDATE, or DELETE
statements and also SQL DDL (Data Definition Language) statements like CREATE
TABLE and DROP TABLE. The effect of an INSERT, UPDATE, or DELETE
statement is a modification of one or more columns in zero or more rows in a table.
The return value of executeUpdate is an integer indicating the number of rows that
were affected (referred to as the update count). For statements such as CREATE
TABLE or DROP TABLE, which do not operate on rows, the return value of
executeUpdate is always zero.
The method execute is used to execute statements that return more than one result
set, more than one update count, or a combination of the two. Because it is an
advanced feature that most programmers will never need, it is explained in its own
section later in this overview.
All of the methods for executing statements close the calling Statement object's
current result set if there is one open. This means that one needs to complete any
processing of the current ResultSet object before re-executing a Statement object.
It should be noted that the PreparedStatement interface, which inherits all of the
methods in the Statement interface, has its own versions of the methods
executeQuery, executeUpdate and execute. Statement objects do not themselves
contain an SQL statement; therefore, one must be provided as the argument to the
Statement.execute methods. PreparedStatement objects do not supply an SQL
statement as a parameter to these methods because they already contain a
152
.
precompiled SQL statement. CallableStatement objects inherit the
PreparedStatement forms of these methods. Using a query parameter with the
PreparedStatement or CallableStatement versions of these methods will cause an
SQLException to be thrown.
Statement Completion
When a connection is in auto-commit mode, the statements being executed within it
are committed or rolled back when they are completed. A statement is considered
complete when it has been executed and all its results have been returned. For the
method executeQuery, which returns one result set, the statement is completed
when all the rows of the ResultSet object have been retrieved. For the method
executeUpdate, a statement is completed when it is executed. In the rare cases
where the method execute is called, however, a statement is not complete until all of
the result sets or update counts it generated have been retrieved.
Some DBMSs treat each statement in a stored procedure as a separate statement;
others treat the entire procedure as one compound statement. This difference
becomes important when auto-commit is enabled because it affects when the
method commit is called. In the first case, each statement is individually committed;
in the second, all are committed together.
Closing Statement Objects
Statement objects will be closed automatically by the Java garbage collector.
Nevertheless, it is recommended as good programming practice that they be closed
explicitly when they are no longer needed. This frees DBMS resources immediately
and helps avoid potential memory problems.
Using the Method execute
The execute method should be used only when it is possible that a statement may
return more than one ResultSet object, more than one update count, or a
combination of ResultSet objects and update counts. These multiple possibilities for
results, though rare, are possible when one is executing certain stored procedures or
dynamically executing an unknown SQL string (that is, unknown to the application
programmer at compile time). For example, a user might execute a stored procedure
and that stored procedure could perform an update, then a select, then an update,
then a select, and so on. Typically, someone using a stored procedure will know what
it returns.
Because the method execute handles the cases that are out of the ordinary, it is no
surprise that retrieving its results requires some special handling. For instance,
suppose it is known that a procedure returns two result sets. After using the method
execute to execute the procedure, one must call the method getResultSet to get the
first result set and then the appropriate getXXX methods to retrieve values from it. To
get the second result set, one needs to call getMoreResults and then getResultSet a
second time. If it is known that a procedure returns two update counts, the method
getUpdateCount is called first, followed by getMoreResults and a second call to
getUpdateCount.
Those cases where one does not know what will be returned present a more
complicated situation. The method execute returns true if the result is a ResultSet
object and false if it is a Java int. If it returns an int, that means that the result is either
an update count or that the statement executed was a DDL command. The first thing
to do after calling the method execute, is to call either getResultSet or
getUpdateCount. The method getResultSet is called to get what might be the first of
two or more ResultSet objects; the method getUpdateCount is called to get what
might be the first of two or more update counts.
When the result of an SQL statement is not a result set, the method getResultSet will
return null. This can mean that the result is an update count or that there are no more
153
.
results. The only way to find out what the null really means in this case is to call the
method getUpdateCount, which will return an integer. This integer will be the number
of rows affected by the calling statement or -1 to indicate either that the result is a
result set or that there are no results. If the method getResultSet has already
returned null, which means that the result is not a ResultSet object, then a return
value of -1 has to mean that there are no more results. In other words, there are no
results (or no more results) when the following is true:
((stmt.getResultSet() == null) && (stmt.getUpdateCount() == -1))
If one has called the method getResultSet and processed the ResultSet object it
returned, it is necessary to call the method getMoreResults to see if there is another
result set or update count. If getMoreResults returns true, then one needs to again
call getResultSet to actually retrieve the next result set. As already stated above, if
getResultSet returns null, one has to call getUpdateCount to find out whether null
means that the result is an update count or that there are no more results.
When getMoreResults returns false, it means that the SQL statement returned an
update count or that there are no more results. So one needs to call the method
getUpdateCount to find out which is the case. In this situation, there are no more
results when the following is true:
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
The code below demonstrates one way to be sure that one has accessed all the
result sets and update counts generated by a call to the method execute:
stmt.execute(queryStringWithUnknownResults);
while (true) {
int rowCount = stmt.getUpdateCount();
if (rowCount > 0) { // this is an update count
System.out.println("Rows changed = " + count);
stmt.getMoreResults();
continue;
}
if (rowCount == 0) { // DDL command or 0 updates
System.out.println(" No rows changed or statement was DDL
command");
stmt.getMoreResults();
continue;
}
ResultSet rs = stmt.getResultSet;
if (rs != null) {
. . . // use metadata to get info about result set columns
while (rs.next()) {
. . . // process results
stmt.getMoreResults();
continue;
}
break; // there are no more results
ResultSet
A ResultSet contains all of the rows which satisfied the conditions in an SQL
statement, and it provides access to the data in those rows through a set of get
methods that allow access to the various columns of the current row. The
154
.
ResultSet.next method is used to move to the next row of the ResultSet, making the
next row become the current row.
The general form of a result set is a table with column headings and the
corresponding values returned by a query. For example, if your query is SELECT a,
b, c FROM Table1, your result set will have the following form:
a b c
------ --------- -------
12345 Cupertino CA
83472 Redmond WA
83492 Boston MA
The following code fragment is an example of executing an SQL statement that will
return a collection of rows, with column 1 as an int, column 2 as a String, and column
3 as an array of bytes:
155
.
Either the column name or the column number can be used to designate the column
from which to retrieve data. For example, if the second column of a ResultSet object
rs is named "title" and stores values as strings, either of the following will retrieve the
value stored in that column:
String s = rs.getString("title");
String s = rs.getString(2);
Note that columns are numbered from left to right starting with column 1. Also,
column names used as input to getXXX methods are case insensitive.
The option of using the column name was provided so that a user who specifies
column names in a query can use those same names as the arguments to getXXX
methods. If, on the other hand, the select statement does not specify column names
(as in "select * from table1" or in cases where a column is derived), column numbers
should be used. In such situations, there is no way for the user to know for sure what
the column names are.
In some cases, it is possible for a SQL query to return a result set that has more than
one column with the same name. If a column name is used as the parameter to a
getXXX method, getXXX will return the value of the first matching column name.
Thus, if there are multi0ple columns with the same name, one needs to use a column
index to be sure that the correct column value is retrieved. It may also be slightly
more efficient to use column numbers.
Information about the columns in a ResultSet is available by calling the method
ResultSet.getMetaData. The ResultSetMetaData object returned gives the number,
types, and properties of its ResultSet object's columns.
If the name of a column is known, but not its index, the method findColumn can be
used to find the column number.
Data Types and Conversions
For the getXXX methods, the JDBC driver attempts to convert the underlying data to
the specified Java type and then returns a suitable Java value. For example, if the
getXXX method is getString, and the data type of the data in the underlying database
is VARCHAR, the JDBC driver will convert VARCHAR to Java String. The return
value of getString will be a Java String object.
The following table shows which JDBC types a getXXX method is allowed to retrieve
and which JDBC types (generic SQL types) are recommended for it to retrieve. A
small x indicates a legal getXXX method for a particular data type; a large X indicates
the recommended getXXX method for a data type. For example, any getXXX method
except getBytes or getBinaryStream can be used to retrieve the value of a
LONGVARCHAR, but getAsciiStream or getUnicodeStream are recommended,
depending on which data type is being returned. The method getObject will return
any data type as a Java Object and is useful when the underlying data type is a
database-specific abstract type or when a generic application needs to be able to
accept any data type.
Use of ResultSet.getXXX methods to retrieve common JDBC data types.
An "x" indicates that the getXXX method may legally be used to retrieve the given
JDBC type.
An "X" indicates that the getXXX method is recommended for retrieving the given
JDBC type.
T S I B R F D D N B C V L B V L D T T
I M N I E L O E U I H A O I A O A I I
N A T G A O U C M T A R N N R N T M M
Y L E L A B I E R C G A B G E E E
I L G N T L M R H V R I V S
156
.
N I E T E A I
A A Y N A T
T N R L C
R R A R A
T C R B M
H Y I P
A N
R A
R
Y
getByte X x x x x x x x x x x x x
getShort x X x x x x x x x x x x x
getInt x x X x x x x x x x x x x
getLong x x x X x x x x x x x x x
getFloat x x x x X x x x x x x x x
getDouble x x x x x X X x x x x x x
getBigDecimal x x x x x x x X X x x x x
getBoolean x x x x x x x x x X x x x
getString x x x x x x x x x x X X x x x x x x x
getBytes X X x
getDate x x x X x
getTime x x x X x
getTimestamp x x x x X
getAsciiStream x x X x x x
getUnicodeStream x x X x x x
getBinaryStream x x X
GetObject x x x x x x x x x x x x x x x x x x x
Using Streams for Very Large Row Values
ResultSet makes it possible to retrieve arbitrarily large LONGVARBINARY or
LONGVARCHAR data. The methods getBytes and getString return data as one large
chunk (up to the limits imposed by the return value of Statement.getMaxFieldSize).
However, it may be more convenient to retrieve very large data in smaller, fixed-size
chunks. This is done by having the ResultSet class return java.io.Input streams from
which data can be read in chunks. Note that these streams must be accessed
immediately because they will be closed automatically on the next getXXX call on
ResultSet. (This behavior is imposed by underlying implementation constraints on
large blob access.)
The JDBC API has three separate methods for getting streams, each with a different
return value:
• getBinaryStream returns a stream which simply provides the raw bytes from
the database without any conversion.
• getAsciiStream returns a stream which provides one-byte ASCII characters.
• getUnicodeStream returns a stream which provides two-byte Unicode
characters.
Note that this differs from Java streams, which return untyped bytes and can (for
example) be used for both ASCII and Unicode characters.
The following code gives an example of using getAsciiStream:
java.sql.Statement stmt = con.createStatement();
ResultSet r = stmt.executeQuery("SELECT x FROM Table2");
// Now retrieve the column 1 results in 4 K chunks:
byte buff = new byte[4096];
while (r.next()) {
157
.
Java.io.InputStream fin = r.getAsciiStream(1);
for (;;) {
int size = fin.read(buff);
if (size == -1) { // at end of stream
break;
}
// Send the newly-filled buffer to some ASCII output stream:
output.write(buff, 0, size);
}
}
NULL Result Values
To determine if a given result value is JDBC NULL, one must first read the column
and then use the ResultSet.wasNull method to discover if the read returned a JDBC
NULL.
When one has read a JDBC NULL using one of the ResultSet.getXXX methods, the
method wasNull will return one of the following:
• A Java null value for those getXXX methods that return Java objects
(methods such as getString, getBigDecimal, getBytes, getDate, getTime,
getTimestamp, getAsciiStream, getUnicodeStream, getBinaryStream,
getObject).
• A zero value for getByte, getShort, getInt, getLong, getFloat, and getDouble.
• A false value for getBoolean.
Optional or Multiple Result Sets
Normally SQL statements are executed using either executeQuery (which returns a
single ResultSet) or executeUpdate (which can be used for any kind of database
modification statement and which returns a count of the rows updated). However,
under some circumstances an application may not know whether a given statement
will return a result set until the statement has executed. In addition, some stored
procedures may return several different result sets and/or update counts.
To accommodate these situations, JDBC provides a mechanism so that an
application can execute a statement and then process an arbitrary collection of result
sets and update counts. This mechanism is based on first calling a fully general
execute method, and then calling three other methods, getResultSet,
getUpdateCount, and getMoreResults. These methods allow an application to
explore the statement results one at a time and to determine if a given result was a
ResultSet or an update count.
You do not need to do anything to close a ResultSet; it is automatically closed by the
Statement that generated it when that Statement is closed, is re-executed, or is used
to retrieve the next result from a sequence of multiple results.
PreparedStatement
The PreparedStatement interface inherits from Statement and differs from it in two
ways:
10. Instances of PreparedStatement contain an SQL statement that has already
been compiled. This is what makes a statement "prepared."
11. The SQL statement contained in a PreparedStatement object may have one
or more IN parameters. An IN parameter is a parameter whose value is not
specified when the SQL statement is created. Instead the statement has a
question mark ("?") as a placeholder for each IN parameter. A value for each
158
.
question mark must be supplied by the appropriate setXXX method before the
statement is executed.
Because PreparedStatement objects are precompiled, their execution can be faster
than that of Statement objects. Consequently, an SQL statement that is executed
many times is often created as a PreparedStatement object to increase efficiency.
Being a subclass of Statement, PreparedStatement inherits all the functionality of
Statement. In addition, it adds a whole set of methods which are needed for setting
the values to be sent to the database in place of the placeholders for IN parameters.
Also, the three methods execute, executeQuery, and executeUpdate are modified so
that they take no argument. The Statement forms of these methods (the forms that
take an SQL statement parameter) should never be used with a PreparedStatement
object.
Creating PreparedStatement Objects
The following code fragment, where con is a Connection object, creates a
PreparedStatement object containing an SQL statement with two placeholders for IN
parameters:
PreparedStatement pstmt = con.prepareStatement(
"UPDATE table4 SET m = ? WHERE x = ?");
The object pstmt now contains the statement "UPDATE table4 SET m = ? WHERE x
= ?", which has already been sent to the DBMS and been prepared for execution.
Passing IN Parameters
Before a PreparedStatement object is executed, the value of each ? parameter must
be set. This is done by calling a setXXX method, where XXX is the appropriate type
for the parameter. For example, if the parameter has a Java type of long, the method
to use is setLong. The first argument to the setXXX methods is the ordinal position of
the parameter to be set, and the second argument is the value to which the
parameter is to be set. For example, the following code sets the first parameter to
123456789 and the second parameter to 100000000:
pstmt.setLong(1, 123456789);
pstmt.setLong(2, 100000000);
Once a parameter value has been set for a given statement, it can be used for
multiple executions of that statement until it is cleared by a call to the method
clearParameters.
In the default mode for a connection (auto-commit enabled), each statement is
commited or rolled back automatically when it is completed.
The same PreparedStatement object may be executed multiple times if the
underlying database and driver will keep statements open after they have been
committed. Unless this is the case, however, there is no point in trying to improve
performance by using a PreparedStatement object in place of a Statement object.
Using pstmt, the PreparedStatement object created above, the following code
illustrates setting values for the two parameter placeholders and executing pstmt 10
times. As stated above, for this to work, the database must not close pstmt. In this
example, the first parameter is set to "Hi" and remains constant. The second
parameter is set to a different value each time around the for loop, starting with 0 and
ending with 9.
pstmt.setString(1, "Hi");
for (int i = 0; i < 10; i++) {
pstmt.setInt(2, i);
int rowCount = pstmt.executeUpdate();
}
159
.
Data Type Conformance on IN Parameters
The XXX in a setXXX method is a Java type. It is implicitly a JDBC type (a generic
SQL type) because the driver will map the Java type to its corresponding JDBC type
and send that JDBC type to the database. For example, the following code fragment
sets the second parameter of the PreparedStatement object pstmt to 44, with a Java
type of short:
pstmt.setShort(2, 44);
The driver will send 44 to the database as a JDBC SMALLINT, which is the standard
mapping from a Java short.
It is the programmer's responsibility to make sure that the Java type of each IN
parameter maps to a JDBC type that is compatible with the JDBC data type expected
by the database. Consider the case where the database expects a JDBC SMALLINT.
If the method setByte is used, the driver will send a JDBC TINYINT to the database.
This will probably work because many databases convert from one related type to
another, and generally a TINYINT can be used anywhere a SMALLINT is used.
However, for an application to work with the most databases possible, it is best to
use Java types that correspond to the exact JDBC types expected by the database. If
the expected JDBC type is SMALLINT, using setShort instead of setByte will make
an application more portable.
Using setObject
A programmer can explicitly convert an input parameter to a particular JDBC type by
using the method setObject. This method can take a third argument, which specifies
the target JDBC type. The driver will convert the Java Object to the specified JDBC
type before sending it to the database.
If no JDBC type is given, the driver will simply map the Java Object to its default
JDBC type and then send it to the database. This is similar to what happens with the
regular setXXX methods; in both cases, the driver maps the Java type of the value to
the appropriate JDBC type before sending it to the database. The difference is that
the setXXX methods use the standard mapping from Java types to JDBC types
whereas the setObject method uses the mapping from Java Object types to JDBC
types
The capability of the method setObject to accept any Java object allows an
application to be generic and accept input for a parameter at run time. In this
situation the type of the input is not known when the application is compiled. By using
setObject, the application can accept any Java object type as input and convert it to
the JDBC type expected by the database.
Sending JDBC NULL as an IN parameter
The setNull method allows a programmer to send a JDBC NULL value to the
database as an IN parameter. Note, however, that one must still specify the JDBC
type of the parameter.
A JDBC NULL will also be sent to the database when a Java null value is passed to a
setXXX method (if it takes Java objects as arguments). The method setObject,
however, can take a null value only if the JDBC type is specified.
Sending Very Large IN Parameters
The methods setBytes and setString are capable of sending unlimited amounts of
data. Sometimes, however, programmers prefer to pass in large blobs of data in
smaller chunks. This can be accomplished by setting an IN parameter to a Java input
stream. When the statement is executed, the JDBC driver will make repeated calls to
this input stream, reading its contents and transmitting those contents as the actual
parameter data.
160
.
JDBC provides three methods for setting IN parameters to input streams:
setBinaryStream for streams containing uninterpreted bytes, setAsciiStream for
streams containing ASCII characters, and setUnicodeStream for streams containing
Unicode characters. These methods take one more argument than the other setXXX
methods because the total length of the stream must be specified. This is necessary
because some databases need to know the total transfer size before any data is
sent.
The following code illustrates using a stream to send the contents of a file as an IN
parameter:
java.io.File file = new java.io.File("/tmp/data");
int fileLength = file.length();
java.io.InputStream fin = new java.io.FileInputStream(file);
java.sql.PreparedStatement pstmt = con.prepareStatement(
"UPDATE Table5 SET stuff = ? WHERE index = 4");
pstmt.setBinaryStream (1, fin, fileLength);
pstmt.executeUpdate();
When the statement executes, the input stream fin will get called repeatedly to
deliver up its data.
CallableStatement
A CallableStatement object provides a way to call stored procedures in a standard
way for all DBMSs. A stored procedure is stored in a database; the call to the stored
procedure is what a CallableStatement object contains. This call is written in an
escape syntax that may take one of two forms: one form with a result parameter, and
the other without one. A result parameter, a kind of OUT parameter, is the return
value for the stored procedure. Both forms may have a variable number of
parameters used for input (IN parameters), output (OUT parameters), or both
(INOUT parameters). A question mark serves as a placeholder for a parameter.
The syntax for invoking a stored procedure in JDBC is shown below. Note that the
square brackets indicate that what is between them is optional; they are not
themselves part of the syntax.
{call procedure_name[(?, ?, ...)]}
The syntax for a procedure that returns a result parameter is:
{? = call procedure_name[(?, ?, ...)]}
The syntax for a stored procedure with no parameters would look like this:
{call procedure_name}
Normally, anyone creating a CallableStatement object would already know that the
DBMS being used supports stored procedures and what those procedures are. If one
needed to check, however, various DatabaseMetaData methods will supply such
information. For instance, the method supportsStoredProcedures will return true if the
DBMS supports stored procedure calls, and the method getProcedures will return a
description of the stored procedures available.
CallableStatement inherits Statement methods, which deal with SQL statements in
general, and it also inherits PreparedStatement methods, which deal with IN
parameters. All of the methods defined in CallableStatement deal with OUT
parameters or the output aspect of INOUT parameters: registering the JDBC types
(generic SQL types) of the OUT parameters, retrieving values from them, or checking
whether a returned value was JDBC NULL.
Creating a CallableStatement Object
CallableStatement objects are created with the Connection method prepareCall. The
example below creates an instance of CallableStatement that contains a call to the
stored procedure getTestData, which has two arguments and no result parameter:
CallableStatement cstmt = con.prepareCall(
161
.
"{call getTestData(?, ?)}");
Whether the ? placeholders are IN, OUT, or INOUT parameters depends on the
stored procedure getTestData.
162
.
IN and OUT Parameters
Passing in any IN parameter values to a CallableStatement object is done using the
setXXX methods inherited from PreparedStatement. The type of the value being
passed in determines which setXXX method to use (setFloat to pass in a float value,
and so on).
If the stored procedure returns OUT parameters, the JDBC type of each OUT
parameter must be registered before the CallableStatement object can be executed.
(This is necessary because some DBMSs require the JDBC type.) Registering the
JDBC type is done with the method registerOutParameter. Then after the statement
has been executed, CallableStatement's getXXX methods retrieve the parameter
value. The correct getXXX method to use is the Java type that corresponds to the
JDBC type registered for that parameter. In other words, registerOutParameter uses
a JDBC type (so that it matches the JDBC type that the database will return), and
getXXX casts this to a Java type.
To illustrate, the following code registers the OUT parameters, executes the stored
procedure called by cstmt, and then retrieves the values returned in the OUT
parameters. The method getByte retrieves a Java byte from the first OUT parameter,
and getBigDecimal retrieves a BigDecimal object (with three digits after the decimal
point) from the second OUT parameter:
CallableStatement cstmt = con.prepareCall(
"{call getTestData(?, ?)}");
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 3);
cstmt.executeQuery();
byte x = cstmt.getByte(1);
java.math.BigDecimal n = cstmt.getBigDecimal(2, 3);
Unlike ResultSet, CallableStatement does not provide a special mechanism for
retrieving large OUT values incrementally.
INOUT Parameters
A parameter that supplies input as well as accepts output (an INOUT parameter)
requires a call to the appropriate setXXX method (inherited from PreparedStatement)
in addition to a call to the method registerOutParameter. The setXXX method sets a
parameter's value as an input parameter, and the method registerOutParameter
registers its JDBC type as an output parameter. The setXXX method provides a Java
value which the driver converts to a JDBC value before sending it to the database.
The JDBC type of this IN value and the JDBC type supplied to the method
registerOutParameter should be the same. Then to retrieve the output value, a
corresponding getXXX method is used. For example, a parameter whose Java type
is byte should use the method setByte to assign the input value, should supply a
TINYINT as the JDBC type to registerOutParameter, and should use getByte to
retrieve the output value.
The following example assumes that there is a stored procedure reviseTotal whose
only parameter is an INOUT parameter. The method setByte sets the parameter to
25, which the driver will send to the database as a JDBC TINYINT. Next
registerOutParameter registers the parameter as a JDBC TINYINT. After the stored
procedure is executed, a new JDBC TINYINT value is returned, and the method
getByte will retrieve this new value as a Java byte.
CallableStatement cstmt = con.prepareCall(
"{call reviseTotal(?)}");
cstmt.setByte(1, 25);
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.executeUpdate();
byte x = cstmt.getByte(1);
163
.
Retrieve OUT Parameters after Results
Because of limitations imposed by some DBMSs, it is recommended that for
maximum portability, all of the results generated by the execution of a
CallableStatement object should be retrieved before OUT parameters are retrieved
using CallableStatement.getXXX methods.
If a CallableStatement object returns multiple ResultSet objects (using a call to the
method execute), all of the results should be retrieved before OUT parameters are
retrieved. In this case, to be sure that all results have been accessed, the Statement
methods getResultSet, getUpdateCount, and getMoreResults need to be called until
there are no more results.
After this is done, values from OUT parameters can be retrieved using the
CallableStatement.getXXX methods.
Retrieving NULL Values as OUT Parameters
The value returned to an OUT parameter may be JDBC NULL. When this happens,
the JDBC NULL value will be converted so that the value returned by a getXXX
method will be null, 0, or false, depending on the getXXX method type. As with
ResultSet objects, the only way to know if a value of 0 or false was originally JDBC
NULL is to test it with the method wasNull, which returns true if the last value read by
a getXXX method was JDBC NULL and false otherwise.
Mapping SQL and Java Types
Since SQL data types and Java data types are not identical, there needs to be some
mechanism for reading and writing data between an application using Java types and
a database using SQL types.
To accomplish this, JDBC provides sets of getXXX and setXXX methods, the method
registerOutParameter, and the class Types.
This section brings together information about data types affecting various classes
and interfaces and puts all the tables showing the mappings between SQL types and
Java types in one place for easy reference.
Mapping SQL Data Types into Java
Unfortunately there are significant variations between the SQL types supported by
different database products. Even when different databases support SQL types with
the same semantics, they may give those types different names. For example, most
of the major databases support an SQL data type for large binary values, but Oracle
calls this type LONG RAW, Sybase calls it IMAGE, Informix calls it BYTE, and DB2
calls it LONG VARCHAR FOR BIT DATA.
Fortunately, JDBC programmers will normally not need to concern themselves with
the actual SQL type names used by a target database. Most of the time JDBC
programmers will be programming against existing database tables, and they need
not concern themselves with the exact SQL type names that were used to create
these tables.
JDBC defines a set of generic SQL type identifiers in the class java.sql.Types. These
types have been designed to represent the most commonly used SQL types. In
programming with the JDBC API, programmers will normally be able to use these
JDBC types to reference generic SQL types, without having to be concerned about
the exact SQL type name used by the target database. These JDBC types are fully
described in the next section.
The one major place where programmers may need to use SQL type names is in the
SQL CREATE TABLE statement when they are creating a new database table. In this
case programmers must take care to use SQL type names that are supported by
their target database. We recommend that you consult your database documentation
164
.
if you need exact definitions of the behavior of the various SQL types on a particular
database.
If you want to be able to write portable JDBC programs that can create tables on a
variety of different databases, you have two main choices. First, you can restrict
yourself to using only very widely accepted SQL type names such as INTEGER,
NUMERIC, or VARCHAR, which are likely to work for all databases.
Or second, you can use the java.sql.DatabaseMetaData.getTypeInfo method to
discover which SQL types are actually supported by a given database and select a
database-specific SQL type name that matches a given JDBC type.
JDBC defines a standard mapping from the JDBC database types to Java types. For
example, a JDBC INTEGER is normally mapped to a Java int. This supports a simple
interface for reading and writing JDBC values as simple Java types.
The Java types do not need to be exactly isomorphic to the JDBC types; they just
need to be able to represent them with enough type information to correctly store and
retrieve parameters and recover results from SQL statements. For example, a Java
String object does not precisely match any of the JDBC CHAR types, but it gives
enough type information to represent CHAR, VARCHAR, or LONGVARCHAR
successfully.
Examples of Mapping
In any situation where a Java program retrieves data from a database, there has to
be some form of mapping and data conversion. In most cases, JDBC programmers
will be programming with knowledge of their target database's schema. They would
know, for example, what tables the database contains and the data type for each
column in those tables. They can therefore use the strongly-typed access methods in
the interfaces ResultSet, PreparedStatement, and CallableStatement. This section
presents three different scenarios, describing the data mapping and conversion
required in each.
Simple SQL Statement
In the most common case, a user executes a simple SQL statement and gets back a
ResultSet object with the results. The value returned by the database and stored in a
ResultSet column will have a JDBC data type. A call to a ResultSet.getXXX method
will retrieve that value as a Java data type. For example, if a ResultSet column
contains a JDBC FLOAT value, the method getDouble will retrieve that value as a
Java double. The getXXX methods may be used to retrieve which JDBC types. (A
user who does not know the type of a ResultSet column can get that information by
calling the method ResultSet.getMetaData and then invoking the ResultSetMetaData
methods getColumnType or getColumnTypeName.) The following code fragment
demonstrates getting the column type names for the columns in a result set:
165
.
driver will convert 2345678 to a JDBC BIGINT in order to send it to the database.
Which JDBC type the driver sends to the database is determined by the standard
mapping from Java types to JDBC types.
SQL Statement with INOUT Parameters
In yet another scenario, a user wants to call a stored procedure, assign values to its
INOUT parameters, retrieve values from the results, and retrieve values from the
parameters. This case is rather uncommon and more complicated than most, but it
gives a good illustration of mapping and data conversion.
In this scenario, the first thing to do is to assign values to the INOUT parameters
using PreparedStatement.setXXX methods. In addition, since the parameters will
also be used for output, the programmer must register each parameter with the
JDBC type of the value that the database will return to it. This is done with the
method CallableStatement.registerOutParameter, which takes one of the JDBC types
defined in the class Types. A programmer retrieves the results returned to a
ResultSet object with ResultSet.getXXX methods and retrieves the values stored in
the output parameters with CallableStatement.getXXX methods.
The XXX type used for ResultSet.getXXX methods is fairly flexible in some cases.
The XXX type used for CallableStatement.getXXX must map to the JDBC type
registered for that parameter. For example, if the database is expected to return an
output value whose type is JDBC REAL, the parameter should have been registered
as java.sql.Types.REAL. Then to retrieve the JDBC REAL value, the method
CallableStatement.getFloat should be called. The method getFloat will return the
value stored in the output parameter after converting it from a JDBC REAL to a Java
float. To accommodate various databases and make an application more portable, it
is recommended that values be retrieved from ResultSet objects before values are
retrieved from output parameters.
The following code demonstrates calling a stored procedure named getTestData,
which has two parameters that are both INOUT parameters. First the Connection
object con creates the CallableStatement object cstmt. Then the method setByte sets
the first parameter to 25 as a Java byte. The driver will convert 25 to a JDBC
TINYINT and send it to the database. The method setBigDecimal sets the second
parameter with an input value of 83.75. The driver will convert this
java.math.BigDecimal object to a JDBC NUMERIC value. Next the two parameters
are registered as OUT parameters, the first parameter as a JDBC TINYINT and the
second parameter as a JDBC DECIMAL with two digits after the decimal point. After
cstmt is executed, the values are retrieved from the ResultSet object using
ResultSet.getXXX methods. The method getString gets the value in the first column
as a Java String object, getInt gets the value in the second column as a Java int, and
getInt gets the value in the third column as a Java int.
Then CallableStatement.getXXX methods retrieve the values stored in the output
parameters. The method getByte retrieves the JDBC TINYINT as a Java byte, and
getBigDecimal retrieves the JDBC DECIMAL as a java.math.BigDecimal object with
two digits after the decimal point. Note that when a parameter is both an input and an
output parameter, the setXXX method uses the same Java type as the getXXX
method (as in setByte and getByte). The registerOutParameter method registers it to
the JDBC type that is mapped from the Java type
CallableStatement cstmt = con.prepareCall(
"{call getTestData(?, ?)}");
cstmt.setByte(1, 25);
cstmt.setBigDecimal(2, 83.75);
// register the first parameter as a JDBC TINYINT and the second
//parameter as a JDBC DECIMAL with two digits after the decimal point
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
166
.
cstmt.registerOutParameter(2, java.sql.Types.DECIMAL, 2);
ResultSet rs = cstmt.executeUpdate();
// retrieve and print values in result set
while(rs.next()) {
String name = rs.getString(1);
int score = rs.getInt(2);
int percentile = rs.getInt(3);
System.out.print("name = " + name + ", score = " + score + ", "
System.out.println("percentile = " + percentile);
// retrieve values in output parameters
byte x = cstmt.getByte(1);
java.math.BigDecimal n = cstmt.getBigDecimal(2, 2);
To generalize, the XXX in CallableStatement.getXXX and
PreparedStatement.setXXX methods is a Java type. For setXXX methods, the driver
converts the Java type to a JDBC type before sending it to the database. For getXXX
methods, the driver converts the JDBC type returned by the database to a Java type
before returning it to the getXXX method.
The method registerOutParameter always takes a JDBC type as an argument, and
the method setObject may take a JDBC type as an argument.
Note that if a JDBC type is supplied in its optional third argument, the method
setObject will cause an explicit conversion of the parameter value from a Java type to
the JDBC type specified. If no target JDBC type is supplied to setObject, the
parameter value will be converted to the JDBC type that is the standard mapping
from the Java. The driver will perform the explicit or implicit conversion before
sending the parameter to the database.
Dynamic Data Access
In most cases, the user wants to access results or parameters whose data types are
known at compile time. However, some applications, such as generic browsers or
query tools, are compiled with no knowledge of the database schema they will
access. For this reason, JDBC provides support for fully dynamically-typed data
access in addition to static data type access.
Three methods and one constant facilitate accessing values whose data types are
not known at compile time:
• ResultSet.getObject
• PreparedStatement.setObject
• CallableStatement.getObject
• java.sql.Types.OTHER (used as an argument to
CallableStatement.registerOutParameter)
If, for example, an application wants to be able to accept a variety of types as results
in a ResultSet object, it can use the method ResultSet.getObject.
The methods ResultSet.getObject and CallableStatement.getObject retrieve a value
as a Java Object. Since Object is the base class for all Java objects, an instance of
any Java class can be retrieved as an instance of Object. However, the following
Java types are built-in "primitive" types and are therefore not instances of the class
Object: boolean, char, byte, short, int, long, float, and double. As a result, these types
cannot be retrieved by getObject methods. However, each of these primitive types
has a corresponding class that serves as a wrapper. Instances of these classes are
objects, which means that they can be retrieved with the methods
ResultSet.getObject and CallableStatement.getObject
The method getObject can also be used to retrieve user-defined Java types. With the
advent of abstract data types (ADTs) or other user-defined types in some database
167
.
systems, some vendors may find it convenient to use getObject for retrieving these
types.
Tables for Data Type Mapping
Java type
JDBC type
CHAR String
VARCHAR String
LONGVARCHAR String
NUMERIC java.math.BigDecimal
DECIMAL java.math.BigDecimal
BIT boolean
TINYINT byte
SMALLINT short
INTEGER int
BIGINT long
REAL float
FLOAT double
DOUBLE double
BINARY byte[]
VARBINARY byte[]
LONGVARBINARY byte[]
DATE java.sql.Date
TIME java.sql.Time
TIMESTAMP java.sql.Timestamp
The mapping for String will normally be VARCHAR but will turn into LONGVARCHAR
if the given value exceeds the driver's limit on VARCHAR values. The same is true
for byte[] and VARBINARY and LONGVARBINARY values.
JDBC Types Mapped to Java Object Types
Since the Java built-in types such as boolean and int are not subtypes of Object,
there is a slightly different mapping from JDBC types to Java object types for the
getObject/setObject methods. This mapping is shown in the following table:
168
.
JDBC Type Java Object Type
CHAR String
VARCHAR String
LONGVARCHAR String
NUMERIC java.math.BigDecimal
DECIMAL java.math.BigDecimal
BIT Boolean
TINYINT Integer
SMALLINT Integer
INTEGER Integer
BIGINT Long
REAL Float
FLOAT Double
DOUBLE Double
BINARY byte[]
VARBINARY byte[]
LONGVARBINARY byte[]
DATE java.sql.Date
TIME java.sql.Time
TIMESTAMP java.sql.Timestamp
Java Object Types Mapped to JDBC Types
Java Object Type JDBC Type
String VARCHAR or LONGVARCHAR
java.math.BigDecimal NUMERIC
Boolean BIT
Integer INTEGER
Long BIGINT
Float REAL
Double DOUBLE
byte[] VARBINARY or LONGVARBINARY
java.sql.Date DATE
java.sql.Time TIME
java.sql.Timestamp TIMESTAMP
Note that the mapping for String will normaly be VARCHAR but will turn into
LONGVARCHAR if the given value exceeds the driver's limit on VARCHAR values.
The case is similar for byte[] and VARBINARY and LONGVARBINARY values.
Conversions by setObject
The method setObject converts Java object types to JDBC types.
T S I B R F D D N B C V L B V L D T T
169
.
I M N I E L O E U I H A O I A O A I I
N A T G A O U C M T A R N N R N T M M
Y L E I L A B I E R C G A B G E E E
I L G N T L M R H V R I V S
N I E T E A I A A Y N A T
T N R L C R R A R A
T C R B M
H Y I P
A N
R A
R
Y
String x x x x x x x x x x x x x x x x x x x
java.math.BigDecimal x x x x x x x x x x x x x
Boolean x x x x x x x x x x x x x
Integer x x x x x x x x x x x x x
Long x x x x x x x x x x x x x
Float x x x x x x x x x x x x x
Double x x x x x x x x x x x x x
byte[] x x x
java.sql.Date x x x x x
java.sql.Time x x x x
java.sql.Time- stamp x x x x x x
170
.
JDBC Types Retrieved by ResultSet.getXXX Methods
An "x" means that the method can retrieve the JDBC type. An "X" means that the
method is recommended for the JDBC type.
T S I B R F D D
N B C V L B V L D T T
I M N I E L O E
U I H A O I A O A I I
N A T G A O U C
M T A R N N R N T M M
Y L E I L E A B I
R C G A B G E E E
I L G N R T L M
H V R I V S
N I E T I E A
A A Y N A T
T N R C R R L A R A
T C R B M
H Y I P
A N
R A
R
Y
getByte X x x x x x x x x x x x x
getShort x X x x x x x x x x x x x
getInt x x X x x x x x x x x x x
getLong x x x X x x x x x x x x x
getFloat x x x x X x x x x x x x x
getDouble x x x x x X X x x x x x x
getBigDecimal x x x x x x x X X x x x x
getBoolean x x x x x x x x x X x x x
getString x x x x x x x x x x X X x x x x x x x
getBytes X X x
getDate x x x X x
getTime x x x X x
getTimestamp x x x x X
getAsciiStream x x X x x x
getUnicodeStream x x X x x x
getBinaryStream x x X
getObject x x x x x x x x x x x x x x x x x x x
171
.
Sample Code
// The following code can be used as a template. Simply
// substitute the appropriate url, login, and password, and
then substitute //the
// SQL statement you want to send to the database.
//----------------------------------------------------------
------------------
//
// Module: SimpleSelect.java
//
// Description:
//Test program for ODBC API interface.
//This java application will connect to
//a JDBC driver,
//issue a select statement
// and display all result columns and rows
//----------------------------------------------------------
------------------
import java.net.URL;
import java.sql.*;
class SimpleSelect
{
public static void main (String args[])
{
String url = "jdbc:odbc:my-dsn";
String query = "SELECT * FROM emp";
try
{
// Load the jdbc-odbc bridge driver
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
DriverManager.setLogStream(System.out);
// Attempt to connect to a driver. Each one
// of the registered drivers will be loaded until
// one is found that can process this URL
Connection con = DriverManager.getConnection (
url, "my-user", "my-passwd");
// If we were unable to connect, an exception
// would have been thrown. So, if we get here,
// we are successfully connected to the URL
// Check for, and display and warnings generated
// by the connect.
172
.
System.out.println("");
dispResultSet (rs);
rs.close();
stmt.close();
con.close();
}
catch (SQLException ex)
{
// A SQLException was generated. Catch it and
// display the error information. Note that there
// could be multiple error objects chained
// together
System.out.println ("\n*** SQLException caught ***\n");
while (ex != null)
{
System.out.println ("SQLState: " +ex.getSQLState ());
System.out.println ("Message: " + ex.getMessage ());
System.out.println ("Vendor: " +ex.getErrorCode ());
ex = ex.getNextException ();
System.out.println ("");
}
}
catch (java.lang.Exception ex)
{
// Got some other type of exception. Dump it.
ex.printStackTrace ();
}
}
//----------------------------------------------------------
---------
// checkForWarning
// Checks for and displays warnings. Returns true if a
warning
173
.
// existed
//----------------------------------------------------------
---------
if (warn != null)
{
System.out.println ("\n *** Warning ***\n");
rc = true;
while (warn != null)
{
System.out.println ("SQLState: "
+warn.getSQLState ());
System.out.println ("Message: "
+warn.getMessage ());
System.out.println ("Vendor: "
+warn.getErrorCode ());
System.out.println ("");
warn = warn.getNextWarning ();
}
}
return rc;
}
//----------------------------------------------------------
---------
// dispResultSet
// Displays all columns and rows in the given result set
//----------------------------------------------------------
---------
174
.
{
if (i > 1) System.out.print(",");
System.out.print(rsmd.getColumnLabel(i));
}
System.out.println("");
175
.
named libodbcinst.so.1 and libodbc.so.1, so symbolic links for these names must be
created.
Using the Bridge
The Bridge is used by opening a JDBC connection using a URL with the odbc
subprotocol. See below for URL examples. Before a connection can be established,
the bridge driver class, sun.jdbc.odbc.JdbcOdbcDriver, must either be added to the
java.lang.System property named jdbc.drivers, or it must be explicitly loaded using
the Java class loader. Explicit loading is done with the following line of code:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
When loaded, the ODBC driver (like all good JDBC drivers) creates an instance of
itself and registers this with the JDBC driver manager.
Using the Bridge from an Applet
JDBC used with a Pure Java JDBC driver works well with applets. The Bridge driver
does not work well with applets.
Most Browsers Do Not Support the Bridge
Since the Bridge is an optional component of the JDK, it may not be provided by a
browser. Even if it is provided, only trusted applets (those allowed to write to files) will
be able to use the Bridge. This is required in order to preserve the security of the
applet sandbox. Finally, even if the applet is trusted, ODBC and the DBMS client
library must be configured on each client.
Tested Configurations
From Solaris, we have used the Bridge to access Oracle 7.1.6 and Sybase Version
10 running on Solaris. From NT, we have used the Bridge to access SQL Server 6.x.
ODBC Drivers Known to Work with the Bridge
Visigenic provides ODBC drivers which have been tested with the the Bridge. Drivers
are available for Oracle, Sybase, Informix, Microsoft SQL Server, and Ingres. To
purchase the ODBC DriverSet 2.0, please contact Visigenic sales at 415-312-7197,
or visit the web site www.visigenic.com. The INTERSOLV ODBC driver suite should
be completely compatible with the JDBC-ODBC Bridge. The following drivers have
successfully passed a minimal test suite: Oracle, xBASE, Sybase (Windows NT/95
only), Microsoft SQL-Server, and Informix. To evaluate or purchase INTERSOLV
ODBC drivers, please contact INTERSOLV DataDirect Sales at 1- 800-547-4000
Option 2 or via the World Wide Web at http:\\www.intersolv.com. The MS SQL Server
driver has also been used successfully on NT. Many other ODBC drivers will likely
work.
ODBC Driver Incompatibilities
On Solaris, we have found that the Sybase ctlib-based drivers don't work because
ctlib has a signal-handling conflict with the Java VM. This is likely not a problem on
NT due to differences in the NT Java VM; however, this has not been verified. Some
ODBC drivers only allow a single result set to be active per connection.
0
What Is the JDBC URL Supported by the Bridge?
The Bridge driver uses the odbc subprotocol. URLs for this subprotocol are of the
form:
jdbc:odbc:<data-source-name>[<attribute-name>=<attribute-value>]*
For example:
jdbc:odbc:sybase
jdbc:odbc:mydb;UID=me;PWD=secret
jdbc:odbc:ora123;Cachesize=300
176
.
Debugging
The Bridge provides extensive tracing when DriverManager tracing is enabled. The
following line of code enables tracing and sends it to standard out:
java.sql.DriverManager.setLogStream(java.lang.System.out);
General Notes
The Bridge assumes that ODBC drivers are not reentrant. This means the Bridge
must synchronize access to these drivers. The result is that the Bridge provides
limited concurrency. This is a limitation of the Bridge. Most Pure Java JDBC drivers
provide the expected level of concurrent access.
177