Basic Java

  • November 2019
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Basic Java as PDF for free.

More details

  • Words: 49,637
  • Pages: 171
.

Basic Java

Basic Java

1

.

Table of Contents

1. Introduction to Java ……………..…………………………………………….3 2. The language package ……………………………………................29 3. The Utilities package ……………………………………….................36 4. The I/O Package ……………………………………………………………….48 5. Applet Programming …………………………………………...............68 6. Multithreading ………………………………………………………………….109 7. Networking in Java …………………………………………………………..120 8. Java Database Connectivity ……………………………………………131

Basic Java

2

.

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 is a simple, distributed, interpreted, secure, architecturally neutral, portable, high-performance, multithreaded and dynamic language. But what exactly makes Java unique is that it is the first programming language that can be used for writing general-purpose applications, as well as programs designed for the Internet. Java Buzzwords: SIMPLICITY

Java was designed to be a powerful language but simple. To support the development of large software, the concept of package is used. The major difference was the removal of the direct use of pointers. Java automatically handles referencing and dereferencing of language objects. Other difference includes the removal of support for data structures like struct, union. Its in-built classes provide this. Also, the concepts of operator overloading and multiple-inheritance in the form of classes have been removed. 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

Java provides the network capabilities by a pre-defined package java.net. This package has many classes that simplify the network communication. Accessing to any remote object is also possible in Java via the java.rmi package. 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 bytecode verification to ensure that the program is not violating the system integrity. Also,

Basic Java

3

. 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. MULTITHREADED LANGUAGE

Java supports multitasking in the form of multithreading within itself. First Java Application HelloWorld Application

public class HelloWorld { public static void main(String args[]) { System.out.println("Hello World!!"); } } Create the file

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. Compile the code

Type Prompt> javac HelloWorld.java 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. Run the program

Now that you have compiled the program, you can run it by typing at the command prompt: Promtpt> java HelloWorld

The input to the interpreter is nothing but the name of the class that has the main method. After you do this, the computer should print to the screen 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: Basic Java

4

. public class HelloWorld { This declares a class called HelloWorld. To create any class, simply write a line that looks like: public class ClassName 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. public static void main(String args[]){ This line declares what is known as the main method. Methods are essentially miniprograms. 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. Writing to the Screen

The text Hello World!! appears on the screen through 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. The System.out.println serves approximately the same purpose as the writeln in Pascal. In C, the function is printf, and in C++, cout. println Versus print

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.

Basic Java

5

. 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

Basic Java

6

. programs, once you begin to use threads in your programs, this may become a serious problem. 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); } } public class ObjectWorld { public static void main(String args[]) { PrintWorld p_world = new PrintWorld("Hello World"); p_world.printMe(); } } In the above program, PrintWorld p_world = new PrintWorld("Hello World"); 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. Getting information from the user with System.in

System.out has a convenient partner called System.in. While System.out is used to print information to the screen, System.in is used to get information into the program. Requesting input from the user

Let's use System.in.read() to get a character from the user.

Basic Java

7

. ReadHello.java

public class ReadHello { 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 The code we are most interested in is the line, which reads: 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 System.out.println("You entered "+ System.in.read()); 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. Converting integer to character 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.

Basic Java

8

. --inChar =(char) System.in.read(); --Notice the characters before System.in.read().The (char) causes the integer to be changed into a character. The Rest of the Extra Code—try, catch

In this code, there is a sequence there called a try-catch block. In some programming languages, when a problem occurs during execution, there is no way for you as a programmer to catch it and deal with the problem. In some languages, it's a bit complicated. In Java, most problems cause what are known as Exceptions. 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. JAVA LANGUAGE FUNDAMENTALS KEYWORDS

The following is a list of the 56 keywords you can use in Java. abstract case class do final future implements int new package rest super throw var

boolean cast const double finally generic import interface null private return switch throws void

Break Catch Continue Else Float Goto Inner Long Operator Protected Short Synchronized Transient Volatile

Byte Char Default Extends for if instanceof native outer public static this try while

EXTENDING OBJECTS THROUGH INHERITANCE 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. A Simple Inheritance Program

class BaseClass { public BaseClass() Basic Java

9

. { System.out.println("Base Class Constructor Called"); } } /* DerivedClass extends or inherits the property of the BaseClass */ class DerivedClass extends BaseClass { public DerivedClass() { System.out.println("Derived Class Constructed"); } } public class Inheritance { public static void main(String args[]) { BaseClass base = new BaseClass(); System.out.println("------------"); DerivedClass derived = new DerivedClass(); } } The output is: Base Class Constructor Called -----------Base Class Constructor Called 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

public interface Product { public int getPrice(int id);

Basic Java

10

. } public class Shoe implements Product { public int getPrice(int id) { if (id == 1) return(5); else return(10); } } public class Store { public static void main(String argv[]) { Shoe some = new Shoe(); int x = Some.getPrice(3); System.out.println(“the price : “+x); } } The Declaration

Interface declarations have the syntax public interface NameofInterface 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.

Extending Other Interfaces

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. e.g public interface NameOfInterface extends AnotherInterface 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 Interface Body

Basic Java

11

. 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 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

Method declarations in interfaces have the following syntax: public return_value nameofmethod (parameters); 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

Although interfaces are generally employed to provide abstract implementation of methods, you may also define variables within them. Because you cannot place any code within the bodies of the methods, all variables declared in an interface must be global to the class. Furthermore, regardless of the modifiers used when declaring the field, all fields declared in an interface are always public, final, and static. 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

Declaring a method in an interface is a good practice. However, the method cannot be used until a class implements the interface and overrides the given method. 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

Basic Java

12

. 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 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 Example of Function Overload

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); } } public class Overload { public static void main(String args[]) { Sample samp = new Sample(); System.out.println("-------------"); samp.overloadMe(); System.out.println("-------------"); samp.overloadMe("Hi! I am not the old one"); } } Output:

Sample Constructor Called ------------Overload Method Invoked ------------Hi! I am not the old one Here, though the method overloadMe is the same, it throws different ouput based on its invocation. This is termed as method overloading.

JAVA DATA TYPES

Java has Two Types of Data Types In Java, there are really two different categories in which data types have been divided: Primitive types Reference types

Reference types enclose things such as arrays, classes, and interfaces. Java has eight primitive types, each with its own purpose and use:

Basic Java

13

. Type Description • boolean - These have values of either true or false. •

byte - 8-bit 2s-compliment integer with values between -128 to 127



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: •

State the data type that you will be using



State the name the variable will be called



Assign the variable a value



As with every other line of code in Java, terminate the line with a semicolon

example: int number = 0; boolean value = false;

Identifiers—The Naming of a Variable

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 casesensitive and language-sensitive. Examples of Legal Identifiers

HelloWorld

Basic Java

14

. counter HotJava$ ioc_Queue3 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. UNARY LOGICAL OPERATORS Description

Operator

Increment Decrement Negation Bitwise complement

++ -~

Example for Increment and Decrement Operators

class IncDec { public static void main (String args[]) { int x = 8, y = 13; System.out.println(“x = “ + x); System.out.println(“y = “ + y); System.out.println(“++x = “ + ++x); System.out.println(“y++ = “ + y++); System.out.println(“x = “ + x); System.out.println(“y = “ + y); } } Output x=8 y = 13 ++x = 9 y++ = 13 x=9 y = 14 Example for negation operator

class Negation { public static void main (String args[]) { int x = 8; System.out.println(“x = “ + x); int y = -x; System.out.println(“y = “ + y);

Basic Java

15

. } }

Basic Java

16

. Example for Bitwise complement operator

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; Example for Shift Operators

class Shift { public static void main int x = 7; System.out.println(“x System.out.println(“x System.out.println(“x System.out.println(“x } }

(String args[]) { = “ + x); >> 2 = “ + (x >> 2)); << 1 = “ + (x << 1)); >>> 1 = “ + (x >>> 1));

The output of Shift follows: x=7 x >> 2 = 1 x << 1 = 14 Basic Java

17

. x >>> 1 = 3

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">

& | ^ && ||

Basic Java

18

. Negation Equal To Not Equal To Conditional

! == != ?:

CONDITIONAL OPERATOR

It takes the following form: expression1 ? expression2 : expression3 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. Example for Conditional Operator

class Conditional { public static void main (String args[]) { int x = 0; boolean isEven = false; System.out.println(“x = “ + x); x = isEven ? 4 : 7; System.out.println(“x = “ + x); } } The results of the Conditional program follow: x=0 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

Programmers use iteration statements to control sequences of statements that are repeated according to runtime conditions.

Basic Java

19

. Java supports five types of iteration statements: while do for continue break WHILE STATEMENTS

while (expression) statement; DO WHILE LOOP

do statement; while (expression) FOR LOOP

for (initialization, expression , step ) statement; SWITCH STATEMENTS

switch (expression){ case V1: statement1; break; case V2: statement2; break; default: statementD; } 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.

Basic Java

20

. int IQ[] = {123,109,156,142,131}; The next line shows an example of accessing the IQ of the third individual: int ThirdPerson = IQ[3]; Arrays in Java are somewhat tricky. This is mostly because, unlike most other languages, there are really three steps to filling out an array, rather than one: 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; Examples of declaring arrays.

long Primes[] = new long[1000000]; // declare an array and assign // some memory to hold it. long[] EvenPrimes = new long[1]; // Either way, it's an array. EvenPrimes[0] = 2; // populate the array. 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

Java supports three styles of 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 (*/). C++ Style Comments

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

Basic Java

21

. 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; long GrainOfSandOnTheBeachNum=1L; short Mask1=0x007f; String FirstName = "Ernest"; char TibetanNine = '\u1049' boolean UniverseWillExpandForever = true; ESCAPE CHARACTERS

Escape Literal Meaning '\b' \u0008 backspace '\t' \u0009 horizontal tab '\n' \u000a linefeed '\f' \u000c form feed '\r' \u000d carriage return '\"' \u0022 double quote '\'' \u0027 single quote '\\' \u005c backslash Don't use the \u format to express an end-of-line character. Use the \n or \r characters instead. ERROR-HANDLING CLASSES

Runtime error handling is a very important facility in any programming environment. Java provides the following classes for dealing with runtime errors: • Throwable • Exception • Error The Throwable class provides low-level error-handling capabilities such as an execution stack list. The Exception class is derived from Throwable and provides the base level of functionality for all the exception classes defined in the Java system. The Exception class is used for handling normal errors. The Error class is also derived from Throwable, but it is used for handling abnormal errors that aren’t expected to occur. Very few Java programs worry with the Error class; most use the Exception class to handle runtime errors.

Basic Java

22

. Example for Exception Handling

public class ExceptionHandling { public static void main(String args[]) { int values[] = {5,6,3,5,2}; int index = 6; try { int get = values[index]; System.out.println("The value in the requested index is " +get); } catch(Exception err) { System.out.println("Requested Index Not found"); } finally { System.out.println("--------End---------"); } } } 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. Use of finally clause

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

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 EXCEPTION CLASS

The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch.

EXAMPLE FOR MULTIPLE EXCEPTION HANDLING

public class MultipleExceptions { public static void main(String args[]) {

Basic Java

23

. int values[] = {5,6,2,3,5}; int index; char input = (char)-1; String data = ""; System.out.println("Enter an index value"); try { do { input = (char)System.in.read(); data = data + input; }while(input!='\n'); } catch(Exception err) { System.out.println("Unable to obtain system input"); } try { index = Integer.parseInt(data.trim()); System.out.println("The value in the requested index : "+values[index]); } catch(NumberFormatException err) { System.out.println("Invalid Index"); } catch(ArrayIndexOutOfBoundsException err) { System.out.println("Requested Index Not Found"); } finally { System.out.println("--------End---------"); } } } In the above program, there is a pre-defined array of length 5. The user input is got as a String. It is then parsed into an int data type. The value in the array for this index is given as ouput. Here, the exceptions may be thrown • • •

While getting an input While trying to convert the input to an int data type While trying to access that index in the array

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. If input is not an integer, then output is Invalid Index --------End---------

Basic Java

24

. If input is an integer, but index is out of range in the array, then output is Requested Index Not Found --------End--------Note that in both the cases, the finally block is executed.

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. Java addresses four categories of visibility for class members. 1. 2. 3. 4.

Sub classes in the same package Non Subclasses in the same package Subclasses in different packages Classes are neither in the same package nor subclasses

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. default – When a member doesnot have an access specification, it is visible to subclasses as well as other classes in the same package. protected – If an element has to be seen outside the current package but only to classes that subclass your class directly. Defining Package: Creating a package in Java is quite easy. This is achieved by simply including a package command as the first statement in a Java Source file. Any classes that are declared with in that file belong to the specified package. The package statement defines a namepsace in which classes are stored. If you omit the package statement, the classes are put into the default package that has no name.

Basic Java

25

. Syntax for package statement: package mypackage;

Use package keyword as the first line in the file. E.g. package com.first The classes under this package are in com/first namespace. The classes under this package must be stored inside the com/first folder Use import keyword for using the classes in different package. Example for Package mechanism: package com.first.one; public class BaseClass { int x=6; // default access private int x_pri=2; // private access protected int x_pro=3; //protected access public int x_pub=4; //public access 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; class Derived extends BaseClass { Derived() { System.out.println("Inside Derived Class Constrcutor\n"); System.out.println("Value of x(default) is "+x); // Not available to derived class also because it is private (Class only) // System.out.println("Value of x(private) is "+x_pri); System.out.println("Value of x(protected) is "+x_pro);

Basic Java

26

. System.out.println("Value of x(public) is

"+x_pub);

} public static void main(String arg[]) { Derived deri=new Derived(); } } package com.first.one; public class TestBaseClass { public TestBaseClass() { System.out.println("Inside TestBaseClass constructor"); BaseClass bc1=new BaseClass(); System.out.println("Value of x(default) is "+bc1.x); // Not accessible because private - access is for Class only // System.out.println("Value of x(private) is "+bc1.x_pri); System.out.println("Value of x(protected) is "+bc1.x_pro); System.out.println("Value of x(public) is "+bc1.x_pub); } public static void main(String arg[]) { BaseClass bc=new BaseClass(); bc.display(); System.out.println("\n****************TestBaseClass* **************\n"); TestBaseClass test=new TestBaseClass(); } } package com.first.two; import com.first.one.BaseClass; class BaseClassNew extends BaseClass { BaseClassNew() { System.out.println("Constrcutor of Base class in another package"); //Not accessible because it is default - for package only //System.out.println("Value of x(default) is"+x); // Not accessible becuase it is private - for Class only //System.out.println("Value of x(private) is "+x_pri);

Basic Java

27

. System.out.println("Value of x(protected) is "+x_pro); System.out.println("Value of x(public) is "+x_pub); } public static void main(String arg[]) { BaseClassNew bcn=new BaseClassNew(); } } package com.first.two; import com.first.one.*; public class SomeClass { SomeClass() { System.out.println("Inside Constructor of SomeClass"); BaseClass bc=new BaseClass(); // Only for package //System.out.println("Value of x(default) is "+bc.x); // Only for Class //System.out.println("Value of x(private) is "+bc.x_pri); // Only for Class, subClass & package //System.out.println("Value of x(protected) is "+bc.x_pro); System.out.println("Value of x(public) is "+bc.x_pub); } public static void main(String arg[]) { SomeClass sc=new SomeClass(); } }

Basic Java

28

.

Basic Java

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 Data Type Wrapper Classes The Math Class String Classes System and Runtime Classes Thread Classes Class Classes Exception Handling Classes Process Classes

THE OBJECT CLASS 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.

DATA TYPE WRAPPER CLASSES The fundamental data types (int, char, float, and so on) in Java are not implemented as classes. Many times it is useful, however, to know more information about a fundamental types than just its value. By implementing class wrappers for the fundamental types, additional information can be maintained, as well as methods defined that act on the types. The data type wrapper classes serve as class versions of the fundamental data types, and are named similarly to the types they wrap. For example, the type wrapper for int is the Integer class. Following are the Java data type wrapper classes: • • • • • • •

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.

Basic Java

30

. THE MATH CLASS The Math class serves as a grouping of mathematical functions and constants. It is interesting to note that all the variables and methods in Math are static, and the Math class itself is final. This means you can’t derive new classes from Math. Additionally, you can’t instantiate the Math class. It’s best to think of the Math class as just a conglomeration of methods and constants for performing mathematical computations. The Math class includes the E and PI constants, methods for determining the absolute value of a number, methods for calculating trigonometric functions, and minimum and maximum methods, among others. EXAMPLE FOR MATH CLASS public class MathExample { public static void main(String args[]) { char temp = (char)-1; String input = ""; Double data = null; System.out.println("Enter any number"); /** Gets the user input**/ 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); } double d_data = data.doubleValue(); System.out.println("Printing Math values......"); System.out.println("Sin : " + (Math.sin(d_data))); System.out.println("Cos : " + (Math.cos(d_data))); System.out.println("Tan : " + (Math.tan(d_data))); System.out.println("asin : " + (Math.asin(d_data))); System.out.println("acos : " + (Math.acos(d_data))); System.out.println("atan : " + (Math.atan(d_data))); System.out.println("Abs : " + (Math.abs(d_data))); System.out.println("Exp : " + (Math.exp(d_data))); System.out.println("Log : " + (Math.log(d_data))); System.out.println("Sqrt : " + (Math.sqrt(d_data))); System.out.println("Ceil : " + (Math.ceil(d_data))); System.out.println("Floor : " + (Math.floor(d_data))); System.out.println("rint : " + (Math.rint(d_data)));

Basic Java

31

. 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 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: String str = "abc"; is equivalent to: char data[] = {'a', 'b', 'c'}; String str = new String(data); Here are some more examples of how strings can be used: 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. EXAMPLE FOR STRING CLASS public class StringExample { public static void main(String args[]) { String str = new String("Java World"); int length = str.length(); System.out.println("Length of data : "+length); System.out.println("Extracting character..."); for(int index=0;index
32

. } System.out.println("Substring from 3rd position : " +(str.substring(3))); System.out.println("Substring from 3rd to 5th position : " +(str.substring(3,6))); System.out.println("Index of Wor : " + (str.indexOf("Wor"))); System.out.println("Converting to Upper Case : " + (str.toUpperCase())); System.out.println("Replacing 'a' with '*' : " + (str.replace('a','*'))); System.out.println("--------End-------"); } } THE STRINGBUFFER CLASS 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" is compiled to the equivalent of: 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. EXAMPLE FOR STRINGBUFFER public class SBExample { public static void main(String args[]) { String s = new String("Hello"); /** Constructors

Basic Java

33

. 1. Empty Constructor will create with initial capacity of 16 characters. 2. Constructor with specified characters as the initial capacity 3. Constructor with specified string as the initial value */ StringBuffer sb1 = new StringBuffer(); StringBuffer sb2 = new StringBuffer(40); StringBuffer sb3 = new StringBuffer(s); //Appending a boolean value sb1.append(true); System.out.println("Value of StringBuffer = " + sb1); //Appending a character sb1.append('c'); System.out.println("Value of StringBuffer = " + sb1); //Appending a character array char c[] = {'H','e','l','l','o'}; sb1.append(c); System.out.println("Value of StringBuffer = " + sb1); sb1.append(c,2,3); System.out.println("Value of StringBuffer = " + sb1); double d = 12.141354; sb1.append(d); System.out.println("Value of StringBuffer = " + sb1); float f = (float)15.1; sb1.append(f); System.out.println("Value of StringBuffer = " + sb1); int i = 1; sb1.append(i); System.out.println("Value of StringBuffer = " + sb1); long l = 1000000; sb1.append(l); System.out.println("Value of StringBuffer = " + sb1); sb1.append(s); System.out.println("Value of StringBuffer = " + sb1); System.out.println("Capacity = " + sb2.capacity()); System.out.println("Character at 5th position = " + sb1.charAt(5)); sb1.getChars(0,4,c,0); System.out.println("Chars extracted from Sb1 = " + c); //Insert the boolean value at the 5th position sb1.insert(5,true);

//Insert the character value at the 9th position sb1.insert(9,'M');

Basic Java

34

. System.out.println("Length of the string buffer = " + sb1.length()); sb1.reverse(); System.out.println("Reverse of the String Buffer = " + sb1); sb1.setCharAt(5, 'Y'); System.out.println("Value of String Buffer = " + sb1); } } THE SYSTEM AND RUNTIME CLASSES The System and Runtime classes provide a means for your programs to access system and runtime environment resources. Like the Math class, the System class is final and is entirely composed of static variables and methods. The System class basically provides a system-independent programming interface to system resources. Examples of system resources include the standard input and output streams, System.in and System.out, which typically model the keyboard and monitor. 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.

EXAMPLE FOR RUNTIME public class RuntimeExample { public static void main(String args[]) { try { Runtime run = Runtime.getRuntime(); run.exec("notepad.exe"); } catch(Exception err) { System.out.println("Exception " +err.getMessage()); } } } 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 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. Basic Java

35

. 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. Example to print the class name of an object: void printClassName(Object obj) { System.out.println("The class of " + obj + " is " + obj.getClass().getName()); }

Basic Java

36

. 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: • • • • • • •

The Date Class Data Structure Classes The Random Class The StringTokenizer Class The Properties Class The Observer Interface The Enumeration Interface

THE DATE CLASS The Date class represents a calendar date and time in a system-independent fashion. The Date class provides methods for retrieving the current date and time as well as computing days of the week and month. EXAMPLE FOR DATE CLASS import java.util.Date; public class DateExample { public static void args[]) { String days[] = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"}; Date sys_date = new Date(); Date date = new Date(101,8,3);

main(String

System.out.println("System Date : " + (sys_date.toString())); System.out.println("Specified Date : " + (date.toString())); int day = date.getDay();System.out.println("The day for the specified date : " + days[day]);System.out.println("Does the specified date precede the system date ? ");System.out.println(date.before(sys_date)); }}

THE CALENDAR CLASS 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.)

Basic Java

37

. 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:

EXAMPLE FOR CALENDAR CLASS import java.util.Calendar; public class CalendarExample { public static void main(String args[]) { Calendar calendar = Calendar.getInstance(); int date = calendar.get(Calendar.DATE); int month = calendar.get(Calendar.MONTH); int year = calendar.get(Calendar.YEAR); System.out.println("Date : " + date + "/" + (month+1) + "/" +year); calendar.add(Calendar.DATE,50); date = calendar.get(Calendar.DATE); month = calendar.get(Calendar.MONTH); year = calendar.get(Calendar.YEAR); System.out.println("Latest (month+1) + "/" +year);

date

:

"

+

date

+

"/"

+

} }

Data Structure Classes The Java data structure classes and interfaces implement popular data structures for storing data. The data structure classes and interfaces are as follows: • Dictionary • Hashtable • Vector • Stack • Enumeration • Random

THE RANDOM CLASS Many programs, especially programs that model the real world, require some degree of randomness. Java provides randomness by way of the Random class. The Random class implements a random-number generator by

Basic Java

38

. 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. EXAMPLE FOR RANDOM CLASS import public public Random

java.util.*; class RandomExample { static void main(String args[]) { random = new Random();

System.out.println("Random number(int):" + (random.nextInt())); System.out.println("Random number(float): " + (random.nextFloat())); System.out.println("Random number(double): " +(random.nextDouble())); System.out.println("Random number(gaussian): " +(random.nextGaussian())); Date date = new Date(); Random seed_random = new Random(date.getTime()); System.out.println("Random number with seed(int): " +(seed_random.nextInt())); System.out.println("Random number with seed(float): " +(seed_random.nextFloat())); System.out.println("Random number with seed(double): " + (seed_random.nextDouble())); System.out.println("Random number with seed(gaussian) : " +(seed_random.nextGaussian())); } }

THE STRINGTOKENIZER CLASS The StringTokenizer class provides a means of converting text strings into individual tokens. By specifying a set of delimiters, you can parse text strings into tokens using the StringTokenizer class. String tokenization is useful in a wide variety of programs, from compilers to text-based adventure games. public class StringTokenizer extends Object implements Enumeration 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

Basic Java

39

. (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: • •

If the flag is false, delimiter characters serve to separate tokens. A token is a maximal sequence of consecutive characters that are not delimiters. If the flag is true, delimiter characters are considered to be tokens. A token is either one delimiter character, or a maximal sequence of consecutive characters that are not delimiters.

The following is one example of the use of the tokenizer. The code: StringTokenizer st = new StringTokenizer("this is a test"); while (st.hasMoreTokens()) { println(st.nextToken()); } prints the following output: this is a test

EXAMPLE FOR STRINGTOKENIZER import java.util.StringTokenizer; public class TokenizerExample { public static void main(String args[]) { char temp = (char)-1; String input = ""; System.out.println("Enter a string "); 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()));

Basic Java

:

40

. while(tokenizer.hasMoreTokens()) { System.out.println(tokenizer.nextToken()); } } }

THE HASHTABLE CLASS 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: Hashtable numbers = new Hashtable(); numbers.put("one", new Integer(1)); numbers.put("two", new Integer(2)); numbers.put("three", new Integer(3)); To retrieve a number, use the following code: Integer n = (Integer)numbers.get("two"); if (n != null) { System.out.println("two = " + n); }

EXAMPLE FOR HASHTABLE CLASS import java.util.*; public class HashtableExample { public static void main(String args[]) { Hashtable hash = new Hashtable(); String days[] = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"}; for(int index=0;index
Basic Java

41

. Integer pos = new Integer(day); System.out.println("Day : " + (hash.get(pos).toString())); } } THE STACK CLASS The Stack class represents a last-in-first-out (LIFO) stack of objects. EXAMPLE FOR STACK CLASS import java.util.*; public class StackExample { public static void main(String args[]) { Stack stack = new Stack(); Date date = new Date(); StringTokenizer tokenizer = new StringTokenizer(date.toString()); System.out.println("tokens : "+tokenizer.countTokens()); while (tokenizer.hasMoreTokens()) { stack.push(tokenizer.nextToken()); } Object obj = stack.peek(); System.out.println("First element in stack (obj.toString()));

- by peek : " +

System.out.println("Pop out the elements in stack "); while(!stack.empty()) { obj = stack.pop(); System.out.println(obj.toString()); } } } THE VECTOR CLASS 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. Each vector tries to optimize storage management by maintaining a capacity and a capacityIncrement. The capacity is always at least as large as the vector size; it is usually larger because as components are added to the vector, the vector's storage 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.

Basic Java

42

. EXAMPLE FOR VECTOR CLASS import java.util.*; public class VectorExample { public static void main(String args[]) { Vector store = new Vector(); String input = ""; char temp = (char)-1; System.out.println("Enter a string "); try { do { temp = (char)System.in.read(); input = input+temp; }while(temp != '\n'); input = input.trim(); }catch(Exception err){} StringTokenizer tokenizer = new StringTokenizer(input); while(tokenizer.hasMoreTokens()) { store.addElement(tokenizer.nextToken()); } System.out.println("Size of the Vector : "+store.size()); System.out.println("Capacity of the Vector : "+store.capacity()); System.out.println("First Element : "+store.firstElement()); System.out.println("Last Element : "+store.lastElement()); Enumeration enum = store.elements(); while(enum.hasMoreElements()) { System.out.println(enum.nextElement().toString()); } store.trimToSize(); System.out.println("Capacity of the vector after trimming : " + (store.capacity())); } }

Basic Java

43

. 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 programming effort by providing useful data structures and algorithms so you don't have to write them yourself.



Increases performance by providing high-performance implementations of useful data structures and algorithms. Because the various implementations of each interface are interchangeable, programs can be easily tuned by switching implementations.



Provides interoperability between unrelated APIs by establishing a common language to pass collections back and forth.



Reduces the effort required to learn APIs by eliminating the need to learn multiple ad hoc collection APIs.



Reduces the effort required to design and implement APIs by eliminating the need to produce ad hoc collections APIs.



Fosters software reuse by providing a standard interface for collections and algorithms to manipulate them.

The collections framework consists of: •

Collection Interfaces - Represent different types of collections, such as sets, lists and maps. These interfaces form the basis of the framework.



General-purpose Implementations - Primary implementations of the collection interfaces.



Legacy Implementations - The collection classes from earlier releases, Vector and Hashtable, have been retrofitted to implement the collection interfaces.



Wrapper Implementations - Add functionality, such as synchronization, to other implementations.



Convenience Implementations - High-performance "mini-implementations" of the collection interfaces.



Abstract Implementations - Partial implementations of the collection interfaces to facilitate custom implementations.

Basic Java

44

. •

Algorithms - Static methods that perform useful functions on collections, such as sorting a list.



Infrastructure - Interfaces that provide essential support for the collection interfaces.



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 collectionview 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. 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

Basic Java

45

. 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. The last two core collection interfaces: SortedSet A SortedSet is a Set that maintains its elements in ascending order. Several additional operations are provided to take advantage of the ordering. The SortedSet interface is used for things like word lists and membership rolls. SortedMap A SortedMap is a Map that maintains its mappings in ascending key order. It is the Map analogue of SortedSet. The SortedMap interface is used for apps like dictionaries and telephone directories. 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: •

Collections that do not support any modification operations (such as add, remove and clear) are referred to as unmodifiable. Collections that are not unmodifiable are referred to modifiable.



Collections that additionally guarantee that no change in the Collection object will ever be visible are referred to as immutable. Collections that are not immutable are referred to as mutable.



Lists that guarantee that their size remains constant even though the elements may change are referred to as fixed-size. Lists that are not fixedsize are referred to as variable-size.

Basic Java

46

. 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.



Obey some arbitrary predicate.

Attempting to add an element that violates an implementation's restrictions results in a runtime exception, typically a ClassCastException, an IllegalArgumentException or a NullPointerException. Attempting to remove or test for the presence of an element that violates an implementation's restrictions may result in an exception, though some "restricted collections" may permit this usage. Collection Implementations Class that implement the collection interfaces typically have names of the form . The general-purpose implementations are summarized in the table below: Implementations Hash Table Resizable Array Balanced Tree Linked List TreeSet Set HashSet Interfaces List ArrayList LinkedList TreeMap Map HashMap The general-purpose implementations support all of the optional operations in the collection interfaces, and have no restrictions on the elements they may contain. The AbstractCollection, AbstractSet, AbstractList, AbstractSequentialList and AbstractMap classes provide skeletal implementations of the core collection interfaces, to minimize the effort required to implement them. The API documentation for these classes describes precisely how each method is implemented so the implementer knows which methods should be overridden, given the performance of the "basic operations" of a specific implementation. 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 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:

Basic Java

47

. 1. It is a truly fundamental operation: a basic operations in terms of which others could be reasonably defined, 2. There is a compelling performance reason why an important implementation would want to override it. 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.

Basic Java

48

. 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: • • • •

Input Stream Classes Output Stream Classes File Classes The StreamTokenizer Class

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.

THE FILE CLASS 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. Example for File import java.io.*; import java.util.Date; public class FileExample { public static void main(String args[]) {

Basic Java

49

. if(args.length == 0) { System.out.println("Usage : java FileExample "); System.exit(0); } String filename = args[0]; try { File file = new File(filename); System.out.println("File Exists : "+file.exists()); Date date = new Date(file.lastModified()); System.out.println("Last Modified : "+date.toString()); System.out.println("Absolute Path : "+file.getAbsolutePath()); System.out.println("Length of file : "+file.length()); System.out.println("Parent : "+file.getParent()); } catch(Exception err) { System.out.println("Exception in accessing file"); } } }

THE RANDOMACCESSFILE CLASS 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. Example for RandomAccessFile 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");

Basic Java

50

. System.out.println("The size of the file "+srcrdm.length()); System.out.println("The file pointer is at "+srcrdm.getFilePointer()); data = srcrdm.readLine(); while(!data.equals("")) { dstrdm.writeBytes(data); data = srcrdm.readLine(); } System.out.println("File successfully copied"); System.out.println("Open the destination file to view the result"); } catch(Exception err) { } }} INPUT STREAM CLASSES 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. OUTPUT STREAM CLASSES Output streams are the counterpart to input streams and handle writing data to an output source. Similar to input sources, output sources include files, strings, memory, and anything else that can contain data. The output stream classes defined in java.io follow: • • • •

OutputStream BufferedOutputStream ByteArrayOutputStream DataOutputStream

Basic Java

51

. • • • •

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 BUFFEREDINPUTSTREAM CLASS 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. Example for BufferedInputStream 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 BUFFEREDOUTPUTSTREAM CLASS 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. Example for BufferedOutputStream Class 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”); byte[] buf = new byte[64];

Basic Java

52

. s.getBytes(0, s.length(), buf, 0); // Output the byte array (buffered) BufferedOutputStream out = new BufferedOutputStream(System.out); try { out.write(buf, 0, 64); out.flush(); } catch (Exception e) { System.out.println(“Error: “ + e.toString()); } } } THE DATAINPUTSTREAM CLASS 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: 110bits 6-1010bits 0-5 Characters in the range '\u0800' to '\uFFFF' are represented by three bytes: 1110bits 12-1510bits 6-1110bits 0-5 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.

Example for DataInputStream and DataOutputStream import import import import import import

java.io.DataInputStream; java.io.DataOutputStream; java.io.FileInputStream; java.io.FileOutputStream; java.io.File; java.io.IOException;

public class DataIOApp {

Basic Java

53

. public static void main(String args[]) throws IOException { File file = new File("test.txt"); FileOutputStream outFile = new FileOutputStream(file); DataOutputStream outStream = new DataOutputStream(outFile); outStream.writeBoolean(true); outStream.writeInt(123456); outStream.writeChar('j'); outStream.writeDouble(1234.56); System.out.println(outStream.size()+" bytes were written"); outStream.close(); outFile.close(); FileInputStream inFile = new FileInputStream(file); DataInputStream inStream = new DataInputStream(inFile); System.out.println(inStream.readBoolean()); System.out.println(inStream.readInt()); System.out.println(inStream.readChar()); System.out.println(inStream.readDouble()); inStream.close(); inFile.close(); file.delete(); } } THE FILEINPUTSTREAM CLASS A file input stream is an input stream for reading data from a File or from a FileDescriptor. Example for FileInputStream 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); }}

Basic Java

54

. THE FILEOUTPUTSTREAM CLASS A file output stream is an output stream for writing data to a File or to a FileDescriptor. Example for FileOutputStream import java.io.*; class WriteFile { public static void main (String args[]) { // Read the user input byte buf[] = new byte[64]; try { System.in.read(buf, 0, 64); } catch (Exception e) { System.out.println(“Error: “ + e.toString()); } // Output the data to a file try { FileOutputStream out = new FileOutputStream(“Output.txt”); out.write(buf); } catch (Exception e) { System.out.println(“Error: “ + e.toString()); } } } THE BYTEARRAYINPUTSTREAM 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. Example for Byte Array input/output stream import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class ByteArrayIOApp { public static void main(String args[]) throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); String s = "This is a test."; for(int i=0;i<s.length();++i) outStream.write(s.charAt(i)); System.out.println("outstream: "+outStream);

Basic Java

55

. 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)); } } THE BYTEARRAYOUTPUTSTREAM CLASS 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(). THE CHARARRAYREADER CLASS This class implements a character buffer that can be used as a character-input stream. Example for CharArrayReader and Writer import java.io.CharArrayReader; import java.io.CharArrayWriter; import java.io.IOException; public class CharArrayIOApp { public static void main(String args[]) throws IOException { CharArrayWriter outStream = new CharArrayWriter(); String s = "This is a test."; for(int i=0;i<s.length();++i) outStream.write(s.charAt(i)); System.out.println("outstream: "+outStream); System.out.println("size: "+outStream.size()); CharArrayReader inStream; inStream = new CharArrayReader(outStream.toCharArray()); int ch=0; StringBuffer sb = new StringBuffer(""); while((ch = inStream.read()) != -1) sb.append((char) ch); s = sb.toString(); System.out.println(s.length()+" characters were read"); System.out.println("They are: "+s); } }

Basic Java

56

. THE LINENUMBERREADER CLASS A buffered character-input stream that keeps track of line numbers. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed. Example for LineNumberReader import import import import

java.io.LineNumberReader; java.io.FileReader; java.io.BufferedWriter; java.io.IOException;

public class LineNumberIOApp { public static void main(String args[]) throws IOException { FileReader inFile = new FileReader("LineNumberIOApp.java"); LineNumberReader inLines = new LineNumberReader(inFile); String inputLine; while ((inputLine=inLines.readLine()) != null) { System.out.println(inLines.getLineNumber()+". "+inputLine); } } } THE PUSHBACKINPUTSTREAM CLASS 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. Example PushbackInputStream and OutputStream Classes import import import import

java.io.PushbackInputStream; java.io.ByteArrayInputStream; java.io.ByteArrayOutputStream; java.io.IOException;

public class PushbackIOApp { public static void main(String args[]) throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); String s = "This is a test."; for(int i=0;i<s.length();++i) outStream.write(s.charAt(i)); Basic Java

57

. System.out.println("outstream: "+outStream); System.out.println("size: "+outStream.size()); ByteArrayInputStream inByteArray; inByteArray = new ByteArrayInputStream(outStream.toByteArray()); PushbackInputStream inStream; inStream = new PushbackInputStream(inByteArray); char ch = (char) inStream.read(); System.out.println("First character of inStream is "+ch); inStream.unread((int) 't'); int inBytes = inStream.available(); System.out.println("inStream has bytes"); byte inBuf[] = new byte[inBytes];

"+inBytes+"

available

for(int i=0;i
Basic Java

58

. else { System.out.print((char) c); ++byteCount; } } System.out.println(byteCount+" bytes were read"); inStream.close(); f1.close(); f2.close(); } }

THE STREAMTOKENIZER CLASS 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. In addition, an instance has four flags. These flags indicate: • • •

Whether line terminators are to be returned as tokens or treated as white space that merely separates tokens. Whether C-style comments are to be recognized and skipped. Whether C++-style comments are to be recognized and skipped. Whether the characters of identifiers are converted to lowercase.

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. Example for StreamTokenizer Class import import import import

java.io.StreamTokenizer; java.io.InputStreamReader; java.io.BufferedReader; java.io.IOException;

public class StreamTokenApp { public static void main(String args[]) throws IOException { BufferedReader inData = new BufferedReader(new InputStreamReader(System.in)); StreamTokenizer inStream = new StreamTokenizer(inData); inStream.commentChar('#');

Basic Java

59

. 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); } } 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. Example for Piped Input/Output Operations import java.io.*; public class PipedExample { public static void main(String args[]) { if(args.length == 0) { System.out.println("Usage : java PipedExample "); System.exit(0); } String filename = args[0]; try { File file = new File(filename); FileInputStream fis = new FileInputStream(file); byte store[] = new byte[fis.available()]; byte p_store[] = new byte[fis.available()]; fis.read(store,0,store.length);

Basic Java

60

. PipedOutputStream piped_out = new PipedOutputStream(); PipedInputStream piped_in = new PipedInputStream(piped_out); piped_out.write(store,0,store.length); piped_in.read(p_store,0,p_store.length); System.out.println("Result stored in file 'output.txt'"); FileOutputStream fos = new FileOutputStream("ouput.txt"); fos.write(p_store,0,p_store.length); System.out.println("----------End----------"); } catch(Exception err) { System.out.println("Exception in accessing file"); } } } 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. THE SERIALIZABLE INTERFACE Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and serves only to identify the semantics of being serializable. To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype'spublic, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable in this case. The error will be detected at runtime. During deserialization, the fields of non-serializable classes will be initialized using thepublic or protected no-arg constructor of the class. A no-arg constructor must be accessible to the subclass that is serializable. The fields of serializable subclasses will be restored from the stream.

Basic Java

61

. 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: private void writeObject(java.io.ObjectOutputStream out) throws IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; 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. 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.

THE EXTENALIZABLE INTERFACE 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. THE OBJECTINPUTSTREAM CLASS An ObjectInputStream deserializes primitive data and objects previously written using an ObjectOutputStream. ObjectOutputStream and ObjectInputStream can provide an application with persistent storage for graphs of objects when used with a

Basic Java

62

. FileOutputStream and FileInputStream respectively. ObjectInputStream is used to recover those objects previously serialized. Other uses include passing objects between hosts using a socket stream or for marshaling and unmarshaling arguments and parameters in a remote communication system. 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. Only objects that support the java.io.Serializable or java.io.Externalizable interface can be read from streams. The method readObject is used to read an object from the stream. Java's safe casting should be used to get the desired type. In Java, strings and arrays are objects and are treated as objects during serialization. When read they need to be cast to the expected type. 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 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. Reading an object is analogous to running the constructors of a new object. Memory is allocated for the object and initialized to zero (NULL). No-arg constructors are invoked for the non-serializable classes and then the fields of the serializable classes are restored from the stream starting with the serializable class closest to java.lang.object and finishing with the object's most specifiec class. For example to read from a stream as written by the example in ObjectOutputStream: FileInputStream istream = new FileInputStream("t.tmp"); ObjectInputStream p = new ObjectInputStream(istream); int i = p.readInt(); String today = (String)p.readObject(); Date date = (Date)p.readObject(); istream.close(); Classes control how they are serialized by java.io.Serializable or java.io.Externalizable interfaces.

implementing

either

the

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: private void writeObject(java.io.ObjectOutputStream stream) throws IOException; private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException;

Basic Java

63

. 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. 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. Example for ObjectInput and Output Streams import import import import import import import import

java.io.ObjectInputStream; java.io.ObjectOutputStream; java.io.Serializable; java.io.FileInputStream; java.io.FileOutputStream; java.io.File; java.io.IOException; java.util.Date;

public class ObjectIOApp { public static void main(String args[]) throws IOException, ClassNotFoundException { File file = new File("test.txt"); FileOutputStream outFile = new FileOutputStream(file); ObjectOutputStream outStream = new ObjectOutputStream(outFile); TestClass1 t1 = new TestClass1(true,9,'A',0.0001,"java"); TestClass2 t2 = new TestClass2(); String t3 = "This is a test."; Date t4 = new Date();

Basic Java

64

. outStream.writeObject(t1); outStream.writeObject(t2); outStream.writeObject(t3); outStream.writeObject(t4); outStream.close(); outFile.close(); FileInputStream inFile = new FileInputStream(file); ObjectInputStream inStream = new ObjectInputStream(inFile); 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(); } }

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; } public String toString() { String r = String.valueOf(b)+" "; r += String.valueOf(i)+" "; r += String.valueOf(c)+" "; r += String.valueOf(d)+" "; r += String.valueOf(s); return r; } }

class TestClass2 implements Serializable { int i; TestClass1 tc1; TestClass1 tc2;

Basic Java

65

. TestClass2() { i=0; tc1 = new TestClass1(true,2,'j',1.234,"Java"); tc2 = new TestClass1(false,7,'J',2.468,"JAVA"); } public String toString() { String r = String.valueOf(i)+" "; r += tc1.toString()+" "; r += tc2.toString(); return r; } } THE OBJECTOUTPUTSTREAM CLASS An ObjectOutputStream writes primitive data types and graphs of Java objects to an OutputStream. The objects can be read (reconstituted) using an ObjectInputStream. Persistent storage of objects can be accomplished by using a file for the stream. If the stream is a network socket stream, the objects can be reconsituted on another host or in another process. 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. For example to write an object that can be read by the example in ObjectInputStream: FileOutputStream ostream = new FileOutputStream("t.tmp"); ObjectOutputStream p = new ObjectOutputStream(ostream); p.writeInt(12345); p.writeObject("Today"); p.writeObject(new Date()); p.flush(); ostream.close();

Basic Java

66

. Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures: private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException; private void writeObject(java.io.ObjectOutputStream stream) throws IOException

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 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. Serialization of an object can be prevented by implementing writeObject and readObject methods that throw the NotSerializableException. The exception will be caught by the ObjectOutputStream and abort the serialization process. 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.

THE INPUTSTREAMREADER CLASS An InputStreamReader is a bridge from byte streams to character streams: It reads bytes and translates them into characters according to a specified character encoding. The encoding that it uses may be specified by name, or the platform's default encoding may be accepted. Each invocation of one of an InputStreamReader's read() methods may cause one or more bytes to be read from the underlying byte-input stream. For top efficiency, consider wrapping an InputStreamReader within a BufferedReader; for example, BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); Example for InputStreamReader import java.io.InputStreamReader; import java.io.BufferedReader;

Basic Java

67

. import java.io.IOException; public class InputConversionApp { public static void main(String args[]) throws IOException { InputStreamReader in = new InputStreamReader(System.in); BufferedReader inStream = new BufferedReader(in); System.out.println("Encoding: "+in.getEncoding()); String inputLine; do { System.out.print(">"); System.out.flush(); inputLine=inStream.readLine(); System.out.println(inputLine); } while (inputLine.length() != 0); } }

THE OUTPUTSTREAMWRITER CLASS Write characters to an output stream, translating characters into bytes according to a specified character encoding. Each OutputStreamWriter incorporates its own CharToByteConverter, and is thus a bridge from character streams to byte streams. The encoding used by an OutputStreamWriter may be specified by name, by providing a CharToByteConverter, or by accepting the default encoding, which is defined by the system property file.encoding. Each invocation of a write() method causes the encoding converter to be invoked on the given character(s). The resulting bytes are accumulated in a buffer before being written to the underlying output stream. The size of this buffer may be specified, but by default it is large enough for most purposes. Note that the characters passed to the write() methods are not buffered. For top efficiency, consider wrapping an OutputStreamWriter within a BufferedWriter so as to avoid frequent converter invocations. For example,

Writer out

Basic Java

= new BufferedWriter(new OutputStreamWriter(System.out));

68

. 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 Applets can be run in a browser such as Netscape Navigator, Internet Explorer. Several differences exist between applets and applications. The most important of these is that Java applet classes extend an existing class. This class is called java.applet.Applet. You have to extend Applet in order for a class to be usable as such. 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); } } Creating HTML file 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. <APPLET CODE="HelloApplet.class"

WIDTH

="200"

HEIGHT="200">

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.

Basic Java

69

. 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 Appletviewer opens a new window and runs HelloApplet in it. UNDERSTANDING THE SOURCE CODE Importing Other Classes 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. Declaring an Applet Class 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. public class HelloApplet extends Applet { 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: public class HelloApplet extends java.applet.Applet {

Basic Java

70

. 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. public void paint (Graphics g) { 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: g.drawString ("Hello World!",0,50); } BRIEF LIFE OF AN APPLET 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. The java.applet.AppletContext Interface 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.

Basic Java

71

. The java.applet.AppletStub Interface 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 java.applet.AudioClip Interface 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.

Basic Java

72

.

THE ABSTRACT WINDOW TOOLKIT The Abstract Window Toolkit (normally referred to as the AWT) is a well-thought-out and very portable windowing library. It is a standard part of the Java environment and provides all of the basic functionality one would expect to use in a modern windowing system. The AWT delivers on the promise made by many cross-platform windowing libraries, allowing your application to run on completely different windowing systems. Moreover, it manages to preserve the look and feel of the user’s system, so AWTbased applications won’t get a reputation for having a Java look. The bulk of the basic AWT is sub classed from one basic class: Component HIERARCHY CHART OF THE AWT COMPONENTS

Object

Compone

Container Panel Button Window TextCompone

TextField Frame

Choice

Dialog

TextArea

List

MenuCompone

MenuItem

Menu

MenuBar

Basic Java

73

. A Simple AWT Applet import java.awt.*; import java.applet.Applet; public class Example1 extends Applet { Button hiButton; public void init() { hiButton = new Button(“Click Me!”); add(hiButton); } } 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 Interface components encompass all of the standard widgets or controls normally associated with a windowing system. Examples of these include buttons, text labels, scrollbars, pick lists, and text-entry fields.

Basic Java

74

. 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: • • • • • • •

The Component object on which to draw. A translation origin for rendering and clipping coordinates. The current clip. The current color. The current font. The current logical pixel operation function (XOR or Paint). The current XOR alternation color .

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.

Basic Java

75

. Example for using Graphics class import java.awt.Graphics; import java.applet.Applet; public class GraphicsExample extends Applet { public void paint(Graphics g) { g.drawString("Example for Graphics class",50,50); 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); } }

The java.awt.Color class 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 0255. 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. Example for Using Color class import java.awt.*; import java.applet.Applet; public class ColorExample extends Applet { Color c_green; public void init() { c_green = new Color(0,255,0); } public void paint(Graphics g) { g.drawString("Example for Color class",50,50); g.drawRect(60,60,10,10); g.setColor(Color.red); g.drawOval(90,60,10,10); Basic Java

76

. 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); } } The java.awt.Font Class A class that produces font objects. Example for Font Class import java.awt.*; import java.applet.Applet; public class FontExample extends Applet { Font font;

public void init() { font = new Font("Helvetica",Font.BOLD+Font.ITALIC,15); } public void paint(Graphics g) { g.setFont(font); g.drawString("Text Displayed in Helvetica font",25,50); } } The java.awt.FontMetrics class public abstract class FontMetrics extends Object implements Serializable A font metrics object, which gives information about the rendering of a particular font on a particular screen. Note that the implementations of these methods are inefficient, they are usually overridden with more efficient toolkit-specific implementations. 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()

Basic Java

77

. • •

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.

In addition, every character in a font has an ascent, a descent, and an advance width. The ascent is the amount by which the character ascends above the baseline. The descent is the amount by which the character descends below the baseline. The advance width indicates the position at which AWT should place the next character. 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. An array of characters or a string can also have an ascent, a descent, and an advance width. The ascent of the array is the maximum ascent of any character in the array. The descent is the maximum descent of any character in the array. The advance width is the sum of the advance widths of each of the characters in the array. 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. The java.awt.Button Class 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. When a button is pressed and released, AWT sends an instance of ActionEvent to the button, by calling processEvent on the button. The button's processEvent method receives all events for the button; it passes an action event along by calling its own processActionEvent method. The latter method passes the action event on to any action listeners that have registered an interest in action events generated by this button.

Basic Java

78

. 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.

The java.awt.TextComponent Class public class TextComponent extends Component The TextComponent class is the superclass of any component that allows the editing of some text. A text component embodies a string of text. The TextComponent class defines a set of methods that determine whether or not this text is editable. If the component is editable, it defines another set of methods that supports a text insertion caret. 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. The java.awt.Checkbox Class public class Checkbox extends Component implements ItemSelectable 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 following code example creates a set of check boxes: add(new Checkbox("one", null, true)); add(new Checkbox("two")); add(new Checkbox("three")); 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. The java.awt.CheckboxGroup Class public class CheckboxGroup extends Object implements Serializable The CheckboxGroup class is used to group together a set of Checkbox buttons. 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: Basic Java

79

. 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 public class Choice extends Component implements ItemSelectable The Choice class presents a popup menu of choices. The current choice is displayed as the title of the menu. Example for adding Choice import java.awt.*; import java.applet.Applet; public class ChoiceExample extends Applet { Choice ColorChooser; public void init|() { ColorChooser = new Choice(); ColorChooser.add("Green"); ColorChooser.add("Red"); ColorChooser.add("Blue"); add(ColorChooser); } } The java.awt.List Class public class List extends Component implements ItemSelectable The List component presents the user with a scrolling list of text items. The list can be set up so that the user can choose either one item or multiple items. For example, the code . . . List lst = new List(4, false); lst.add("Mercury"); lst.add("Venus"); lst.add("Earth"); lst.add("JavaSoft"); lst.add("Mars"); lst.add("Jupiter"); lst.add("Saturn"); lst.add("Uranus"); lst.add("Neptune"); lst.add("Pluto"); cnt.add(lst); 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. Basic Java

80

. 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. The java.awt.Canvas Class 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. Example for Canvas Class import java.awt.*; public class CanvasTest extends java.applet.Applet { MyCanvas doodle; public void init() { doodle = getSize().height); add(doodle); } }

new

MyCanvas(getSize().width,

class MyCanvas extends Canvas { public MyCanvas() { this(100,100); } public MyCanvas(int width, int height) { resize(width,height); } public void paint(Graphics g) {

Basic Java

81

. g.fillRect(0, 0, getSize().width-1, getSize().height1); } } The java.awt.Scrollbar Class public class Scrollbar extends Component implements Adjustable The Scrollbar class embodies a scroll bar, a familiar user interface object. A scroll bar provides a convenient means for allowing a user to select from a range of values. The following code provides a sample model of scrollbar. redSlider=new Scrollbar(Scrollbar.VERTICAL, 0, 1, 0, 255); add(redSlider); 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: ranger = new Scrollbar(Scrollbar.HORIZONTAL, 0, 64, 0, 255); add(ranger); 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.

Basic Java

82

. 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. • • • • •

AdjustmentEvent.TRACK replaces Event.SCROLL_ABSOLUTE AdjustmentEvent.UNIT_INCREMENT replaces Event.SCROLL_LINE_UP AdjustmentEvent.UNIT_DECREMENT replaces Event.SCROLL_LINE_DOWN AdjustmentEvent.BLOCK_INCREMENT replaces Event.SCROLL_PAGE_UP AdjustmentEvent.BLOCK_DECREMENT replaces Event.SCROLL_PAGE_DOWN

The java.awt.ScrollPane Class public class ScrollPane extends Container 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 2.always 3.never

: scrollbars created and shown only when needed by scrollpane : scrollbars created and always shown by the scrollpane : 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. Certain adjustable properties (minimum, maximum, blockIncrement, and visibleAmount) are set internally by the scrollpane in accordance with the geometry of the scrollpane and its child and these should not be set by programs using the scrollpane. 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 placement of the scrollbars is controlled by platformspecific properties set by the user outside of the program. The initial size of this container is set to 100x100, but can be reset using setSize().

Basic Java

83

. 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. Example for ScrollPane Class 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); } }

class XCanvas extends Canvas { public void paint(Graphics g) { g.drawLine(0, 0, 200, 200); g.drawLine(0, 200, 200, 0); } }

The java.awt.Insets Class public class Insets extends Object implements Cloneable, Serializable An Insets object is a representation of the borders of a container. It specifies the space that a container must leave at each of its edges. The space can be a border, a blank space, or a title. Example for Using Insets import java.awt.*; class InsetTest { public static void main (String[] args) { MyFrame win = new MyFrame(); win.setLayout( new FlowLayout() ); win.add( new Button("One") ); win.add( new Button("Two") ); win.add( new Button("Three") ); Basic Java

84

. 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, //public Insets insets() { return new Insets(25, } // public Insets insets() { return new Insets(25, } //public Insets insets() { return new Insets(25, 2, }

2, 2); } 100, 2, 2); 2, 100, 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. Event Handling in JDK1.0 Example for using action import java.awt.*; import java.applet.Applet; public class ActionEvent extends Applet { Button bt_submit; TextField tf_data; public void init() { bt_submit = new Button("SUBMIT"); tf_data = new TextField(15); add(bt_submit);

Basic Java

85

. add(tf_data); } public boolean action(Event evt, Object obj) { if(evt.target == bt_submit) { tf_data.setText("Button Clicked"); } return true; } } Let’s break the action() method down line by line: public boolean action(Event evt, Object what) { 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. Event Handling in Detail In almost all cases, you will want to use the event-handling methods that Sun has provided for you. These are summarized in Table below. Remember that everything is relative to the component. For example, the mouseMove() method of a component is called when the mouse is moved inside that component. Java events. Event Type Action taken Mouse button pressed Mouse button released Mouse moved Mouse dragged Mouse enters component Mouse exits component Key pressed

Basic Java

Method action(Event evt, Object what) mouseDown(Event evt, int x, int y) mouseUp(Event evt, int x, int y) mouseMove(Event evt, int x, int y) mouseDrag(Event evt, int x, int y) mouseEnter(Event evt, int x, int y) mouseExit(Event evt, int x, int y) keyDown(Event evt, int key)

86

. Key released keyUp(Event evt, int key) 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. Adding new behavior to the sample applet. import java.awt.*; import java.applet.Applet; public class Example3 extends Applet { Button hiButton; public void init() { hiButton = new Button(“Click Me!!!”); add(hiButton); } public boolean mouseEnter(Event evt, int x, int y) { hiButton.setLabel(“Go Away!”); return true; } public boolean mouseExit(Event evt, int x, int y) { hiButton.setLabel(“Stay Away!”); return true; } public boolean action(Event evt, Object what) { if (evt.target == hiButton) { hiButton.setLabel(“Clicked!”); return true; } else

Basic Java

87

. return false; } } 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: Example using handleEvent class MyLabel extends Label { MyLabel(String label) { super(label); } public boolean mouseEnter(Event evt, int x, int y) { setText(“Not Again”); } public boolean handleEvent(Event evt) { if (Event.id == KEY_PRESS) { setText(“Keypress”); return true; } else return false; } } You would expect the mouseEnter() you had written to keep working. Unfortunately that’s not the case. Because the default handleEvent() has been overridden,

Basic Java

88

. mouseEnter() never gets called. Luckily there is an easy solution to this problem for many cases. Add the following to your handleEvent() in place of return false;: 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) ...

// Append 1 to the current number

} ... } 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: Event aEvent = new Event (target, id, obj); 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: public boolean keyDown(Event evt, int key) { if (key == 49) { // If the 1 key was pressed deliverEvent(new Event(oneKey,Event.MOUSE_DOWN, null)); return true; } ... }

Basic Java

89

. 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 AWT event types. Event type The Action event Mouse button pressed Mouse dragged Mouse entered Mouse exited Mouse button released Mouse moved Key pressed Key released

Event ID’s ACTION_EVENT MOUSE_DOWN MOUSE_DRAG MOUSE_ENTER MOUSE_EXIT MOUSE_UP MOUSE_MOVE KEY_PRESS KEY_RELEASE

Dealing with Focus When a user clicks a user interface component, that item becomes in a sense “selected”. This is as known as the input focus. For instance, when a text field is clicked on, the user then can type in the field because it has the input focus. When a component receives the input focus, the gotFocus() method of that component is called, as follows: public boolean gotFocus(Event evt, Object what) { ... } When a component loses the input focus, the lostFocus() method of that component is called, as follows: public boolean lostFocus(Event evt, Object what) { ... } It is not uncommon for a program to desire to keep the focus. For example, if a textentry 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: public void requestFocus() { ... } This could be placed in the container that the text field has been used in and would bar that field from receiving the focus.

Basic Java

90

. Event Handling in JDK1.1 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 Example for using ActionListener import import import

java.awt.Button; java.applet.Applet; java.awt.event.*;

public class ButtonDelegateTest extends Applet { public void init() { Button b = new Button("I have a listener!"); add(b); b.addActionListener(listener); } public void actionPerformed(ActionEvent e) { System.out.println("Listener here: clicked."); }

the

button

was

} The java.awt.event.ActionListener interface public interface ActionListener extends EventListener The listener interface for receiving action events. Methods public abstract void actionPerformed(ActionEvent e) Invoked when an action occurs. Example for using AdjustmentListener import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class AdjustmentEventTest extends Applet implements AdjustmentListener { public void init() { Basic Java

91

. setLayout(new BorderLayout()); // 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); } public void processAdjustmentEvent (AdjustmentEvent e) { System.out.println("Scrollbar #2: " + e.getValue()); } } Example for using FocusEvent import java.applet.Applet; import java.awt.*; import java.awt.event.*;

public class FocusEventTest extends Applet implements FocusListener { public void init() { setLayout(new BorderLayout()); // A textfield that delegates to the applet. TextField tf = new TextField(); tf.addFocusListener(this); add(tf, "North"); // A subclass that handles its own focus events SelfTextArea sta = new SelfTextArea(); add(sta, "Center"); } public void focusGained(FocusEvent e) { System.out.println("Text Field gained focus"); } public void focusLost(FocusEvent e) { System.out.println("Text Field lost focus"); }

Basic Java

92

. }

class SelfTextArea extends TextArea { public SelfTextArea() { enableEvents(AWTEvent.FOCUS_EVENT_MASK); } public void processFocusEvent(FocusEvent e) { if (e.getId() == FocusEvent.FOCUS_GAINED) System.out.println("Text Area gained focus"); else System.out.println("Text Area lost focus"); } } The java.awt.event.FocusListener interface The listener interface for receiving keyboard focus events on a component. public abstract void focusGained(FocusEvent e) public abstract void focusLost(FocusEvent e)

Example for using ItemListener import java.applet.Applet; import java.awt.*; import java.awt.event.*;

public class ItemEventTest extends Applet implements ItemListener { List list; SelfChoice sc; public void init() { // A list that delegates to the applet. list = new List(5, false); list.addItem("Chocolate"); list.addItem("Vanilla"); list.addItem("Strawberry"); list.addItem("Mocha"); list.addItem("Peppermint Swirl"); list.addItem("Blackberry Ripple"); list.addItem("Butterscotch"); list.addItem("Spumoni"); list.addItemListener(this); add(list); // A choice subclass that handles its own item events sc = new SelfChoice(); sc.addItem("Ice Cream"); sc.addItem("Frozen Yogurt"); sc.addItem("Sorbet"); Basic Java

93

. add(sc); } public void itemStateChanged(ItemEvent e) { System.out.println("New item from list:" + list.getSelectedItem()); } }

class SelfChoice extends Choice { public SelfChoice() { enableEvents(AWTEvent.ITEM_EVENT_MASK); } public void processItemEvent(ItemEvent e) { System.out.println("New item from choice: " + getSelectedItem()); } } The java.awt.event.ItemListener interface public interface ItemListener extends EventListener The listener interface for receiving item events. public abstract void itemStateChanged(ItemEvent e) Example for using KeyListener import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class KeyEventTest extends Applet implements KeyListener { public void init() { setLayout(new BorderLayout()); // A text field that delegates to the applet. TextField tf = new TextField(); tf.addKeyListener(this); add(tf, "North"); // A text area subclass that handles its own item events SelfKeyTextArea sta = new SelfKeyTextArea(); add(sta, "Center"); } public void keyTyped(KeyEvent e) { System.out.println("Key typed in text field: " + e.getKeyChar()); }

Basic Java

94

. public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent e) { } }

class SelfKeyTextArea extends TextArea { public SelfKeyTextArea() { enableEvents(AWTEvent.KEY_EVENT_MASK); } public void processKeyEvent(KeyEvent e) { if (e.getId() == KeyEvent.KEY_TYPED) System.out.println("Key typed in text area: " + e.getKeyChar()); } } The java.awt.event.KeyListener interface public interface KeyListener extends EventListener The listener interface for receiving keyboard events.

public abstract void keyTyped(KeyEvent e) Invoked when a key has been typed. This event occurs when a key press is followed by a key release. public abstract void keyPressed(KeyEvent e) Invoked when a key has been pressed. public abstract void keyReleased(KeyEvent e) Invoked when a key has been released. Example for using Mouse & MouseMotion Listeners import java.applet.Applet; import java.awt.*; import java.awt.event.*;

public class MouseEventTest extends Applet implements MouseListener, MouseMotionListener { public void init() { setLayout(new GridLayout(2, 1)); // A canvas that delegates to the applet. Canvas can1 = new Canvas(); can1.setBackground(Color.yellow); can1.addMouseListener(this); can1.addMouseMotionListener(this); add(can1); // A canvas subclass that handles its own item events SelfMouseCanvas can2 = new SelfMouseCanvas(); add(can2); } Basic Java

95

. public void mousePressed(MouseEvent e) { System.out.println("UPPER: mouse pressed at " + e.getX() + "," + e.getY()); } public void mouseReleased(MouseEvent e) { System.out.println("UPPER: mouse released at " + e.getX() + "," + e.getY()); } public void mouseEntered(MouseEvent e) { System.out.println("UPPER: mouse entered"); } public compiler public public public }

void mouseExited(MouseEvent e) void mouseClicked(MouseEvent e) void mouseMoved(MouseEvent e) void mouseDragged(MouseEvent e)

{ } { } { } { }

// Satisfy

// Ditto // Ditto // Ditto

class SelfMouseCanvas extends Canvas { public SelfMouseCanvas() { setBackground(Color.green); enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); } public void processMouseEvent(MouseEvent e) { if (e.getId() == MouseEvent.MOUSE_PRESSED) System.out.println("LOWER: mouse pressed at " + e.getX() + "," + e.getY()); else if (e.getId() == MouseEvent.MOUSE_RELEASED) System.out.println("LOWER: mouse released at " + e.getX() + "," + e.getY()); else if (e.getId() == MouseEvent.MOUSE_ENTERED) System.out.println("LOWER: mouse entered"); } } The java.awt.event.MouseListener interface public interface MouseListener extends EventListener The listener interface for receiving mouse events on a component. public abstract void mouseClicked(MouseEvent e) Invoked when the mouse has been clicked on a component. public abstract void mousePressed(MouseEvent e) Invoked when a mouse button has been pressed on a component. public abstract void mouseReleased(MouseEvent e) Invoked when a mouse button has been released on a component.

Basic Java

96

. public abstract void mouseEntered(MouseEvent e) Invoked when the mouse enters a component. public abstract void mouseExited(MouseEvent e) Invoked when the mouse exits a component. The java.awt.event.MouseMotionListener interface public interface MouseMotionListener extends EventListener The listener interface for receiving mouse motion events on a component. public abstract void mouseDragged(MouseEvent e) Invoked when a mouse button is pressed on a component and then dragged. Mouse drag events will continue to be delivered to the component where the first originated until the mouse button is released (regardless of whether the mouse position is within the bounds of the component). public abstract void mouseMoved(MouseEvent e) Invoked when the mouse button has been moved on a component (with no buttons no down). Example for using TextListener import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class TextEventTest extends Applet implements TextListener { public void init() { setLayout(new GridLayout(2, 1)); // A text area that delegates to the applet. TextArea ta1 = new TextArea(); ta1.addTextListener(this); add(ta1); // A text area subclass that handles its own item events SelfTextTA ta2 = new SelfTextTA(); add(ta2); } public void textValueChanged(TextEvent e) { System.out.println("UPPER get text event: " + e); } } class SelfTextTA extends TextArea { public SelfTextTA() { enableEvents(AWTEvent.TEXT_EVENT_MASK); } public void processTextEvent(TextEvent e) { System.out.println("LOWER get text event: " + e); } } Basic Java

97

.

The java.awt.event.TextListener interface public interface TextListener extends EventListener The listener interface for receiving adjustment events. public abstract void textValueChanged(TextEvent e) Invoked when the value of the text has changed.

The java.awt.event.WindowListener interface public interface WindowListener extends EventListener The listener interface for receiving window events. public abstract void windowOpened(WindowEvent e) Invoked when a window has been opened. public abstract void windowClosing(WindowEvent e) Invoked when a window is in the process of being closed. The close operation can be overridden at this point. public abstract void windowClosed(WindowEvent e) Invoked when a window has been closed. public abstract void windowIconified(WindowEvent e) Invoked when a window is iconified. public abstract void windowDeiconified(WindowEvent e) Invoked when a window is deiconified. public abstract void windowActivated(WindowEvent e) Invoked when a window is activated. public abstract void windowDeactivated(WindowEvent e) Invoked when a window is deactivated.

AWT LAYOUTS The java.awt.FlowLayout class public class FlowLayout extends Object implements LayoutManager, Serializable A flow layout arranges components in a left to right flow, much like lines of text in a paragraph. Flow layouts are typically used to arrange buttons in a panel. It will arrange buttons left to right until no more buttons fit on the same line. Each line is centered.

Basic Java

98

. Example for FlowLayout import java.awt.*; import java.applet.Applet; public class myButtons extends Applet { Button button1, button2, button3; FlowLayout flow; public void init() { flow = new FlowLayout(FlowLayout.CENTER); setLayout(flow); button1 = new Button("Ok"); button2 = new Button("Open"); button3 = new Button("Close"); add(button1); add(button2); add(button3); } } A flow layout lets each component assume its natural (preferred) size. The java.awt.BorderLayout class public class BorderLayout extends Object implements LayoutManager2, Serializable 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: Panel p = new Panel(); p.setLayout(new BorderLayout()); p.add(new Button("Okay"), "South"); As a convenience, BorderLayout interprets the absence of a string specification the same as "Center": Panel p2 = new Panel(); p2.setLayout(new BorderLayout()); p2.add(new TextArea()); // Same as p.add(new TextArea(), "Center"); 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: Example for using BorderLayout import java.awt.*; import java.applet.Applet; public class buttonDir extends Applet { public void init() {

Basic Java

99

. 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")); } } The java.awt.BorderLayout class public class BorderLayout extends Object implements LayoutManager2, Serializable 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: Panel p = new Panel(); p.setLayout(new BorderLayout()); p.add(new Button("Okay"), "South"); As a convenience, BorderLayout interprets the absence of a string specification the same as "Center": Panel p2 = new Panel(); p2.setLayout(new BorderLayout()); p2.add(new TextArea()); // Same as p.add(new TextArea(), "Center"); 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: Example for using BorderLayout 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")); } } The java.awt.GridLayout class public class GridLayout extends Object implements LayoutManager,

Basic Java

100

. Serializable The GridLayout class is a layout manager that lays out a container's components in a rectangular grid. 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: Example for GridLayout 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")); } }

The java.awt.GridbagLayout class public class GridBagLayout extends Object implements LayoutManager2, Serializable The GridBagLayout class is a flexible layout manager that aligns components vertically and horizontally, without requiring that the components be of the same size. Each GridBagLayout object maintains a dynamic rectangular grid of cells, with each component occupying one or more cells, called its display area. Each component managed by a grid bag layout is associated with an instance of GridBagConstraints that specifies how the component is laid out within its display area. How a GridBagLayout object places a set of components depends on the GridBagConstraints object associated with each component, and on the minimum size and the preferred size of the components' containers. 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

Basic Java

101

. 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. 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.

Basic Java

102

.

Example for GridbagLayout 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); } }

Basic Java

103

.

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. JFC 1.2 consists of five major packages: 9 9 9 9 9

Swing Pluggable Look-and-feel(PL&F) Drag and Drop Accessibility 2D

Swing 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 Swing

AWT

Basic Java

Drag and Drop Accessibility

104

. 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. The Swing packages available are : 9 javax.swing 9 javax.swing.border 9 javax.swing.text 9 javax.swing.table 9 javax.swing.event 9 javax.swing.undo

9 javax.swing.plaf etc The First Swing Program: 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, panel.setBorder (BorderFactory.createTitledBorder("Border Example"));

Basic Java

105

. creates a border with title "Border Example" Other Borders 9 MatteBorder 9 LineBorder 9 EtchedBorder 9 EmptyBorder 9 BevelBorder 9 SoftBevelBorder 9 CompoundBorder Example of using Other Borders ---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); --displays a ImageButton with the image and the label

Basic Java

106

. Example Using JProgressBar import import import import public

javax.swing.*; java.awt.*; javax.swing.border.*; java.awt.event.*; class UsingProgressBar extends JFrame implements ActionListener,Runnable {

JButton start; JProgressBar progress; int count = 0; Thread t = null; public UsingProgressBar(String title) { //code for adding components t = new Thread(this); } public void actionPerformed(ActionEvent event) t.start(); } public void run() { while(true) progress.setValue(++count); }

{

Example using JTree

//code for class & constructor DefaultMutableTreeNode main = new DefaultMutableTreeNode("MDC Futura"); DefaultMutableTreeNode first = new DefaultMutableTreeNode("COMXpert"); DefaultMutableTreeNode second = new DefaultMutableTreeNode("CORBAXpert"); DefaultMutableTreeNode a_first = new DefaultMutableTreeNode("ASP"); DefaultMutableTreeNode b_first = new DefaultMutableTreeNode("COM"); DefaultMutableTreeNode a_second = new DefaultMutableTreeNode("JAVA"); DefaultMutableTreeNode b_second = new DefaultMutableTreeNode("CORBA"); first.add(a_first); first.add(b_first); second.add(a_second); second.add(b_second); main.add(first); main.add(second); TreeModel model = new DefaultTreeModel(main); JTree tree = new JTree(model);

Basic Java

107

. //rest of code This displays a tree structure as follows:

Using JTable

//code Vector Vector Vector

for class & constructor row = new Vector(); data = new Vector(); column = new Vector();

row.addElement("COMXpert"); row.addElement("ASP & COM"); data.addElement(row); row = new Vector(); row.addElement("CORBAXpert"); row.addElement("JAVA & CORBA"); data.addElement(row); row = new Vector(); row.addElement("WEBXpert"); row.addElement("COM & CORBA"); data.addElement(row); column.addElement("Course Name"); column.addElement("Course Contents"); JTable table = new JTable(data,column); //code to add the table

Basic Java

108

. 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 Creating Custom Cursor The code block, --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.

Basic Java

109

. 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. Threads: What They Are and Why You Need Them 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

Basic Java

110

. (such as an animation loop, or a bit of code that takes a long time to execute), put it in a thread. 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.

Preemptive Versus Non-preemptive 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. Non-preemptive scheduling is very courtly, always asking for permission to schedule, and is quite valuable in extremely time-critical, real-time applications where being interrupted at the wrong moment, or for too long, could mean crashing an airplane. Most modern schedulers use preemptive time slicing, because except for a few timecritical cases, it has turned out to make writing multithreaded programs much easier. For one thing, it does not force each thread to decide exactly when it should “yield” control to another thread. Instead, every thread can just run blindly on, knowing that the scheduler will be fair about giving all the other threads their chance to run. It turns out that this approach is still not the ideal way to schedule threads. You’ve given a little too much control to the scheduler. The final touch many modern schedulers add is to enable you to assign each thread a priority. This creates a total ordering of all threads, making some threads more “important” than others. Being higher priority often means that a thread gets run more often (or gets more total running time), but it always means that it can interrupt other, lower-priority threads, even before their “time-slice” has expired. If you're going to depend on the priority of your threads, make sure that you test the application on both a Windows and Macintosh or UNIX machine. 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.

Basic Java

111

. Writing Applets with Threads 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: public class MyAppletClass extends java.applet.Applet implements Runnable { …... } What does this do? It includes support for the Runnable interface in your applet. Here, the Runnable interface includes the behavior your applet needs to run a thread; in particular, it gives you a default definition for the run() method. Before proceeding further, let’s get on to the details of Runnable interface The java.lang.Runnable interface 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.

public abstract void run() When an object implementing interface Runnable is used to create a thread, starting the thread causes the object's run method to be called in that separately executing thread.

Step 2:

Basic Java

112

. 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:

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() { // what your applet actually does } 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; } }

The stop() method here does two things: • •

it stops the thread from executing sets the thread’s variable (runner) to 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

Basic Java

113

. 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. 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 java.lang.Thread Class A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently. Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon. When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon. When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class). The Java Virtual Machine continues to execute threads until either of the following occurs:

Basic Java

114

. • •

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 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: class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }

The following code would then create a thread and start it running: PrimeThread p = new PrimeThread(143); p.start(); 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: class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }

The following code would then create a thread and start it running: PrimeRun p = new PrimeRun(143); new Thread(p).start(); 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.

Basic Java

115

. 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. An example illustrating this method being used follows: 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++; } } }

public Thread(Runnable target) Allocates a new Thread object. This constructor has the same effect as Thread(null, target, gname), where gname is a newly generated name. Automatically generated names are of the form "Thread-"+n, where n is an integer. public Thread(ThreadGroup group, Runnable target) Allocates a new Thread object. This constructor has the same effect as Thread(group, target, gname), where gname is a newly generated name. Automatically generated names are of the form "Thread-"+n, where n is an integer. public Thread(String name) Basic Java

116

. Allocates a new Thread object. This constructor has the same effect as Thread(null, null, name). public Thread(ThreadGroup group, String name) Allocates a new Thread object. This constructor has the same effect as Thread(group, null, name) public Thread(Runnable target,String name) Allocates a new Thread object. This constructor has the same effect as Thread(null, target, name). public Thread(ThreadGroup group,Runnable target, String name) Allocates a new Thread object so that it has target as its run object, has the specified name as its name, and belongs to the thread group referred to by group. If group is not null, the checkAccess method of that thread group is called with no arguments; this may result in throwing a SecurityException; if group is null, the new process belongs to the same group as the thread that is creating the new thread. If the target argument is not null, the run method of the target is called when this thread is started. If the target argument is null, this thread's run method is called when this thread is started. The priority of the newly created thread is set equal to the priority of the thread creating it, that is, the currently running thread. The method setPriority may be used to change the priority to a new value. The newly created thread is initially marked as being a daemon thread if and only if the thread creating it is currently marked as a daemon thread. The method setDaemon may be used to change whether or not a thread is a daemon. Example for using Thread Class class FirstThread extends Thread { public FirstThread(String name) { super(name); } public void run() { for(int count = 0;count<25;count++) { System.out.println("count : "+count); if(count == 10) { System.out.println("First Thread state"); try { Thread.sleep(3000); } catch(Exception err){} } }

Basic Java

in

sleep

117

. } } class SecondThread extends Thread { public SecondThread(String name) { super(name); } public void run() { for(int index = 0;index<25;index++) { System.out.println("***"); } } }

public class ThreadExample{public static void main(String args[]) { FirstThread first = new FirstThread("first"); SecondThread second = new SecondThread("second"); System.out.println("Number of active threads : " + (Thread.activeCount())); System.out.println("Name of current thread : " +((Thread.currentThread()).getName( ))); first.start(); second.start(); } } 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.

Basic Java

118

. Two synchronized methods. public synchronized void setVar(int){ myVar=x; } public synchronized int getVar (){ return myVar; }

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. The java.lang.ThreadGroup Class 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 public ThreadGroup(String name) Constructs a new thread group. The parent of this new group is the thread group of the currently running thread. public ThreadGroup(ThreadGroup parent,String name) Creates a new thread group. The parent of this new group is the specified thread group. The checkAccess method of the parent thread group is called with no arguments; this may result in a security exception.

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:

Basic Java

119

. • 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. The java.lang.ThreadDeath Class 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.

Basic Java

120

. CHAPTER – 7: Networking in Java The Java execution environment is designed so that applications can be easily written to efficiently communicate and share processing with remote systems. Much of this functionality is provided with the standard Java API within the java.net package. 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. Internet Protocol (IP) 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. IP defines a universal-addressing scheme called IP addresses. An IP address is a 32-bit number and each standard address is unique on the Internet. Given an IP packet, the information can be routed to the destination based upon the IP address defined in the packet header. IP addresses are generally written as four numbers, between 0 and 255, separated by period. (for example, 124.148.157.6) While a 32-bit number is an appropriate way to address systems for computers, humans understandably have difficulty remembering them. Thus, a system called the Domain Name System (DNS) was developed to map IP addresses to more intuitive identifiers and vice-versa. You can use www.netspace.org instead of 128.148.157.6. 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. Java provides a class to work with IP Addresses, InetAddress.

Basic Java

121

. 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.



Connection-oriented. TCP sets up a connection with a remote system by transmitting control information, often known as a handshake, before beginning a communication. At the end of the connect, a similar closing handshake ends the transmission.



Continuous-stream. TCP provides a communications medium that allows for an arbitrary number of bytes to be sent and received smoothly; once a connection has been established, TCP segments provide the application layer the appearance of a continuous flow of data.

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. User Datagram Protocol (UDP) UDP is a low-overhead alternative to TCP for host-to-host communications. In contrast to TCP, UDP has the following features: • Unreliable. UDP has no mechanism for detecting errors nor retransmitting lost or corrupted information. • Connectionless. UDP does not negotiate a connection before transmitting data. Information is sent with the assumption that the recipient will be listening. • Message-oriented. UDP allows applications to send self-contained messages within UDP datagrams, the unit of UDP transmission. The application must package all information within individual datagrams. 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

Basic Java

122

. 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.

Uniform Resource Locator (URL) While IP addresses uniquely identify systems on the Internet, and ports identify TCP or UDP services on a system, URLs provide a universal identification scheme at the application level. Anyone who has used a Web browser is familiar with seeing URLs, though their complete syntax may not be self-evident. URLs were developed to create a common format of identifying resources on the Web, but they were designed to be general enough so as to encompass applications that predated the Web by decades. Similarly, the URL syntax is flexible enough so as to accommodate future protocols. URL Syntax The primary classification of URLs is the scheme, which usually corresponds to an application protocol. Schemes include http, ftp, telnet, and gopher. The rest of the URL syntax is in a format that depends upon the scheme. These two portions of information are separated by a colon to give us: scheme-name:scheme-info Thus, while mailto:[email protected] indicates "send mail to user dwb at the machine netspace.org," ftp://[email protected]/ means "open an FTP connection to netspace.org and log in as user dwb."

General URL Format 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. Java and URLs Java provides a very powerful and elegant mechanism for creating network client applications allowing you to use relatively few statements to obtain resources from the Internet. The java.net package contains the sources of this power, the URL and URLConnection classes.

Basic Java

123

. THE URL CLASS Class URL represents a Uniform Resource Locator, a pointer to a "resource" on the World Wide Web. A resource can be something as simple as a file or a directory, or it can be a reference to a more complicated object, such as a query to a database or to a search engine. More information on the types of URLs and their formats can be found at: http://www.ncsa.uiuc.edu/demoweb/url-primer.html 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 A URL may have appended to it an "anchor", also known as a "ref" or a "reference". The anchor is indicated by the sharp sign character "#" followed by more characters. For example, 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. For example, if the contents of the URL: http://java.sun.com/index.html contained within it the relative URL: FAQ.html it would be a shorthand for: http://java.sun.com/FAQ.html 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.

Basic Java

124

. Example for URL GetURLApp.java import java.net.URL; import java.net.MalformedURLException; import java.io.*; public class GetURLApp { public static void main(String args[]) { try { if(args.length!=1) error("Usage: java GetURLApp URL"); System.out.println("Fetching URL: "+args[0]); URL url = new URL(args[0]); BufferedReader inStream = new BufferedReader( new InputStreamReader(url.openStream())); String line; while ((line = inStream.readLine())!= null) { System.out.println(line); } inStream.close(); } catch (MalformedURLException ex) { error("Bad URL"); } catch (IOException ex) { error("IOException occurred."); } } public static void error(String s){ System.out.println(s); System.exit(1); } } THE URLCONNECTION CLASS 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() Manipulate parameters that affect the connection to the remote resource. Interact with the resource; query header fields and contents.

Basic Java

125

. 1. The connection object is created by invoking the openConnection method on a URL. 2. The setup parameters and general request properties are manipulated. 3. The actual connection to the remote object is made, using the connect method. 4. The remote object becomes available. The header fields and the contents of the remote object can be accessed. The setup parameters are modified using the following methods: • setAllowUserInteraction • setDoInput • setDoOutput • setIfModifiedSince • setUseCaches and the general request properties are modified using the method: * setRequestProperty Default values for the AllowUserInteraction and UseCaches parameters can be set using the methods setDefaultAllowUserInteraction and setDefaultUseCaches. Default values for general request properties can be set using the setDefaultRequestProperty method. Each of the above set methods has a corresponding get method to retrieve the value of the parameter or general request property. The specific parameters and general request properties that are applicable are protocol specific. The following methods are used to access the header fields and the contents after the connection is made to the remote object: * getContent * getHeaderField * getInputStream * getOutputStream Certain header fields are accessed frequently. The methods: * getContentEncoding * getContentLength * getContentType * getDate * getExpiration * getLastModified provide convenient access to these fields. The getContentType method is used by the getContent method to determine the type of the remote object; subclasses may find it convenient to override the getContentType method. In the common case, all of the pre-connection parameters and general request properties can be ignored: the pre-connection parameters and request properties default to sensible values. For most clients of this interface, there are only two interesting methods: getInputStream and getObject, which are mirrored in the URL class by convenience methods. More information on the request properties and header fields of an http connection can be found at: http://www.w3.org/hypertext/WWW/Protocols/HTTP1.0/draft-ietf-http-spec.html

Basic Java

126

. TCP Socket Basics Sockets were originally developed at the University of California at Berkeley as a tool to easily accomplish network programming. Originally part of UNIX operating systems, the concept of sockets has been incorporated into a wide variety of operating environments, including Java.

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: • • • •

The local system's IP address The TCP port number which the local application is using The remote system's IP address The TCP port number to which the remote application is responding

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. Well-known TCP ports and services Port 21 23 25 79 80

Service FTP Telnet SMTP (Internet Mail Transfer) Finger 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

Basic Java

127

. different systems and/or different ports; the uniqueness of the link's characteristics are preserved. JAVA TCP SOCKET CLASSES 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. THE SERVERSOCKET CLASS public class ServerSocket extends Object 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. Example for ServerSocket ServerExample.java import java.io.*; import java.net.*; import java.util.Date; public class ServerExample { public static void main(String args[]) { ServerSocket server = null; Socket socket = null; BufferedOutputStream send = null; try { server = new ServerSocket(3000); System.out.println("server started"); while(true) { socket = server.accept(); send = BufferedOutputStream(socket.getOutputStream()); String date = (new Date()).toString(); byte data[] = new byte[date.length()]; data = date.getBytes(); send.write(data,0,data.length); send.flush(); System.out.println("data flushed"); send.close(); socket.close(); } } Basic Java

new

128

. catch(Exception err) { System.out.println("Exception client"); } } }

in

transferring

data

to

THE SOCKET CLASS 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. Example for Socket import java.io.*; import java.net.*; public class ClientExample { public static void main(String args[]) { Socket socket = null; BufferedInputStream receive = null; if(args.length == 0){ System.out.println("Usage : java ClientExample <server IP address>"); System.exit(0); } String ser_address = args[0]; try { socket = new Socket(InetAddress.getByName(ser_address),3000); receive = new BufferedInputStream(socket.getInputStream()); System.out.println("socket created"); byte data[] = new byte[100]; receive.read(data,0,data.length); String date = new String(data); System.out.println("Date from server : "+date); receive.close(); socket.close(); } catch(Exception err){ System.out.println("Exception in accessing file"); } } }

Overview of UDP Messaging Programming with UDP has significant ramifications. Understanding these factors will inform your network programming. UDP is a good choice for applications in which communications can be separated into discrete messages, where a single query from a client invokes a single response from a server. Time-dependent data is particularly suited to UDP. UDP requires much less overhead, but the burden of

Basic Java

129

. engineering any necessary reliability into the system is your responsibility. For instance, if clients never receive responses to their queries—perfectly possible and legitimate with UDP—you might want to program the clients to retransmit the request or perhaps display an informative message indicating communication difficulties. UDP Socket Characteristics UDP is described as unreliable, connectionless, and message-oriented. A common analogy that elucidates UDP is that of communicating with postcards. A dialog with 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 metainformation. Java UDP Classes The java.net package has the tools that are necessary to perform UDP communications. For working with datagrams, Java provides the DatagramPacket and DatagramSocket classes. When receiving a UDP datagram, you also use the DatagramPacket class to read the data, sender, and meta-information. THE DATAGRAMPACKET CLASS 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.

Basic Java

130

. Example for DatagramPacket import java.net.*; import java.io.*; public class DatagramClient { public static void main(String args[]) { if(args.length == 0) { 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"); } } } THE DATAGRAMSOCKET CLASS 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.

Basic Java

131

. CHAPTER – 8: Java Database Connectivity 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.

Basic Java

132

. What Does JDBC Do? Simply put, JDBC makes it possible to do three things: • • •

establish a connection with a database send SQL statements process the results.

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. Basic Java

133

. JDBC versus ODBC and other APIs At this point, Microsoft's ODBC (Open DataBase Connectivity) API is probably the most widely used programming interface for accessing relational databases. It offers the ability to connect to almost all databases on almost all platforms. So why not just use ODBC from Java? The answer is that you can use ODBC from Java, but this is best done with the help of JDBC in the form of the JDBC-ODBC Bridge, which we will cover shortly. The question now becomes, "Why do you need JDBC?" There are several answers to this question: 1. ODBC is not appropriate for direct use from Java because it uses a C interface. Calls from Java to native C code have a number of drawbacks in the security, implementation, robustness, and automatic portability of applications. 2. A literal translation of the ODBC C API into a Java API would not be desirable. For example, Java has no pointers, and ODBC makes copious use of them, including the notoriously error-prone generic pointer "void *". You can think of JDBC as ODBC translated into an object-oriented interface that is natural for Java programmers. 3. ODBC is hard to learn. It mixes simple and advanced features together, and it has complex options even for simple queries. JDBC, on the other hand, was designed to keep simple things simple while allowing more advanced capabilities where required. 4. A Java API like JDBC is needed in order to enable a "pure Java" solution. When ODBC is used, the ODBC driver manager and drivers must be manually installed on every client machine. When the JDBC driver is written completely in Java, however, JDBC code is automatically installable, portable, and secure on all Java platforms from network computers to mainframes. In summary, the JDBC API is a natural Java interface to the basic SQL abstractions and concepts. It builds on ODBC rather than starting from scratch, so programmers familiar with ODBC will find it very easy to learn JDBC. JDBC retains the basic design features of ODBC; in fact, both interfaces are based on the X/Open SQL CLI (Call Level Interface). The big difference is that JDBC builds on and reinforces the style and virtues of Java, and, of course, it is easy to use. More recently, Microsoft has introduced new APIs beyond ODBC: RDO, ADO, and OLE DB. These designs move in the same direction as JDBC in many ways, that is, in being an object-oriented database interface based on classes that can be implemented on ODBC. However, we did not see compelling functionality in any of these interfaces to make them an alternative basis to ODBC, especially with the ODBC driver market well-established. Mostly they represent a thin veneer on ODBC. This is not to say that JDBC does not need to evolve from the initial release; however, we feel that most new functionality belongs in higher- level APIs such as the object/relational mappings and embedded SQL mentioned in the previous section.

Basic Java

134

.

Two-tier and Three-tier Models The JDBC API supports both two-tier and three-tier models for database access. In the two-tier model, a Java applet or application talks directly to the database. This requires a JDBC driver that can communicate with the particular database management system being accessed. A user's SQL statements are delivered to the database, and the results of those statements are sent back to the user. The 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 DBMS Proprietary Protocol

DBMS

Database Server

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

Basic Java

135

. 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. 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 ODBCstyle 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 COMPLIANTTM" 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

Basic Java

136

. 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. 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 DBMSindependent 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.

Basic Java

137

. 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: DRIVER CATEGORY 1 – JDBC-OCBC Bridge 2 – Native API as basis 3 – JDBC-Net 4 – Native protocol as basis

ALL JAVA? No No Yes Yes

NET PROTOCOL Direct Direct Requires Connector Direct

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");

Basic Java

138

. 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

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

Basic Java

139

. 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. 7. <subname>-a way to identify the database. The subname can vary, depending on the subprotocol, and it can have a subsubname with any internal syntax the driver writer chooses. The point of a subname is to give enough information to locate the database. In the previous example, "fred" is enough because ODBC provides the remainder of the information. A database on a remote server requires more information, however. If the database is to be accessed over the Internet, for example, the network 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 Supposing that "dbnet" is a protocol for connecting to a host on the Internet, a JDBC URL might look like this: 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:[;=]* Basic Java

140

. 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 JDBCODBC 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: [email protected] Sending SQL Statements Once a connection is established, it is used to pass SQL statements to its underlying database. JDBC does not put any restrictions on the kinds of SQL statements that can be sent; this provides a great deal of flexibility, allowing the use of databasespecific statements or even non-SQL statements. It requires, however, that the user be responsible for making sure that the underlying database can process the SQL statements being sent and suffer the consequences if it cannot. For example, an application that tries to send a stored procedure call to a DBMS that does not support stored procedures will be unsuccessful and generate an exception. JDBC requires that a driver provide at least ANSI SQL-2 Entry Level capabilities in order to be designated JDBC COMPLIANTTM. This means that users can count on at least this standard level of functionality. JDBC provides three classes for sending SQL statements to the database, and three methods in the Connection interface create instances of these classes. These classes and the methods which create them are listed below: 5. Statement- -created by the method createStatement. A Statement object is used for sending simple SQL statements. 6. PreparedStatement- -created by the method prepareStatement. A PreparedStatement object is used for SQL statements that take one or more parameters as input arguments (IN parameters). PreparedStatement has a group of methods which set the value of IN parameters, which are sent to the 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: Basic Java

141

. createStatement method is used for •

simple SQL statements (no parameters)

prepareStatement method is used for •

SQL statements with one or more IN parameters



simple SQL statements that are executed frequently

prepareCall method is used for •

call to stored procedures

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 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 Basic Java

142

. 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: Basic Java

143

. 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-togeneric-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.

Basic Java

144

. 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: 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

Basic Java

145

. 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 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.

Basic Java

146

. 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 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

Basic Java

147

. 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; } // if we have gotten this far, we have either a result set // or no more results 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 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

Basic Java

148

. 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: java.sql.Statement stmt = conn.createStatement(); ResultSet r = stmt.executeQuery("SELECT a, b, c FROM Table1"); while (r.next()) { // print the values for the current row. int i = r.getInt("a"); String s = r.getString("b"); float f = r.getFloat("c"); System.out.println("ROW = " + i + " " + s + " " + f); }

Rows and Cursors A ResultSet maintains a cursor which points to its current row of data. The cursor moves down one row each time the method next is called. Initially it is positioned before the first row, so that the first call to next puts the cursor on the first row, making it the current row. ResultSet rows are retrieved in sequence from the top row down as the cursor moves down one row with each successive call to next. A cursor remains valid until the ResultSet object or its parent Statement object is closed. In SQL, the cursor for a result table is named. If a database allows positioned updates or positioned deletes, the name of the cursor needs to be supplied as a parameter to the update or delete command. This cursor name can be obtained by calling the method getCursorName. Note that not all DBMSs support positioned update and delete. The DatabaseMetaData.supportsPositionedDelete and supportsPositionedUpdate methods can be used to discover whether a particular connection supports these operations. When they are supported, the DBMS/driver must ensure that rows selected are properly locked so that positioned updates do not result in update anomalies or other concurrency problems.

Columns The getXXX methods provide the means for retrieving column values from the current row. Within each row, column values may be retrieved in any order, but for maximum portability, one should retrieve values from left to right and read column values only once. 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);

Basic Java

149

. 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.

Basic Java

150

.

N B C V L B V L U I H A O I A O M T A R N N R N R C G A B G E H V R I V R A A Y N A I A R R R C R B C Y I H N A A R R Y X x x x x x x x x x x x x x X x x x x x x x x x x x x x X x x x x x x x x x x x x x X x x x x x x x x x x x x x X x x x x x x x x x x x x x X X x x x x x x x x x x x x x X X x x x x x x x x x x x x x X x x x x x x x x x x x x x X X x x x x X X x x x x x x x x x x x x X x x x x x X x x x x x X x x x x x x x x x x x x x x x x T I N Y I N T

getByte getShort getInt getLong getFloat getDouble getBigDecimal getBoolean getString getBytes getDate getTime getTimestamp getAsciiStream getUnicodeStream getBinaryStream GetObject

S M A L L I N T

I N T E G E R

B R F D I E L O G A O U L A B T L N E T

D E C I M A L

D A T E

T I M E

T I M E S T A M P

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.

Basic Java

151

x

. •

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()) { 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.

Basic Java

152

. 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 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.

Basic Java

153

. 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(); }

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

Basic Java

154

. 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. 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.

Basic Java

155

. 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( "{call getTestData(?, ?)}"); Whether the ? placeholders are IN, OUT, or INOUT parameters depends on the stored procedure getTestData.

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,

Basic Java

156

. 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);

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.

Basic Java

157

. 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 if you need exact definitions of the behavior of the various SQL types on a particular database.

Basic Java

158

. 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: String query = "select * from Table1"; ResultSet rs = stmt.executeQuery(query); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); for (int i = 1; i <= columnCount; i++) { String s = rsmd.getColumnTypeName(i); System.out.println ("Column " + i + " is type " + s); }

Basic Java

159

. SQL Statement with IN Parameters In another possible scenario, the user sends an SQL statement which takes input parameters. In this case, the user calls the PreparedStatement.setXXX methods to assign a value to each input parameter. For example, PreparedStatement.setLong(1, 2345678) will assign the value 2345678 to the first parameter as a Java long. The 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

Basic Java

160

. 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); 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

Basic Java

161

. •

java.sql.Types.OTHER (used as CallableStatement.registerOutParameter)

an

argument

to

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 systems, some vendors may find it convenient to use getObject for retrieving these types. Tables for Data Type Mapping Java type JDBC type CHAR VARCHAR LONGVARCHAR NUMERIC DECIMAL BIT TINYINT SMALLINT INTEGER BIGINT REAL FLOAT DOUBLE BINARY VARBINARY LONGVARBINARY DATE TIME TIMESTAMP

Basic Java

String String String java.math.BigDecimal java.math.BigDecimal boolean byte short int long float double double byte[] byte[] byte[] java.sql.Date java.sql.Time java.sql.Timestamp

162

. Java Types Mapped to JDBC Types Java Type String java.math.BigDecimal boolean byte short int long float double byte[] java.sql.Date java.sql.Time java.sql.Timestamp

JDBC type VARCHAR or LONGVARCHAR NUMERIC BIT TINYINT SMALLINT INTEGER BIGINT REAL DOUBLE VARBINARY or LONGVARBINARY DATE TIME 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: JDBC Type CHAR VARCHAR LONGVARCHAR NUMERIC DECIMAL BIT TINYINT SMALLINT INTEGER BIGINT REAL FLOAT DOUBLE BINARY VARBINARY LONGVARBINARY DATE TIME

Basic Java

Java Object Type String String String java.math.BigDecimal java.math.BigDecimal Boolean Integer Integer Integer Long Float Double Double byte[] byte[] byte[] java.sql.Date java.sql.Time

163

. TIMESTAMP

java.sql.Timestamp

Java Object Types Mapped to JDBC Types Java Object Type String java.math.BigDecimal Boolean Integer Long Float Double byte[] java.sql.Date java.sql.Time java.sql.Timestamp

JDBC Type VARCHAR or LONGVARCHAR NUMERIC BIT INTEGER BIGINT REAL DOUBLE VARBINARY or LONGVARBINARY DATE TIME 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.

String java.math.BigDecimal Boolean Integer Long Float Double byte[] java.sql.Date java.sql.Time java.sql.Time- stamp

Basic Java

T I N Y I N T

S M A L L I N T

I N T E G E R

B I G I N T

R E A L

F L O A T

D O U B L E

D E C I M A L

x x x x x x x

x x x x x x x

x x x x x x x

x x x x x x x

x x x x x x x

x x x x x x x

x x x x x x x

x x x x x x x

N B C V L B V L U I H A O I A O M T A R N N R N R C G A B G E H V R I V R A A Y N A I A R R R C R B C Y I H N A A R R Y x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x

D A T E

T I M E

T I M E S T A M P

x x x

x

x

x x x x

164

. JDBC Types Retrieved by ResultSet.getXXX Methods An "x" means that the method can retrieve method is recommended for the JDBC type. T S I B R I M N I E N A T G A Y L E I L I L G N N I E T T N R T

getByte getShort getInt getLong getFloat getDouble getBigDecimal getBoolean getString getBytes getDate getTime getTimestamp getAsciiStream getUnicodeStream getBinaryStream getObject

Basic Java

the JDBC type. An "X" means that the

N B C V L B V L U I H A O I A O M T A R N N R N R C G A B G E H V R I V R A A Y N A I A R R R C R B C Y I H N A A R R Y X x x x x x x x x x x x x x X x x x x x x x x x x x x x X x x x x x x x x x x x x x X x x x x x x x x x x x x x X x x x x x x x x x x x x x X X x x x x x x x x x x x x x X X x x x x x x x x x x x x x X x x x x x x x x x x x x x X X x x x x X X x x x x x x x x x x x x X x x x x x X x x x x x X x x x x x x x x x x x x x x x x F L O A T

D O U B L E

D E C I M A L

D A T E

T I M E

T I M E S T A M P

x

x

x

X x

x X x X

x

x

165

x

. 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. checkForWarning (con.getWarnings ()); // Get the DatabaseMetaData object and display // some information about the connection DatabaseMetaData dma = con.getMetaData (); System.out.println("\nConnected to " + dma.getURL()); System.out.println("Driver " + System.out.println("Version +dma.getDriverVersion()); System.out.println("");

Basic Java

dma.getDrive "

166

. // Create a Statement object so we can submit // SQL statements to the driver Statement stmt = con.createStatement (); // Submit a query, creating a ResultSet object ResultSet rs = stmt.executeQuery (query); // Display all columns and rows from the result set dispResultSet (rs); // Close the result set rs.close(); // Close the statement stmt.close(); // Close the connection 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 // existed

Basic Java

167

. //-----------------------------------------------------------------private static boolean throws SQLException { boolean rc = false;

checkForWarning

(SQLWarning

warn)

// If a SQLWarning object was given, display the // warning messages. Note that there could be // multiple warnings chained together 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 //-----------------------------------------------------------------private static void dispResultSet (ResultSet rs) throws SQLException { int i; // Get the ResultSetMetaData. This will be used for // the column headings ResultSetMetaData rsmd = rs.getMetaData (); // Get the number of columns in the result set int numCols = rsmd.getColumnCount (); // Display column headings for (i=1; i<=numCols; i++) {

Basic Java

168

. if (i > 1) System.out.print(","); System.out.print(rsmd.getColumnLabel(i)); } System.out.println(""); // Display data, fetching until end of the result set boolean more = rs.next (); while (more) { // Loop through each column, getting the // column data and displaying for (i=1; i<=numCols; i++) { if (i > 1) System.out.print(","); System.out.print(rs.getString(i)); } System.out.println(""); // Fetch the next result set row more = rs.next (); } } }

JDBC-ODBC Bridge Driver If possible, use a Pure Java JDBC driver instead of the Bridge and an ODBC driver. This completely eliminates the client configuration required by ODBC. It also eliminates the potential that the Java VM could be corrupted by an error in the native code brought in by the Bridge (that is, the Bridge native library, the ODBC driver manager library, the ODBC driver library, and the database client library).

What Is the JDBC-ODBC Bridge? The JDBC-ODBC Bridge is a JDBC driver which implements JDBC operations by translating them into ODBC operations. To ODBC it appears as a normal application program. The Bridge implements JDBC for any database for which an ODBC driver is available. The Bridge is implemented as the sun.jdbc.odbc Java package and contains a native library used to access ODBC. The Bridge is a joint development of Intersolv and JavaSoft.

What Version of ODBC Is Supported? The bridge supports ODBC 2.x. This is the version that most ODBC drivers currently support. It will also likely work with most forthcoming ODBC 3.x drivers; however, this has not been tested.

Basic Java

169

. The Bridge Implementation The Bridge is implemented in Java and uses Java native methods to call ODBC.

Installation The Bridge is installed automatically with the JDK as package sun.jdbc.odbc. See your ODBC driver vendor for information on installing and configuring ODBC. No special configuration is required for the Bridge. See your database vendor for client installation and configuration information. On Solaris, some ODBC driver managers name their libs libodbcinst.so and libodbc.so. The Bridge expects these libraries to be 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

Basic Java

170

. 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.

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:[=]* For example: jdbc:odbc:sybase jdbc:odbc:mydb;UID=me;PWD=secret jdbc:odbc:ora123;Cachesize=300

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.

Basic Java

171

Related Documents

Basic Java
November 2019 17
Basic Java
November 2019 22
Basic Java Chapter 3
June 2020 4
Java Basic Book 2
October 2019 24
Basic Java Refresher
November 2019 7