1
Advanced Java
2
Preface
There are numerous good computer languages. There are relatively few languages with the momentum to make a real difference in software development. Java is one of those languages. The developers of Java had a chance to look at existing computer languages and address their deficiencies. Coming ten years after the introduction of C++, Java takes advantages of vast improvements in hardware and software technologies to address the issues raised by C++. Java allows inexperienced users to write high quality code. It incorporates user interface directly in the language. Java and Object-Oriented technology are a major paradigm shift. This course aims to introduce some advanced topics of java to enable the students to appreciate the power of these technologies. This course exposes the student to Advanced Java features such as Network Programming, Servlet Programming, the Java Database Connectivity, Remote Method Invocation, and Swing.
Unit 1 gives you a quick introduction to Java programming. It tells you what Java is and provides you with an opportunity to compile and run some simple Java programs. The unit gives you the background knowledge you need to understand how the advanced java programs work. Unit 2 gives an introduction to java GUI programming. Applets, AWT and Swings are covered by this unit. Unit 3 covers the networking and connectivity topics of java such as Networking, RMI, JDBC and Servlets. This course gives you an introduction to all the basic and advanced topics of Java. Each of these topics, by them selves are large enough to become an independent course. Students are encouraged to explore each of these interesting topics in much more detail.
The Essay on Advanced Topics In Management Accounting And Control
The purpose of this paper is to analyze the economic situation of the company Macedonian Shipping and give a recommendation whether the company should use the motor vessel Tashtego as a freight tender beween Dar-es-Salaam and Zanzibar in East Africa or as a tapioca ship between Balik Papan and Singapore in the East Indies. Fundamental to all these considerations are measurement issues. Financial ...
3
BASIC JAVA
1.1 INTRODUCTION
The Java programming language originated as part of a research project to develop advanced software for a wide variety of network devices and embedded systems. The goal was to develop a small, reliable, portable, distributed, real-time operating platform. When the project started, C++ was the language of choice. But over time the difficulties encountered with C++ grew to the point where the problems could best be addressed by creating an entirely new language platform. Design and architecture decisions drew from a variety of languages such as Eiffel, SmallTalk, Objective C, and Cedar/Mesa. The result is a language platform that has proven ideal for developing secure, distributed, network-based end-user applications in environments ranging from network-embedded devices to the World-Wide Web and the desktop. The Java programming language is designed to meet the challenges of application development in the context of heterogeneous, network-wide distributed environments. Java technology is both a programming language and a platform.
1.2
OBJECTIVES
What is Java? What are the features of Java Java Language Syntax Object Oriented Features of Java What are Exceptions? Java Input/Output Multithreading
In this unit you will learn
4
1.3
INTRODUCTION TO JAVA
The Java Programming Language
The Java programming language is a high-level language that can be
characterized by all of the following buzzwords:
Simple Object oriented Distributed Interpreted Robust Secure
Architecture neutral Portable High performance Multithreaded Dynamic
Simple, Object Oriented, and Familiar Primary characteristics of the Java programming language include a simple language that can be programmed without extensive programmer training. The Java programming language is designed to be object oriented from the ground up. The needs of distributed, client-server based systems coincide with the encapsulated, message-passing paradigms of object-based software. To function within increasingly complex, network-based
The Term Paper on Modeling Web Applications Using Java And Xml Related Technologies
... Java (Process Definition for Java/JPD) * Those that are programming language independent (BPEL4WS) For Java- ... economically. The integration platform's management environment must provide a comprehensive operational ... are now including application server technology within their next-generation integration ... of service for passing those business objects between applications (e.g., security, guaranteed ...
environments, programming systems must adopt object-oriented concepts. Java technology provides a clean and efficient object-based development platform. Java language has retained the familiar good features of C and C++ and has eliminated the most of the inefficient features of theses languages.
5 Robust and Secure The Java programming language is designed for creating highly reliable software. It provides extensive compile-time checking, followed by a second level of run-time checking. Language features guide programmers towards reliable programming habits.
The memory management model is extremely simple: objects are created with a new operator. There are no explicit programmer-defined pointer data types, no pointer arithmetic, and automatic garbage collection. Java technology is designed to operate in distributed environments, which means that security is of paramount importance. With security features designed into the language and run-time system, Java technology lets you construct applications that can’t be invaded from outside. In the network environment, applications written in the Java programming language are secure from intrusion by unauthorized code attempting to get behind the scenes and create viruses or invade file systems.
Architecture Neutral and Portable Java technology is designed to support applications that will be deployed into heterogeneous network environments. In such environments, applications must be capable of executing on a variety of hardware architectures. Within this variety of hardware platforms, applications must execute atop a variety of operating systems and interoperate with multiple programming language interfaces. To accommodate the diversity of operating environments, the Java Compiler product generates bytecodes–an architecture neutral intermediate format designed to transport code efficiently to multiple hardware and software platforms. The interpreted nature of Java technology solves both the binary distribution problem and the version problem; the same Java programming language byte codes will run on any platform.
Architecture neutrality is just one part of a truly portable system. Java technology takes portability a stage further by being strict in its definition of the basic language. Java technology specifies the sizes of its basic data types and the behavior of its arithmetic 6 operators. Java programs are the same on every platform–there are no data type incompatibilities across hardware and software architectures.
The Essay on C Programming
C programming for the complete newbie Hello there im Krisis you may have seen me on irc.hackersclub.com. Well I thought it was about time to write an article like everyone else. But unlike many others mine wont be on Hacking, Cracking, or Phreaking it's on C programming, you see I'm not the best hacker but I'm an ok programmer. So here it goes. This is based for absolute beginners so those of you ...
The architecture-neutral and portable language platform of Java technology is known as the Java virtual machine. It’s the specification of an abstract machine for which Java programming language compilers can generate code. Specific implementations of the Java virtual machine for specific hardware and software platforms then provide the concrete realization of the virtual machine.
High Performance Performance is always a consideration. The Java platform achieves superior performance by adopting a scheme by which the interpreter can run at full speed without needing to check the run-time environment. The automatic garbage collector runs as a lowpriority background thread, ensuring a high probability that memory is available when required, leading to better performance. Applications requiring large amounts of compute power can be designed such that compute-intensive sections can be rewritten in native machine code as required and interfaced with the Java platform.
Interpreted, Threaded, and Dynamic The Java interpreter can execute Java bytecodes directly on any machine to which the interpreter and run-time system have been ported. In an interpreted platform such as Java technology-based system, the link phase of a program is simple, incremental, and lightweight. Programmers benefit from much faster development cycles–prototyping, experimentation, and rapid development are the normal case, versus the traditional heavyweight compile, link, and test cycles.
Java technology’s multithreading capability provides the means to build applications with many concurrent threads of activity. Multithreading thus results in a high degree of 7 interactivity for the end user. The Java platform supports multithreading at the language level with the addition of sophisticated synchronization primitives: the language library provides the Thread class, and the run-time system provides monitor and condition lock primitives. At the library level, moreover, Java technology’s high-level system libraries have been written to be thread safe: the functionality provided by the libraries is available without conflict to multiple concurrent threads of execution.
The Essay on Composing Exercise Program
This is a composing exercise program. This will only work on the computer. The way this program works is very simple. All the poems from Shijing have already been stored within the application itself, and users can choose and open any poems that they want among the list. Since the Shijing has over hundreds of poems, they have been classified according to categories to facilitate the search. The ...
While the Java Compiler is strict in its compile-time static checking, the language and run-time system are dynamic in their linking stages. Classes are linked only as needed. New code modules can be linked in on demand from a variety of sources, even from sources across a network..
The Java Platform
Taken individually, the characteristics discussed above can be found in a variety of software development platforms. What’s completely new is the manner in which Java technology and its runtime environment have combined them to produce a flexible and powerful programming system.
Developing applications using the Java programming language results in
software that is portable across multiple machine architectures, operating systems, and graphical user interfaces, secure, and high performance.
With most programming languages, you either compile or interpret a program so that you can run it on your computer. The Java programming language is unusual in that a program is both compiled and interpreted. With the compiler, first you translate a program into an intermediate language called Java bytecodes —the platform-independent codes interpreted by the interpreter on the Java platform. The interpreter parses and runs each Java bytecode instruction on the computer. Compilation happens just once; interpretation occurs each time the program is executed. The following figure illustrates how this works.
8
Java bytecodes can be thought as the machine code instructions for the Java Virtual Machine (Java VM).
Every Java interpreter, whether it’s a development tool or a Web browser that can run applets, is an implementation of the Java VM. Java bytecodes help make “write once, run anywhere” possible. You can compile your program into bytecodes on any platform that has a Java compiler. The bytecodes can then be run on any implementation of the Java VM. That means that as long as a computer has a Java VM, the same program written in the Java programming language can run on Windows 2000, a Solaris workstation, or on an iMac.
The Essay on Java Programming: Mutator Method Assignment Q&A
On paper, write a method called sum with a while loop that adds up all numbers between two numbers a and b, inclusive, and returns the sum as its result. The values for a and b can be passed to the sum method as parameters. For instance: sum(1, 5) would return the value 15 (i.e., 1 + 2 + 3 + 4 + 5). What happens if the value of the second parameter is less than the value of the first? public int ...
A platform is the hardware or software environment in which a program runs. We’ve already mentioned some of the most popular platforms like Windows 2000, Linux, Solaris, and MacOS. Most platforms can be described as a combination of the operating system and hardware. The Java platform differs from most other platforms in that it’s a software-only platform that runs on top of other hardware-based platforms. The Java platform has two components: • •
The Java Virtual Machine (Java VM) The Java Application Programming Interface (Java API)
9 Java VM is the base for the Java platform and is ported onto various hardware-based platforms. The Java API is a large collection of ready-made software components that provide many useful capabilities, such as graphical user interface (GUI) widgets. The Java API is grouped into libraries of related classes and interfaces; these libraries are known as packages.
The following figure depicts a program that’s running on the Java platform. As the figure shows, the Java API and the virtual machine insulate the program from the hardware.
Native code is code that after you compile it, the compiled code runs on a specific hardware platform. As a platform-independent environment, the Java platform can be a bit slower than native code. However, smart compilers, well-tuned interpreters, and just-in-time bytecode compilers can bring performance close to that of native code without threatening portability.
First Program In Java: Hello World
10
The following instructions will help you write your first program. These instructions are for users of Win32 platforms, which include Windows 95/98 and Windows NT/2000. We start with a checklist of what you need to write your first program.
To write your first program, you need: 1. The Java 2 Platform, Standard Edition. 2. A text editor
These two items are all you need to write your first Java program. Your first program, HelloWorldApp, will simply display the greeting “Hello world!”. To create this program, you
will:
•
Create a source file. A source file contains text, written in the Java programming
language, that you and other programmers can understand. You can use any text editor to create and edit source files.
The Essay on Near Earth Objects Spacewatch Program
Near Earth Objects What are NEOs? Where do they come from? Do they pose any real threat to Earth? Can they provide viable space resources? All of these questions are now under investigation by planetary scientist. There are two highly recognized research programs that I will discuss with you. The Spaceguard program is sponsored and run by NASA Ames Space Science Division: Asteroid and Comet Impact ...
1. Start NotePad. in a new document, type in the following code: /** * The HelloWorldApp class implements an application that * displays “Hello World!” to the standard output. */ public class HelloWorldApp { public static void main(String[] args) { // Display “Hello World!” System.out.println(“Hello World!”); } }
11 Type all code, commands, and file names exactly as shown. The Java compiler and interpreter are case-sensitive, so you must capitalize consistently
2. Save this code to a file. From the menu bar, select File > Save As. In the Save As
dialog box:
o
Using the Save in drop-down menu, specify the folder (directory) where you’ll save your file. In this example, the directory is java on the C drive.
o
In the File name text box, type “HelloWorldApp.java”, including the double quotation marks.
o
From the Save as type drop-down menu, choose Text Document.
When you’re finished, the dialog box should look like this:
Now click Save, and exit NotePad.
•
Compile the source file into a bytecode file. The compiler, javac, takes your source file and translates its text into instructions that the Java Virtual Machine (Java VM) can understand. The compiler converts these instructions into a bytecode file.
From the Start menu, select the MS-DOS Prompt application (Windows 95/98) or Command Prompt application (Windows NT).
To compile your source code file, change your current directory to the directory where your file is located. For example, if your source directory is java on the C drive, you would type the following command at the prompt and press Enter: cd c:\java
12
Now the prompt should change to C:\java>. Now you can compile. At the prompt, type the following command and press Enter: javac HelloWorldApp.java
If your prompt reappears without error messages, congratulations. You have successfully compiled your program. The compiler has generated a Java bytecode file, HelloWorldApp.class. At the prompt, Error Explanation
Bad command or file name (Windows 95/98) The name specified is not recognized as an internal or external command, operable program or batch file (Windows NT) If you receive this error, Windows cannot find the Java compiler, javac. Here’s one way to tell Windows where to find javac. Suppose you installed the Java 2 Software Development Kit in C:\jdk1.3. At the prompt you would type the following command and press Enter: C:\jdk1.3\bin\javac HelloWorldApp.java Note: If you choose this option, each time you compile or run a program, you’ll have to precede your javac and java commands with
C:\jdk1.3\bin\. To avoid this extra typing, consult the section set the class path variable correctly.
type dir to see the new file that was generated:
•
Now that you have a .class file, you can run your program.
13
• Run the program contained in the bytecode file. The Java interpreter installed on your computer implements the Java VM. This interpreter takes your bytecode file and carries out the instructions by translating them into instructions that your computer can understand.
In the same directory, enter at the prompt:
java HelloWorldApp
Now you should see:
Error Explanation Exception in thread “main” java.lang.NoClassDefFoundError: HelloWorldApp
If you receive this error, java cannot find your bytecode file, HelloWorldApp.class. One of the places java tries to find your bytecode file is your current directory. So, if your bytecode file is in C:\java, you should change your current directory to that. To change your directory, type the following command at the prompt and press Enter: cd c:\java The prompt should change to C:\java>. If you enter dir at the prompt, you should see your .java and .class files. Now enter java HelloWorldApp again. If you still have problems, you might have to change your CLASSPATH variable. To see if this is necessary, try “clobbering” the classpath with the following command: set CLASSPATH= Now enter java HelloWorldApp again.
14
Now that you’ve seen a Java application (and perhaps even compiled and run it), you might be wondering how it works. This section dissects the “Hello World” application you’ve already seen. Here, again, is its code:
/** * The HelloWorldApp class implements an application that * simply displays “Hello World!” to the standard output. */ class HelloWorldApp { public static void main(String[] args) { System.out.println(“Hello World!”); //Display the string. } }
Comments in Java Code
The “Hello World” application has two blocks of comments. The first block, at the top of the program, uses /** and */ delimiters. Later, a line of code is explained with a comment that’s marked by // characters. The Java language supports a third kind of comment, as well -the familiar C-style comment, which is delimited with /* and */.
Defining a Class
In the Java language, each method (function) and variable exists within a class or an object (an instance of a class).
The Java language does not support global functions or variables. Thus, the skeleton of any Java program is a class definition.
15 A class–the basic building block of an object-oriented language such as Java–is a template that describes the data and behavior associated with instances of that class. When you instantiate a class you create an object that looks and feels like other instances of the same class. The data associated with a class or object is stored in variables; the behavior associated with a class or object is implemented with methods. Methods are similar to the functions or procedures in procedural languages such as C. In the Java language, the simplest form of a class definition is class name { … } The keyword class begins the class definition for a class named name. The variables and methods of the class are embraced by the curly brackets that begin and end the class definition block. The “Hello World” application has no variables and has a single method named main.
The main Method
The entry point of every Java application is its main method. When you run an application with the Java interpreter, you specify the name of the class that you want to run. The interpreter invokes the main method defined within that class. The main method controls the flow of the program, allocates whatever resources are needed, and runs any other methods that provide the functionality for the application. Every Java application must contain a main method whose signature looks like this: public static void main(String[] args) The method signature for the main method contains three modifiers: • • •
public indicates that the main method can be called by any object. static indicates that the main method is a class method. void indicates that the main method doesn’t return any value.
16
How the main Method Gets Called
The main method in the Java language is similar to the main function in C and C++. When the Java interpreter executes an application (by being invoked upon the application’s controlling class), it starts by calling the class’s main method. The main method then calls all the other methods required to run your application. If you try to invoke the Java interpreter on a class that does not have a main method, the interpreter refuses to run your program and displays an error message similar to this: In class NoMain: void main(String argv[]) is not defined
Arguments to the main Method
The main method accepts a single argument: an array of elements of type String. public static void main(String[] args) This array is the mechanism through which the runtime system passes information to your application. Each String in the array is called a command-line argument. Commandline arguments let users affect the operation of the application without recompiling it. The “Hello World” application ignores its command-line arguments.
Using Classes and Objects
The other components of a Java application are the supporting objects, classes, methods, and Java language statements that you write to implement the application.
The “Hello World” application is about the simplest Java program you can write that actually does something. Because it is such a simple program, it doesn’t need to define any classes except for HelloWorldApp. The “Hello World” application does use another class–the System class–that is part of the API (application programming interface) provided with the
17 Java environment. The System class provides system-independent access to system-dependent functionality. Let’s take a look at the first segment of the statement: System.out.println(“Hello World!”);
The construct System.out is the full name of the out variable in the System class. Notice that the application never instantiates the System class and that out is referred to directly from the class name. This is because out is a class variable–a variable associated with the class rather than with an instance of the class. You can also associate methods with a class–class methods.
To refer to class variables and methods, you join the class name and the name of the class method or class variable together with a period (“.”).
Using an Instance Method or Variable
Methods and variables that are not class methods or class variables are known as instance methods and instance variables. To refer to instance methods and variables, you must reference the methods and variables from an object.
While System’s out variable is a class variable, it refers to an instance of the PrintStream class (a class provided with the Java development environment) that implements the standard output stream. When the System class is loaded into the application, it instantiates PrintStream and assigns the new PrintStream object to the out class variable. Now that you have an instance of a class, you can call one of its instance methods:
System.out.println(“Hello World!”);
18 As you can see, you refer to instance methods and variables similarly to the way you refer to class methods and variables. You join an object reference (out) and the name of the instance method or variable (println) together with a period (“.”).
The Java compiler allows you to cascade references to class and instance methods and variables together, resulting in constructs like the one that appears in the sample program: System.out.println(“Hello World!”);
This line of code displays “Hello World!” to the application’s standard output stream.
Objects and Object-oriented Programming
The central concept of object-oriented programming is the object, which is a kind of module containing data and functions. The point-of-view in OOP is that an object is a kind of self-sufficient entity that has an internal state (the data it contains) and that can respond (behavior) to messages (calls to its methods).
The OOP approach to software engineering is to start by identifying the objects involved in a problem and the messages that those objects should respond to. The program that results is a collection of objects, each with its own data and its own set of responsibilities. The objects interact by sending messages to each other.
You should think of objects as “knowing” how to respond to certain messages. Different objects might respond to the same message in different ways. For example, a “print” message would produce very different results, depending on the object it is sent to. This property of objects — that different objects can respond to the same message in different ways — is called polymorphism. It is common for objects to bear a kind of “family relationship” to one another. Objects that contain the same type of data and that respond to the same messages in the same way belong to the same class. (In actual programming, the class is primary; that is, a class is created and then one or more objects are created using that class as a template.) But objects can be similar without being in exactly the same class.
19
DrawableObject, MultipointObject, and TwoPointObject would be classes in the program. MultipointObject and TwoPointObject would be subclasses of DrawableObject. The class Line would be a subclass of TwoPointObject and (indirectly) of DrawableObject. A subclass of a class is said to inherit the properties of that class. The subclass can add to its inheritance and it can even “override” part of that inheritance (by defining a different response to some method).
Nevertheless, lines, rectangles, and so on are drawable objects, and the class DrawableObject expresses this relationship.
Inheritance is a powerful means for organizing a program. It is also related to the problem of reusing software components. A class is the ultimate reusable component. Not only can it be reused directly if it fits exactly into a program you are trying to write, but if it just almost fits, you can still reuse it by defining a subclass and making only the small changes necessary to adapt it exactly to your needs.
The reusable components should be as “modular” as possible. A module is a component of a larger system that interacts with the rest of the system in a simple, welldefined, straightforward manner. The idea is that a module can be “plugged into” a system. The details of what goes on inside the module are not important to the system as a whole, as long as the module fulfills its assigned role correctly. This is called information hiding, and it is one of the most important principles of object oriented programming. One common format for software modules is to contain some data, along with some functions for manipulating that data. (ie objects) . Objects provide an interface, hiding the details of implementation, thus providing information hiding. The putting together of data and associated functions can be called as encapsulation.
20
1.4 LANGUAGE FUNDAMENTALS
Data Types
The Java type system supports a variety of primitive (built-in) data types, such as int for representing integer data, float for representing floating-point values, and others, as well as class-defined data types that exist in supporting libraries (Java packages).
All Java primitive data types have lowercase letters. The Java programming language has the following primitive types: Primitive Type Description boolean byte char short int long float Double True/false 8 bits 16 bits (UNICODE) 16 bits 32 bits 64 bits 32 bits IEEE 754-1985 64 bits IEEE 754-1985
Variable Definition and Assignment
A data definition operation specifies a data type and a variable name, and optionally, an initial value: Data Definition
21
; , , …, ; = ;
The data type may be a primitive or built-in type or a user-defined type such as Dog. The value may be a literal value or an instance of a user-defined type such as Dog. Several examples of data definitions follow: Data Definition Examples
int x; int x = 9; Boolean terminate = false; Dog dog = new Dog();
An assignment operation can occur in the following contexts: Assignment Operation
= ; ; … = ;
The data value to the right of the assignment operator can be a literal value, or any operation that produces a scalar value. Several examples follow: Assignment Example Comment
int x = 4; x = 9; temperature = 21.4; dog = new Dog();
Data definition with assignment Assumes prior definition of x Assumes prior definition of temperature Assumes prior definition of dog
22
Data Type Default Initialization Value
false 0 \u0000 0 0 0 0.0 0.0 null
Boolean byte char Short int long Float double
Arrays
An array is a linear collection of data elements, each element directly accessible via its index. The first element has index 0; the last element has index n – 1. The array has the form:
Generic Array Object
# elements element type element 0 element 1
23 … element n – 1
Java type system provides built-in, language-level syntactic support for arrays. Although language-level support for arrays increases the complexity of the language definition, it’s justified because array usage is entrenched in traditional programming. The syntax for creating an array object is:
Array Definition
[] ;
This declaration defines the array object–it does not allocate memory for the array object, nor does it allocate the elements of the array. Also, you may not specify a size within the square brackets. To allocate an array, use the new operator: int[] x = new int[5]; // array of five elements
The array x of Java primitives has the form:
new int[5]
5 int 0 0 0 0 0
Consider an array definition for a user-defined type such as Dog: Dog[] dog = new Dog[5];
24
This definition creates the array object itself, but not the elements. Subsequently, you can use the new operator to create objects in order to initialize the array elements (which are reference variables): dog[0] = new Dog(); … dog[4] = new Dog(); To create multidimensional arrays, simply create arrays of arrays, for example, T[][] t = new T[10][5];
This definition creates ten arrays of references to arrays of references for objects of type T. This definition does not allocate memory for instances of T. There is a short-hand notation for defining an array and initializing its elements in one step, using comma-separated data values between curly brackets:
Array Definition and Initialization
[] = { , , … };
The following table provides examples:
Examples of Array Definition and Initialization
int x = 4; int[] anArray = {3, x, 9, 2}; String[] seasons = {“winter”, “spring”, “summer”, “fall”};
Note that the array size is determined from the number of initializers. Accessing an undefined array element causes a runtime exception called ArrayIndexOutOfBoundsException.
25 Accessing a defined array element that has not yet been assigned to an object results in a runtime exception called NullPointerException.
Strings
String is a system-defined class–not a primitive–defined in java.lang.The lang package
also provides a complementary class, java.lang.StringBuffer. String instances are immutable; that is, they cannot be modified. To use equivalent terminology, String operations are nondestructive. A programmer simply creates strings, uses them, and when there is no further reference to them, the Java interpreter’s garbage collection facility recovers the storage space. Most of the string-oriented tasks necessary for normal programming can be accomplished with instances of String, for example, creating string constants, concatenating strings, and so on.
StringBuffer, on the other hand, is more powerful. It includes many methods for destructive string operations, for example, substring and character-level manipulation of strings such as splicing one or more characters into the middle of a string. A good rule of thumb is to use String wherever possible, and consider StringBuffer only when the functionality provided by String is inadequate. System.out.println(“x = ” + x);
This simple line of code demonstrates several string-related issues. First, note that println accepts one argument, which is satisfied by the result of the expression evaluation that includes +. In this context, + performs a string concatenation. Because + is recognized by the Java compiler as a string concatenation operator, the compiler automatically generates the code to convert any non-String operands to String instances. In this case, if x is an int with the value 5, its value will be converted, generating the string constant “5”. The latter is concatenated with “x = ” producing “x = 5”, the single argument to println.
26 Because String is a class, the general way to create a string instance is: String prompt = new String(“xyz “);
Note that you have to provide a string in order to create a string! As a convenience for the programmer, the Java programming language always recognizes a sequence of characters between double quotes as a string constant; hence, you can use the following short-cut to create a String instance and assign it to the reference variable prompt: String prompt = “x = “; String barkSound = “Woof.”;
Conditional Execution
Like other languages, the Java programming language provides constructs for conditional execution, specifically, if, switch, and the conditional operator ?. Conditional Constructs
if () … else … switch () { case : … break;
more-case-statement-break-groups…
default: … } () ?
27
:
The more general construct, if has the syntax: if () … where … can be one statement, for example, x = 4; or multiple statements grouped within curly brackets (a statement group), for example, { x = 4; y = 6; } and is any expression that evaluates to a boolean value, for example,
Boolean Expression Interpretation
X= y X != 10 x is less than 3 x is equal to y x is greater than or equal to y x is not equal to 10 variable is true
If the boolean expression evaluates to true, the statement (or statement group) following the if clause is executed. The Java programming language also supports an optional else clause; the syntax is:
if ()
28 … else …
If the boolean expression evaluates to true, the statement (or statement group) following the if clause is executed; otherwise, the statement (or statement group) following the else clause is executed. Boolean expressions often include one or more comparison operators, which are listed in the following table: Comparison Operator Interpretation
< >= == !=
less than less than or equal to greater than greater than or equal to equal to not equal to
With the Java programming language, boolean values are literals (case is significant), and boolean variables accept either value.
Iterative Execution
The Java programming language provides the while, do-while, and for constructs for iterating over a statement (or statement group) multiple times. while is the more general iterative construct; for is the more syntactically powerful.
29
Iterative Constructs
while () … Do … while (); for (…; ; …) …
Multifunction Operators
Java supports several multifunction operators described in the following table:
Multifunction Operator
++ -+= -= *= /= &= |= ^=
Interpretation
increment (by 1) decrement (by 1) increment (by specified value) decrement (by specified value) multiply (by specified value) divide (by specified value) bitwise and (with specified value) bitwise inclusive or (with specified value) bitwise exclusive or (with specified value)
30
%=
integer remainder (by specified value)
These operators are multifunction operators in the sense that they combine multiple operations: expression evaluation followed by variable assignment. The following table includes examples and interpretations:
Multifunction Operator
++ -+= -= *= /= &= |= ^= %=
Example
x++, ++x x–, –x x += y x -= y x *= y x /= y x &= y x |= y x ^= y x %= y
Pedestrian Equivalent
x=x+1 x=x-1 x=x+y x=x-y x=x*y x=x/y x=x&y x=x|y x=x^y x=x%y
Note that the Java programming language restricts bitwise operations with &, |, and ^ to integer values, which makes sense.
Comment Syntax
31 The syntax of the Java programming language supports three types of commenting, illustrated in the following table: Comment Example
int x; // a comment /* The variable x is an integer: */ int x; /** x — an integer representing the */ int x; x coordinate
Description
Remainder of line beginning with “//” is a comment area
All text between the “/*” and “*/”, inclusive, is ignored by compiler
All text between the “/**” and “*/”, inclusive, is ignored by compiler and intended for javadoc documentation utility
Reference Variable Usage
It’s common to use the term reference variable for any variable that holds a reference to dynamically allocated storage for a class instance, for example, fido in the following code: Dog fido = new Dog();
In reality, all variables in high-level languages provide a symbolic reference to a lowlevel data storage area. Consider the following code: int x; Dog fido; Each of these variables represents a data storage area that can hold one scalar value. Using x you can store an integer value such as 5 for subsequent retrieval (reference).
Using fido you can store a data value that is the low-level address (in memory) of a dynamically
allocated instance of a user-defined data type. The critical point is that, in both cases, the
32 variable “holds” a scalar value. In both cases, you can use an assignment operation to store a data value: int x = 5; int y = x; // 1. // 2. x’s value also stored in y
Dog fido = new Dog(); // 3. Dog myDog = fido; Dog spot = null; // 4. fido’s value also stored in myDog // 5.
In the second line, y is initialized with the current value of x. In the fourth line, myDog is initialized with the current value of fido. Note, however, that the value in fido is not the instance of Dog; it is the Java interpreter’s “recollection” of where (in memory) it stored the instance of Dog. Thus, you can use either reference variable to access this one instance of Dog.
With objects, the context in which you use the variable determines whether it simply evaluates to the memory address of an object or actually initiates more powerful operations. When the usage involves dot notation, for example, fido.bark, the evaluation includes binding the object to the appropriate method from the class definition, that is, invoking a method and performing the implied actions. But, when the usage is something like … = fido;, the evaluation is simply the address.
Garbage Collection
One of the really powerful features of the Java virtual machine is its memorymanagement strategy. The Java virtual machine allocates objects on the heap dynamically as requested by the new operator:
33
Other languages put the burden on the programmer to free these objects when they’re no longer needed with an operator such as delete or a library function such as free. The Java programming language does not provide this functionality for the programmer because the Java runtime environment automatically reclaims the memory for objects that are no longer associated with a reference variable. This memory reclamation process is called garbage collection.
Garbage collection involves (1) keeping a count of all references to each object and (2) periodically reclaiming memory for all objects with a reference count of zero. Garbage collection is an old computing technology; it has been used with several computing languages and with environments such as text editors. Consider the following method: … void aMethod() { Dog dog = new Dog(); // do something with the instance dog … } …
34
dog is a local variable within aMethod, allocated automatically upon method invocation. The new operation creates an instance of Dog; its memory address is stored in dog; and, its reference count is incremented to 1. When this method finishes/returns, the reference variable dog goes out of scope and the reference count is decremented to 0. At this point, the Dog instance is subject to reclamation by the garbage collector. Next, consider the following code segment: … while (true) Dog dog = new Dog(); …
Each iteration of the while loop (which continues forever) allocates a new instance of Dog, storing the reference in the variable dog and replacing the reference to the Dog instance allocated in the previous iteration. At this point, the previously allocated instance is subject to reclamation by the garbage collector.
The garbage collector automatically runs periodically. You can manually invoke the garbage collector at any time with System.gc. However, this is only a suggestion that system runs the garbage collector, not a forced execution.
Runtime Environments and Class Path Settings
The Java runtime environment dynamically loads classes upon the first reference to the class. It searches for classes based on the directories listed in the environment variable CLASSPATH. If you use an IDE, it may automatically handle CLASSPATH internally, or write a classpath setting to the appropriate system file during installation.
If you do not use an IDE, for example, if you’re using the Java 2 SDK, you may have to set a classpath before running the Java compiler and interpreter, javac and java, respectively.
35 Also, note that in some situations the installation procedure will automatically update the PATH environment variable, but if you’re unable to run javac or java, be aware that this settin could be unchanged.
PATH environment variable settings vary across operating systems and vendors. In a
Windows environment, the following setting augments the old/existing PATH setting (%PATH%) with C:\java\bin, the default installation directory for the JDK: set PATH=%PATH%;C:\java\bin
When attempting to run a Java IDE, or the Java 2 SDK’s compiler or interpreter, Windows includes the directory C:\java\bin in the search for the executable program. Of course, this setting (C:\java\bin) will vary from one developer environment to the next. Note that the path separator character is ; for Windows environments and : for UNIX environments.
If you find it necessary to set the CLASSPATH environment variable, for example, if you’re using an early JDK from Sun, it should include all directories on your computer system where you have Java class files that you want the Java compiler and interpreter to locate. As you add new class-file directories, you will typically augment this classpath setting.
Windows 9x and NT/2000 users can set classpaths manually with a text editor in the file autoexec.bat, plus Windows NT/2000 users can set a classpath via the control panel’s System dialog. UNIX users can set a classpath manually in the appropriate shell script’s configuration file. Please refer to the appropriate system reference books and documentation that describe how to set an environment variable.
1.5
CLASSES AND OBJECTS AND OTHER OOP FEATURES
Objects, Instance Methods, and Instance Variables
36 Object Oriented Programming represents an attempt to make programs more closely model the way people think about and deal with the world. In the older styles of programming, a programmer who is faced with some problem must identify a computing task that needs to be performed in order to solve the problem. Programming then consists of finding a sequence of instructions that will accomplish that task. But at the heart of objectoriented programming, instead of tasks we find objects — entities that have behaviors, that hold information, and that can interact with one another.
Programming consists of designing a set of objects that somehow model the problem at hand. Software objects in the program can represent real or abstract entities in the problem domain. This is supposed to make the design of the program more natural and hence easier to get right and easier to understand. An object-oriented programming language such as Java includes a number of features. In order to make effective use of those features, you have to “orient” your thinking correctly.
Objects are real world entities. Objects are closely related to classes. A class can contain variables and methods. Classes “describe” objects, or more exactly objects belong to classes, but this might not be much clearer. It is more exact to say that classes are used to create objects. A class is a kind of factory for constructing objects. The non-static parts of the class specify, or describe, what variables and methods the objects will contain. Objects are created and destroyed as the program runs, and there can be many objects with the same structure, if they are created using the same class.
Consider a simple class whose job is to group together a few static member variables. For example, the following class could be used to store information about the person who is using the program:
class UserData { static String name; static int age; }
37 In a program that uses this class, there is only one copy of each variable in the class, UserData.name and UserData.age. The class, UserData, and the variables it contains exist as long as
the program runs. Now, consider a similar class that includes non-static variables: class PlayerData { String name; int age; }
In this case, there is no such variable as PlayerData.name or PlayerData.age, since name and age are not static members of PlayerData. So, there is nothing much in the class at all — except
the potential to create objects. But, it’s a lot of potential, since it can be used to create any number of objects! Each of those objects will have its own variables called name and age. A program might use this class to store information about multiple players in a game. Each player has a name and an age. When a player joins the game, a new PlayerData object can be created to represent that player. If a player leaves the game, the PlayerData object that represents that player can be destroyed. A system of objects in the program is being used to dynamically model what is happening in the game. You can’t do this with “static” variables!
An object that belongs to a class is said to be an instance of that class.
The variables that the object contains are called instance variables. The functions that the object contains are called instance methods. For example, if the PlayerData class, as defined above, is used to create an object, then that object is an instance of the PlayerData class, and name and age are instance variables in the object. It is important to remember that the class of an object determines the types of the instance variables; however, the actual data is contained inside the individual objects, not the class. Thus, each object has its own set of data.
The static and the non-static portions of a class are very different things and serve very different purposes. Many classes contain only static members, or only non-static. However, it is possible to mix static and non-static members in a single class. By the way, static member variables and static member functions in a class are sometimes called class 38 variables and class methods, since they belong to the class itself, rather than to instances of that class.
Let’s look at a specific example to see how it works. Consider this extremely simplified version of a Student class, which could be used to store information about students taking a course: class Student {
String name;
// Student’s name.
double test1, test2, test3; // Grades on three tests.
double getAverage() {
// compute average test grade
return (test1 + test2 + test3) / 3; }
}
// end of class Student
None of the members of this class are declared to be static, so the class exists only for creating objects. This class definition says that any object that is an instance of the Student class will include instance variables named name, test1, test2, and test3, and it will include an instance method named getAverage().
The names and tests in different objects will generally have different values. When called for a particular student, the method getAverage() will compute an average using that student’s test grades. Different students can have different averages. (Again, this is what it means to say that an instance method belongs to an individual object, not to the class.)
In Java, a class is a type, similar to the built-in types such as int and boolean. So, a class name can be used to specify the type of a variable in a declaration statement, the type of a formal parameter, or the return type of a function. For example, a program could define a variable named std of type Student with the statement Student std;
39 However, declaring a variable does not create an object! This is an important point, which is related to this Very Important Fact:
In Java, no variable can ever hold an object. A variable can only hold a reference to an object.
You should think of objects as floating around independently in the computer’s memory. In fact, there is a portion of memory called the heap where objects live. Instead of holding an object itself, a variable holds the information necessary to find the object in memory. This information is called a reference or pointer to the object. In effect, a reference to an object is the address of the memory location where the object is stored. When you use a variable of class type, the computer uses the reference in the variable to find the actual object.
Objects are actually created by an operator called new, which creates an object and returns a reference to that object. For example, assuming that std is a variable of type Student, declared as above, the assignment statement std = new Student();
would create a new object which is an instance of the class Student, and it would store a reference to that object in the variable std. The value of the variable is a reference to the object, not the object itself. It is not quite true, then, to say that the object is the “value of the variable std”. It is certainly not at all true to say that the object is “stored in the variable std.” The proper terminology is that “the variable std refers to the object,” .
So, suppose that the variable std refers to an object belonging to the class Student. That object has instance variables name, test1, test2, and test3. These instance variables can be referred to as std.name, std.test1, std.test2, and std.test3. Similarly, std can be used to call the getAverage() instance method in the object by saying std.getAverage().
To print out the student’s
average, you could say:
System.out.println( “Your average is ” + std.getAverage() );
More generally, you could use std.name any place where a variable of type String is legal. You can use it in expressions. You can assign a value to it. You can even use it to call
40 methods from the String class. For example, std.name.length() is the number of characters in the student’s name.
It is possible for a variable like std, whose type is given by a class, to refer to no object at all. We say in this case that std holds a null reference. The null reference can be written in Java as “null”. You could assign a null reference to the variable std by saying std = null;
and you could test whether the value of std is null by testing if (std == null) . . .
If the value of a variable is null, then it is, of course, illegal to refer to instance variables or instance methods through that variable — since there is no object, and hence no instance variables to refer to. For example, if the value of the variable std is null, then it would be illegal to refer to std.test1. If your program attempts to use a null reference illegally like this, the result is an error called a null pointer exception.
Let’s look at a sequence of statements that work with objects:
Student std, std1, std2, std3; std = new Student();
// Declare four variables of type Student. // Create a new object belonging to the class Student, and object in the // variable std. // store a reference to that
std1 = new Student();
// Create a second Student object // and store a reference to // it in the variable std1.
std2 = std1;
// Copy the reference value in std1 // into the variable std2.
std3 = null;
// Store a null reference in the // variable std3.
std.name = “John Smith”; std1.name = “Mary Jones”;
// Set values of some instance variables. // (Other instance variables have default
41
// initial values of zero.)
After the computer executes these statements, the situation in the computer’s memory looks like this:
This picture shows variables as little boxes, labeled with the names of the variables. Objects are shown as boxes with round corners. When a variable contains a reference to an object, the value of that variable is shown as an arrow pointing to the object. The variable std3, with a value of null, doesn’t point anywhere. The arrows from std1 and std2 both point to
the same object. This illustrates a Very Important Point:
When one object variable is assigned to another, only a reference is copied. The object referred to is not copied.
When the assignment “std2 = std1;” was executed, no new object was created. Instead, std2 is set to refer to the same object that std1 refers to. This has some consequences that might
be surprising. For example, std1.name and std2.name refer to exactly the same variable, namely the instance variable in the object that both std1 and std2 refer to. After the string “Mary Jones” is assigned to the variable std1.name, it is also be true that the value of std2.name is “Mary Jones”. “The object is not in the variable. The variable just holds a pointer to the object.”
42 You can test objects for equality and inequality using the operators == and !=, but here again, the semantics are different from what you are used to. When you make a test “if (std1 == std2)”, you are testing whether the values stored in std1 and std2 are the same. But the
values are references to objects, not objects. So, you are testing whether
std1 and std2 refer to the same object, that is, whether they point to the same location in memory. This is fine, if its what you want to do. But sometimes, what you want to check is whether the instance variables in the objects have the same values. To do that, you would need to ask whether “std1.test1 == std2.test1 && std1.test2 == std2.test2 && std1.test3 == std3.test1 &&
std1.name.equals(std2.name)”
Suppose that a variable that refers to an object is declared to be final. This means that the value stored in the variable can never be changed, once the variable has been initialized. The value stored in the variable is a reference to the object. So the variable will continue to refer to the same object as long as the variable exists. However, this does not prevent the data in the object from changing. The variable is final, not the object. It’s perfectly legal to say final Student stu = new Student(); stu.name = “John Doe”; // Change data in the object; / The value stored in stu is not changed.
Next, suppose that obj is a variable that refers to an object. Let’s consider at what happens when obj is passed as an actual parameter to a method. The value of obj is assigned to a formal parameter in the method, and the method is executed. The method has no power to change the value stored in the variable, obj. It only has a copy of that value. However, that value is a reference to an object. Since the method has a reference to the object, it can change the data stored in the object. After the method ends, obj still points to the same object, but the data stored in the object might have changed. Suppose x is a variable of type int and stu is a variable of type Student. Compare: void dontChange(int z) { z = 42; } void change(Student s) { s.name = “Fred”; }
43
The lines: x = 17; dontChange(x); System.out.println(x); The lines: stu.name = “Jane”; change(stu); System.out.println(stu.name);
output the value 17.
output the value “Fred”.
The value of x is not changed by the subroutine, which is equivalent to z = x; z = 42;
The value of stu is not changed, but stu.name is. This is equivalent to s = stu; s.name = “Fred”;
Constructors and Object Initialization
Object types in java are very different from the primitive types. Simply declaring a variable whose type is given as a class does not automatically create an object of that class. Objects must be explicitly constructed. For the computer, the process of constructing an object means, first, finding some unused memory in the heap that can be used to hold the object and, second, filling in the object’s instance variables. As a programmer, you don’t care where in memory the object is stored, but you will usually want to exercise some control over what initial values are stored in a new object’s instance variables. In many cases, you will also want to do more complicated initialization or bookkeeping every time an object is created.
An instance variable can be assigned an initial value in its declaration, just like any other variable. For example, consider a class named PairOfDice. An object of this class will
44 represent a pair of dice. It will contain two instance variables to represent the numbers showing on the dice and an instance method for rolling the dice: public class PairOfDice {
public int die1 = 3; // Number showing on the first die. public int die2 = 4; // Number showing on the second die.
public void roll() { // Roll the dice by setting each of the dice to be // a
random number between 1 and 6. die1 = (int)(Math.random()*6) + 1; die2 = (int)(Math.random()*6) + 1; }
} // end class PairOfDice
The instance variables die1 and die2 are initialized to the values 3 and 4 respectively. These initializations are executed whenever a PairOfDice object is constructed. It’s important to understand when and how this happens. There can be many PairOfDice objects. Each time one is created, it gets its own instance variables, and the assignments “die1 = 3” and “die2 = 4” are executed to fill in the values of those variables. To make this clearer, consider a variation of the PairOfDice class: public class PairOfDice {
public int die1 = (int)(Math.random()*6) + 1; public int die2 = (int)(Math.random()*6) + 1; public void roll() { die1 = (int)(Math.random()*6) + 1; die2 = (int)(Math.random()*6) + 1; } } // end class PairOfDice
45 Here, the dice are initialized to random values, as if a new pair of dice were being thrown onto the gaming table. Since the initialization is executed for each new object, a set of random initial values will be computed for each new pair of dice. Different pairs of dice can have different initial values. For initialization of static member variables, of course, the situation is quite different. There is only one copy of a static variable, and initialization of that variable is executed just once, when the class is first loaded.
If you don’t provide any initial value for an instance variable, a default initial value is provided automatically. Objects are created with the operator, new. For example, a program that wants to use a PairOfDice object could say: PairOfDice dice; // Declare a variable of type PairOfDice. dice = new PairOfDice(); // Construct a new object and store a // reference to it in the variable.
In this example, “new PairOfDice()” is an expression that allocates memory for the object, initializes the object’s instance variables, and then returns a reference to the object. This reference is the value of the expression, and that value is stored by the assignment statement in the variable, dice. Part of this expression, “PairOfDice()”, looks like function or method call, and that is no accident. It is, in fact, a call to a special type of method called a constructor. This might puzzle you, since there is no such method in the class definition. However, every class has a constructor. If the programmer doesn’t provide one, then the system will provide a default constructor. This default constructor does nothing beyond the basics: allocate memory and initialize instance variables. If you want more than that to happen when an object is created, you can include one or more constructors in the class definition.
The definition of a constructor looks much like the definition of any other method, with three exceptions. A constructor does not have any return type (not even void).
The name of the constructor must be the same as the name of the class in which it is defined. The only
46 modifiers that can be used on a constructor definition are the access modifiers public, private, and protected. (In particular, a constructor can’t be declared static.)
However, a constructor does have a method body of the usual form, a block of statements. There are no restrictions on what statements can be used. And it can have a list of formal parameters. In fact, the ability to include parameters is one of the main reasons for using constructors. The parameters can provide data to be used in the construction of the object. For example, a constructor for the PairOfDice class could provide the values that are initially showing on the dice. Here is what the class would look like in that case:
public class PairOfDice { public int die1; // Number showing on the first die. public int die2; // Number showing on the second die.
public PairOfDice(int val1, int val2) { // Constructor. Creates a pair of dice that // are initially showing the values val1 and val2. die1 = val1; // Assign specified values die2 = val2; // } to the instance variables.
public void roll() { // Roll the dice by setting each of the dice to be // a random number between 1 and 6. die1 = (int)(Math.random()*6) + 1; die2 = (int)(Math.random()*6) + 1; }
} // end class PairOfDice
47 The constructor is declared as “public PairOfDice(int val1, int val2)…”, with no return type and with the same name as the name of the class. This is how the Java compiler recognizes a constructor. The constructor has two parameters, and values for these parameters must be provided when the constructor is called. For example, the expression “new PairOfDice(3,4)” would create a PairOfDice object in which the values of the instance variables die1 and die2 are initially 3 and 4. Of course, in a program, the value returned by the constructor should be used in some way, as in PairOfDice dice; // Declare a variable of type PairOfDice.
dice = new PairOfDice(1,1); // Let dice refer to a new PairOfDice // object that initially shows 1, 1.
Now that we’ve added a constructor to the PairOfDice class, we can no longer create an object by saying “new PairOfDice()”! The system provides a default constructor for a class only if the class definition does not already include a constructor. However, this is not a big problem, since we can add a second constructor to the class, one that has no parameters. In fact, you can have as many different constructors as you want, as long as their signatures are different, that is, as long as they have different numbers or types of formal parameters. In the PairOfDice class, we might have a constructor with no parameters which produces a pair of
dice showing random numbers:
public class PairOfDice {
public int die1; // Number showing on the first die. public int die2; // Number showing on the second die.
public PairOfDice() { // Constructor. Rolls the dice, so that they initially // show some random values. roll(); // Call the roll() method to roll the dice. }
public PairOfDice(int val1, int val2) { // Constructor. Creates a pair of dice that // are initially showing the values val1 and val2.
48
die1 = val1; // Assign specified values die2 = val2; // } to the instance variables.
public void roll() { // Roll the dice by setting each of the dice to be // a random number between 1 and 6. die1 = (int)(Math.random()*6) + 1; die2 = (int)(Math.random()*6) + 1; }
} // end class PairOfDice
Now we have the option of constructing a PairOfDice object either with “new PairOfDice()” or with “new PairOfDice(x,y)”, where x and y are int-valued expressions.
This class, once it is written, can be used in any program that needs to work with one or more pairs of dice. None of those programs will ever have to use the obscure incantation “(int)(Math.random()*6)+1”, because it’s done inside the PairOfDice class. And the programmer, having once gotten the dice-rolling thing straight will never have to worry about it again. Here, for example, is a main program that uses the PairOfDice class to count how many times two pairs of dice are rolled before the two pairs come up showing the same value: public class RollTwoPairs {
public static void main(String[] args) {
PairOfDice firstDice; // Refers to the first pair of dice. firstDice = new PairOfDice();
PairOfDice secondDice; // Refers to the second pair of dice. secondDice = new PairOfDice();
49
int countRolls; // Counts how many times the two pairs of // dice have been rolled.
int total1; int total2;
// Total showing on first pair of dice. // Total showing on second pair of dice.
countRolls = 0;
do { // Roll the two pairs of dice until totals are the same.
firstDice.roll(); // Roll the first pair of dice. total1 = firstDice.die1 + secondDice.die2; // Get total. System.out.println(“First pair comes up ” + total1);
secondDice.roll(); // Roll the first pair of dice. total2 = secondDice.die1 + secondDice.die2; // Get total. System.out.println(“Second pair comes up ” + total2);
countRolls++; // Count this roll.
System.out.println(); // Blank line.
} while (total1 != total2);
System.out.println(“It took ” + countRolls + ” rolls until the totals were
the same.”);
} // end main()
} // end class RollTwoPairs
50 Constructors are methods, but they are subroutines of a special type. Unlike other methods, a constructor can only be called using the new operator, in an expression that has the form new class-name ( parameter-list )
where the parameter-list is possibly empty. A constructor call is more complicated than an ordinary subroutine or function call. It is helpful to understand the exact steps that the computer goes through to execute a constructor call: 1. First, the computer gets a block of unused memory in the heap, large enough to hold an object of the specified type. 2. It initializes the instance variables of the object. If the declaration of an instance variable specifies an initial value, then that value is computed and stored in the instance variable. Otherwise, the default initial value is used. 3. The actual parameters in the constructor, if any, are evaluated, and the values are assigned to the formal parameters of the constructor. 4. The statements in the body of the constructor, if any, are executed. 5. A reference to the object is returned as the value of the constructor call. The end result of this is that you have a reference to a newly constructed object. You can use this reference to get at the instance variables in that object or to call its instance methods.
Garbage Collection
In Java, the destruction of objects takes place automatically. An object exists on the heap, and it can be accessed only through variables that hold references to the object. What should be done with an object if there are no variables that refer to it? Such things can happen. Consider the following two statements (though in reality, you’d never do anything like this): Student std = new Student(“John Smith”);
51
std = null;
In the first line, a reference to a newly created Student object is stored in the variable std. But in the next line, the value of std is changed, and the reference to the Student object is
gone. In fact, there are now no references whatsoever to that object stored in any variable. So there is no way for the program ever to use the object again. It might as well not exist. In fact, the memory occupied by the object should be reclaimed to be used for another purpose.
Java uses a procedure called garbage collection to reclaim memory occupied by objects that are no longer accessible to a program. It is the responsibility of the system, not the programmer, to keep track of which objects are “garbage”. In the above example, it was very easy to see that the Student object had become garbage. Usually, it’s much harder. If an object has been used for a while, there might be several references to the object stored in several variables. The object doesn’t become garbage until all those references have been dropped.
In many other programing languages, it’s the programmer’s responsibility to delete the garbage. Unfortunately, keeping track of memory usage is very error-prone, and many serious program bugs are caused by such errors. A programmer might accidently delete an object even though there are still references to that object. This is called a dangling pointer error, and it leads to problems when the program tries to access an object that is no longer there. Another type of error is a memory leak, where a programmer neglects to delete objects that are no longer in use. This can lead to filling memory with objects that are completely inaccessible, and the program might run out of memory even though, in fact, large amounts of memory are being wasted. Because Java uses garbage collection, such errors are simply impossible.
Inheritance, Polymorphism, and Abstract Classes
52 A class represents a set of objects which share the same structure and behaviors. The class determines the structure of objects by specifying variables that are contained in each instance of the class, and it determines behavior by providing the instance methods that express the behavior of the objects. However, something like this can be done in most programming languages. The central new idea in object-oriented programming — the idea that really distinguishes it from traditional programming — is to allow classes to express the similarities among objects that share some, but not all, of their structure and behavior. Such similarities can expressed using inheritance and polymorphism.
The term inheritance refers to the fact that one class can inherit part or all of its structure and behavior from another class. The class that does the inheriting is said to be a subclass of the class from which it inherits. If class B is a subclass of class A, we also say that class A is a superclass of class B. (Sometimes the terms derived class and base class are used instead of subclass and superclass.) A subclass can add to the structure and behavior that it inherits. It can also replace or modify inherited behavior (though not inherited structure).
The relationship between subclass and superclass is sometimes shown by a diagram in which the subclass is shown below, and connected to, its superclass.
In Java, when you create a new class, you can declare that it is a subclass of an existing class. If you are defining a class named “B” and you want it to be a subclass of a class named “A”, you would write class B extends A { . . // additions to, and modifications of, . // stuff inherited from class A . }
53 Several classes can be declared as subclasses of the same superclass. The subclasses, which might be referred to as “sibling classes,” share some structures and behaviors — namely, the ones they inherit from their common superclass. The superclass expresses these shared structures and behaviors. In the diagram to the left, classes B, C, and D are sibling classes.
Inheritance can also extend over several “generations” of classes. This is shown in the diagram, where class E is a subclass of class D which is itself a subclass of class A. In this case, class E is considered to be a subclass of class A, even though it is not a direct subclass. Let’s look at an example. Suppose that a program has to deal with motor vehicles, including cars, trucks, and motorcycles. (This might be a program used by a Department of Motor Vehicles to keep track of registrations.) The program could use a class named Vehicle to represent all types of vehicles. The Vehicle class could include instance variables such as registrationNumber and owner and instance methods such as transferOwnership().
These are variables and methods common to all vehicles. Three subclasses of Vehicle — Car, Truck, and Motorcycle — could then be used to hold variables and methods specific to particular types of vehicles. The Car class might add an instance variable numberOfDoors, the Truck class might have numberOfAxels, and the Motorcycle class could have a boolean variable hasSidecar. (Well, it could in theory at least, even if it might give a chuckle to the people at the Department of Motor Vehicles.) The declarations of these classes in Java program would look, in outline, like this: class Vehicle { int registrationNumber; Person owner; // (assuming that a Person class has been defined) void transferOwnership(Person newOwner) { … } … } class Car extends Vehicle { int numberOfDoors; …
54
} class Truck extends Vehicle { int numberOfAxels; … } class Motorcycle extends Vehicle { boolean hasSidecar; … }
Suppose that myCar is a variable of type Car that has been declared and initialized with the statement Car myCar = new Car();
Given this declaration, a program could to refer to myCar.numberOfDoors, since numberOfDoors is an instance variable in the class Car. But since class Car extends class Vehicle,
a car also has all the structure and behavior of a vehicle. This means that myCar.registrationNumber, myCar.owner, and myCar.transferOwnership() also
exist.
Now, in the real world, cars, trucks, and motorcycles are in fact vehicles. The same is true in a program. That is, an object of type Car or Truck or Motorcycle is automatically an object of type Vehicle. This brings us to the following Important Fact:
A variable that can hold a reference to an object of class A can also hold a reference to an object belonging to any subclass of A.
The practical effect of this in our example is that an object of type Car can be assigned to a variable of type Vehicle. That is, it would be legal to say Vehicle myVehicle = myCar;
or even
Vehicle myVehicle = new Car();
55 After either of these statements, the variable myVehicle holds a reference to a Vehicle object that happens to be an instance of the subclass, Car. The object “remembers” that it is in fact a Car, and not just a Vehicle. Information about the actual class of an object is stored as part of that object. It is even possible to test whether a given object belongs to a given class, using the instanceof operator. The test: if (myVehicle instanceof Car) …
determines whether the object referred to by myVehicle is in fact a car. On the other hand, if myVehicle is a variable of type Vehicle the assignment statement myCar = myVehicle;
would be illegal because myVehicle could potentially refer to other types of vehicles that are not cars. The computer will not allow you to assign an int value to a variable of type short, because not every int is a short. Similarly, it will not allow you to assign a value of type Vehicle to a variable of type Car because not every vehicle is a car. As in the case of ints and shorts, the solution here is to use type-casting. If, for some
reason, you happen to know that myVehicle does in fact refer to a Car, you can use the type cast (Car)myVehicle to tell the computer to treat myVehicle as if it were actually of type Car. So, you could say myCar = (Car)myVehicle;
and you could even refer to ((Car)myVehicle).numberOfDoors. As an example of how this could be used in a program, suppose that you want to print out relevant data about a vehicle. You could say: System.out.println(“Vehicle Data:”); System.out.println(“Registration number: ” + myVehicle.registrationNumber); if (myVehicle instanceof Car) { System.out.println(“Type of vehicle: Car”); Car c; c = (Car)myVehicle; System.out.println(“Number of doors: ” + c.numberOfDoors); } else if (myVehicle instanceof Truck) { System.out.println(“Type of vehicle: Truck”);
56
Truck t; t = (Truck)myVehicle; System.out.println(“Number of axels: ” + t.numberOfAxels); } else if (myVehicle instanceof Motorcycle) { System.out.println(“Type of vehicle: Motorcycle”); Motorcycle m; m = (Motorcycle)myVehicle; System.out.println(“Has a sidecar: ” + m.hasSidecar); }
Note that for object types, when the computer executes a program, it checks whether type-casts are valid. So, for example, if myVehicle refers to an object of type Truck, then the type cast (Car)myVehicle will produce an error.
As another example, consider a program that deals with shapes drawn on the screen. Let’s say that the shapes include rectangles, ovals, and roundrects of various colors.
Three classes, Rectangle, Oval, and RoundRect, could be used to represent the three types of shapes. These three classes would have a common superclass, Shape, to represent features that all three shapes have in common. The Shape class could include instance variables to represent the color, position, and size of a shape. It could include instance methods for changing the color, position, and size of a shape. Changing the color, for example, might
involve changing the value of an instance variable, and then redrawing the shape in its new color: class Shape {
Color color; // Color of the shape. (Recall that class Color // is defined in package java.awt. Assume // that this class has been imported.)
57
void setColor(Color newColor) { // Method to change the color of the shape. color = newColor; // change value of instance variable redraw(); // redraw shape, which will appear in new color }
void redraw() { // method for drawing the shape ? ? ? // what commands should go here? }
…
// more instance variables and methods
} // end of class Shape
Now, you might see a problem here with the method redraw().
The problem is that each different type of shape is drawn differently. The method setColor() can be called for any type of shape. How does the computer know which shape to draw when it executes the redraw()? Informally, we can answer the question like this: The computer executes redraw() by asking the shape to redraw itself. Every shape object knows what it has to do to redraw itself.
In practice, this means that each of the specific shape classes has its own redraw() method: class Rectangle extends Shape { void redraw() { . . . // commands for drawing a rectangle } . . . // possibly, more methods and variables } class Oval extends Shape { void redraw() { . . . // commands for drawing an oval
58
} . . . // possibly, more methods and variables } class RoundRect extends Shape { void redraw() { . . . // commands for drawing a rounded rectangle } . . . // possibly, more methods and variables }
If oneShape is a variable of type Shape, it could refer to an object of any of the types, Rectangle, Oval, or RoundRect. As a program executes, and the value of oneShape changes, it
could even refer to objects of different types at different times! Whenever the statement oneShape.redraw();
is executed, the redraw method that is actually called is the one appropriate for the type of object to which oneShape actually refers. There may be no way of telling, from looking at the text of the program, what shape this statement will draw, since it depends on the value that oneShape happens to have when the program is executed. Even more is true. Suppose the
statement is in a loop and gets executed many times. If the value of oneShape changes as the loop is executed, it is possible that the very same statement “oneShape.redraw();” will call different methods and draw different shapes as it is executed over and over. We say that the redraw() method is polymorphic. A method is polymorphic if the action performed by the
method depends on the actual type of the object to which the method is applied. Polymorphism is one of the major distinguishing features of object-oriented programming.
In object-oriented programming, calling a method is often referred to as sending a message to an object. The object responds to the message by executing the appropriate method. The statement “oneShape.redraw();” is a message to the object referred to by oneShape. Since that object knows what type of object it is, it knows how it should respond to the message. From this point of view, the computer always executes “oneShape.redraw();” in the same way: by sending a message. The response to the message depends, naturally, on who receives it. From this point of view, objects are active
entities that send and receive messages,
59 and polymorphism is a natural, even necessary, part of this view. Polymorphism just means that different objects can respond to the same message in different ways.
One of the most beautiful things about polymorphism is that it lets code that you write do things that you didn’t even conceive of, at the time you wrote it. If for some reason, you decide that you want to add beveled rectangles to the types of shapes your program can deal with, you can write a new subclass, BeveledRect, of class Shape and give it its own redraw() method. Automatically, code that we wrote
previously — such as the statement oneShape.redraw() — can now suddenly start drawing beveled rectangles, even though the beveled rectangle class didn’t exist when we wrote the statement! In the statement “oneShape.redraw();”, the redraw message is sent to the object oneShape. Look back at the method from the Shape class for changing the color of a shape:
void setColor(Color newColor) { color = newColor; // change value of instance variable redraw(); // redraw shape, which will appear in new color }
A redraw message is sent here, but which object is it sent to? Well, the setColor method is itself a message that was sent to some object. The answer is that the redraw message is sent to that same object, the one that received the setColor message. If that object is a rectangle, then it is the redraw() method from the Rectangle class that is executed. If the object is an oval, then it is the redraw() method from the Oval class. This is what you should expect, but it means that the redraw(); statement in the setColor() method does not necessarily call the redraw() method in the Shape class! The redraw() method that is executed could be in any subclass of Shape.
Again, this is not a real surprise if you think about it in the right way.
Remember that an instance method is always contained in an object. The class only contains the source code for the method. When a Rectangle object is created, it contains a redraw() method. The source
60 code for that method is in the Rectangle class. The object also contains a setColor() method. Since the Rectangle class does not define a setColor() method, the source code for the rectangle’s setColor() method comes from the superclass, Shape. But even though the source codes for the
two methods are in different classes, the methods themselves are part of the same object. When the rectangle’s setColor() method is executed and calls redraw(), the redraw() method that is executed is the one in the same object.
Whenever a Rectangle, Oval, or RoundRect object has to draw itself, it is the redraw() method in the appropriate class that is executed. This leaves open the question, What does the redraw() method in the Shape class do? How should it be defined?
The answer may be surprising: We should leave it blank! The fact is that the class Shape represents the abstract idea of a shape, and there is no way to draw such a thing. Only
particular, concrete shapes like rectangles and ovals can be drawn. So, why should there even be a redraw() method in the Shape class? Well, it has to be there, or it would be illegal to call it in the setColor() method of the Shape class, and it would be illegal to write “oneShape.redraw();”, where oneShape is a variable of type Shape. The computer would say, oneShape is a variable of type Shape and there’s no redraw() method in the Shape class.
Nevertheless the version of redraw() in the Shape class will never be called. In fact, if you think about it, there can never be any reason to construct an actual object of type Shape! You can have variables of type Shape, but the objects they refer to will always belong to one of the subclasses of Shape. We say that Shape is an abstract class. An abstract class is one that is not used to construct objects, but only as a basis for making subclasses.
An abstract class exists only to express the common properties of all its subclasses.
Similarly, we could say that the redraw() method in class Shape is an abstract method, since it is never meant to be called. In fact, there is nothing for it to do — any actual redrawing is done by redraw() methods in the subclasses of Shape. The redraw() method in Shape has to be there. But it is there only to tell the computer that all Shapes understand the redraw
61 message. As an abstract method, it exists merely to specify the common interface of all the actual, concrete versions of redraw() in the subclasses of Shape. There is no reason for the abstract redraw() in class Shape to contain any code at all.
Shape and its redraw() method are semantically abstract. You can also tell the computer,
syntactically, that they are abstract by adding the modifier “abstract” to their definitions. For an abstract method, the block of code that gives the implementation of an ordinary method is replaced by a semicolon. An implementation must be provided for the abstract method in any concrete subclass of the abstract class. Here’s what the Shape class would look like as an abstract class: abstract class Shape { Color color; // color of shape. void setColor(Color newColor) { // method to change the color of the shape color = newColor; // change value of instance variable redraw(); // redraw shape, which will appear in new color } abstract void redraw(); // abstract method — must be defined in // concrete subclasses
…
// more instance variables and methods
} // end of class Shape
Once you have done this, it becomes illegal to try to create actual objects
of type Shape, and the computer will report an error if you try to do so. In Java, every class that you declare has a superclass. If you don’t specify a superclass, then the superclass is automatically taken to be Object, a predefined class that is part of the package java.lang. (The class Object itself has no superclass, but it is the only class that has this property.) Thus, class myClass { . . .
62 is exactly equivalent to
class myClass extends Object { . . .
Every other class is, directly or indirectly, a subclass of Object. This means that any object, belonging to any class whatsoever, can be assigned to a variable of type Object. The class Object represents very general properties that are shared by all objects, belonging to any class. Object is the most abstract class of all!
The Object class actually finds a use in some cases where objects of a very general sort are being manipulated. For example, java has a standard class, java.util.Vector, that represents a list of Objects. (The Vector class is in the package java.util. If you want to use this class in a program you write, you would ordinarily use an import statement to make it possible to use the short name, Vector, instead of the full name, java.util.Vector.) The Vector class is very convenient, because a Vector can hold any number of objects, and it will grow, when necessary, as objects are added to it. Since the items in the list are of type Object, the list can actually hold objects of any type.
A program that wants to keep track of various Shapes that have been drawn on the screen can store those shapes in a Vector. Suppose that the Vector is named listOfShapes. A shape, oneShape, can be added to the end of the list by calling the instance method “listOfShapes.addElement(oneShape);”. The shape could be removed from the list with “listOfShapes.removeElement(oneShape);”. The number of shapes in the list is given by the function “listOfShapes.size()”. And it is possible to retrieve the i-th object from the list with the function call
“listOfShapes.elementAt(i)”. (Items in the list are numbered from 0 to listOfShapes.size() – 1.) However, note that this method returns an Object, not a Shape. (Of course, the people who wrote the Vector class didn’t even know about Shapes, so the method they wrote could hardly have a return type of Shape!) Since you know that the items in the list are, in fact, Shapes and not just Objects, you can type-cast the Object returned by listOfShapes.elementAt(i) to be a value of type Shape: oneShape = (Shape)listOfShapes.elementAt(i);
63 Let’s say, for example, that you want to redraw all the shapes in the list. You could do this with a simple for loop, which is lovely example of object-oriented programming and polymorphism: for (int i = 0; i < listOfShapes.size(); i++) { Shape s; // i-th element of the list, considered as a Shape s = (Shape)listOfShapes.elementAt(i); s.redraw(); }
More Details of Classes
Extending Existing Classes
In day-to-day programming, especially for programmers who are just beginning to work with objects, subclassing is used mainly in one situation. There is an existing class that can be adapted with a few changes or additions. This is much more common than designing groups of classes and subclasses from scratch. The existing class can be extended to make a subclass. The syntax for this is
class subclass-name extends existing-class-name { . . // Changes and additions. . }
(Of course, the class can optionally be declared to be public.)
Interfaces
Some object-oriented programming languages, such as C++, allow a class to extend two or more superclasses. This is called multiple inheritance. In the illustration below, for example, class E is shown as having both class A and class B as direct superclasses, while class F has three direct superclasses.
64
Such multiple inheritance is not allowed in Java. The designers of Java wanted to keep the language reasonably simple, and felt that the benefits of multiple inheritance were not worth the cost in increased complexity. However, Java does have a feature that can be used to accomplish many of the same goals as multiple inheritance: interfaces.
The interface of a Method consists of the name of the method, its return type, and the number and types of its parameters. This is the information you need to know if you want to call the method. A method also has an implementation: the block of code which defines it and which is executed when the subroutine is called.
In Java, interface is a reserved word with an additional meaning. An “interface” in Java consists of a set of subroutine interfaces, without any associated implementations. A class can implement an interface by providing an implementation for each of the subroutines specified by the interface. Here is an example of a very simple Java interface: public interface Drawable { public void draw(); }
This looks much like a class definition, except that the implementation of the method draw() is omitted. A class that implements the interface, Drawable, must provide an
implementation for this method. Of course, the class can also include other methods and variables. For example, class Line implements Drawable { public void draw() {
65
. . . // do something — presumably, draw a line } . . . // other methods and variables }
While a class can extend only one other class, it can implement any number of interfaces. In fact, a class can both extend another class and implement one
or more interfaces. So, we can have things like class FilledCircle extends Circle implements Drawable, Fillable { … }
An interface is very much like an abstract class, that is, a class that can never be used for constructing objects, but can be used as a basis for building other classes. The methods in an interface are abstract methods, which must be implemented in any concrete class that implements the interface. And as with abstract classes, even though you can’t construct an object from an interface, you can declare a variable whose type is given by the interface. For example, if Drawable is an interface, and if Line and FilledCircle are classes that implement Drawable, then you could say:
Drawable figure; // Declare a variable of type Drawable. It can // refer to any object that implements the // Drawable interface.
figure = new Line(); // figure now refers to an object of class Line figure.draw(); // calls draw() method from class Line
figure = new FilledCircle(); // Now, figure refers to an object // of class FilledCircle. figure.draw(); // calls draw() method from class FilledCircle
66 A variable of type Drawable can refer to any object of any class that implements the Drawable interface. A statement like figure.draw(), above, is legal because any such class has a draw() method.
Note that a type is something that can be used to declare variables. A type can also be used to specify the type of a parameter in a method, or the return type of a function. In Java, a type can be either a class, an interface, or one of the eight built-in primitive types. These are the only possibilities. Of these, however, only classes can be used to construct new objects.
The Special Variables this and super
A static member of a class has a simple name, which can only be used inside
the class definition. For use outside the class, it has a full name of the form class-name.simple-name. For example, “System.out” is a static member variable with simple name “out” in the class “System”. It’s always legal to use the full name of a static member, even within the class where it’s defined. Sometimes it’s even necessary, as when the simple name of a static member variable is hidden by a local variable of the same name.
Instance variables and instance methods also have simple names that can be used inside the class where the variable or method is defined. But a class does not actually contain instance variables or methods, only their source code. Actual instance variables and methods are contained in objects. To get at an instance variable or method from outside the class definition, you need a variable that refers to the object. Then the full name is of the form
variable-name.simple-name. But suppose you are writing a class definition, and you want to refer to the object that contains the instance method you are writing? Suppose you want to use a full name for an instance variable, because its simple name is hidden by a local variable?
Java provides a special, predefined variable named “this” that you can use for these purposes. The variable, this, can be used in the source code of an instance method to refer to
67 the object that contains the method. If x is an instance variable, then this.x can be used as a full name for that variable. Whenever the computer executes an instance method, it sets the variable, this, to refer to the object that contains the method. One common use of this is in constructors. For example: public class Student {
private String name; // Name of the student.
public Student(String name) { // Constructor. Create a student with specified name. this.name = name; } . . // More variables and methods. . }
In the constructor, the instance variable called name is hidden by a formal
parameter. However, the instance variable can still be referred to by its full name, this.name. In the assignment statement, the value of the formal parameter, name, is assigned to the instance variable, this.name. This is considered to be acceptable style: There is no need to dream up cute new names for formal parameters that are just used to initialize instance variables. You can use the same name for the parameter as for the instance variable.
There is another common use for this. Sometimes, when you are writing an instance method, you need to pass the object that contains the method to a subroutine, as an actual parameter. In that case, you can use this as the actual parameter. For example, if you wanted to print out a string representation of the object, you could say “System.out.println(this);”.
Java also defines another special variable, named “super”, for use in the definitions of instance methods. The variable super is for use in a subclass. Like this, super refers to the object that contains the method. But it’s forgetful. It forgets that the object belongs to the class you
68 are writing, and it remembers only that it belongs to the superclass of that class. The point is that the class can contain additions and modifications to the superclass. super doesn’t know about any of those additions and modifications. Let’s say that the class that you are writing contains an instance method named doSomething().
Consider the subroutine call statement super.doSomething().
Now, super doesn’t know anything about the doSomething() method in the
subclass. It only knows about things in the superclass, so it tries to execute a method named doSomething() from the superclass. If there is none — if the doSomething() method was an
addition rather than a modification — you’ll get a syntax error.
The reason super exists is so you can get access to things in the superclass that are hidden by things in the subclass. For example, super.x always
refers to an instance variable named x in the superclass. This can be useful for the following reason: If a class contains an instance variable with the same name as an instance variable in its superclass, then an object of that class will actually contain two variables with the same name: one defined as part of the class itself and one defined as part of the superclass. The variable in the subclass does not
replace the variable of the same name in the superclass; it merely hides it. The variable from the superclass can still be accessed, using super.
When you write a method in a subclass that has the same signature as a method in its superclass, the method from the superclass is hidden in the same way. We say that the method in the subclass overrides the method from the superclass. Again, however, super can be used to access the method from the superclass.
The major use of super is to override a method with a new method that extends the behavior of the inherited method, instead of replacing that behavior entirely. The new method can use super to call the method from the superclass, and then it can add additional code to provide additional behavior. As an example, suppose you have a PairOfDice class that includes a roll() method. Suppose that you want a subclass, GraphicalDice, to represent a pair of dice drawn on the computer screen. The roll() method in the GraphicalDice method should
69 change the values of the dice and redraw the dice to show the new values. The GraphicalDice class might look something like this: public class GraphicalDice extends PairOfDice {
public void roll() { // Roll the dice, and redraw them. super.roll(); // Call the roll method from PairOfDice. redraw(); } . . // More stuff, including definition of redraw().
. } // Call a method to draw the dice.
Constructors in Subclasses
Constructors are not inherited. That is, if you extend an existing class to make a subclass, the constructors in the superclass do not become part of the subclass. If you want constructors in the subclass, you have to define new ones from scratch. If you don’t define any constructors in the subclass, then the computer will make up a default constructor, with no parameters, for you. This could be a problem, if there is a constructor in the superclass that does a lot of necessary work. It looks like you might have to repeat all that work in the subclass! This could be a real problem if you don’t have the source code to the superclass, and don’t know how it works, or if the constructor in the superclass initializes private member variables that you don’t even have access to in the subclass!
Obviously, there has to be some fix for this, and there is. It involves the special variable, super, which was introduced in the previous subsection. As the very first statement in a constructor, you can use super to call a constructor from the superclass. The notation for this is a bit ugly and misleading, and it can only be used in this one particular circumstance: It looks like you are calling super as a subroutine (even though super is not a subroutine and you
70 can’t call constructors the same way you call other subroutines anyway).
As an example, assume that the PairOfDice class has a constructor that takes two integers as parameters. Consider a subclass: public class GraphicalDice extends PairOfDice {
public GraphicalDice() { // Constructor for this class.
super(3,4); // Call the constructor from the // PairOfDice class, with parameters 3, 4.
initializeGraphics(); // Do some initialization specific // to the GraphicalDice class. } . . // More constructors, methods, variables… . }
You can use the special variable this in exactly the same way to call another constructor in the same class. This can be useful since it can save you from
repeating the same code in several constructors.
Packages
To provide larger-scale organization, classes in Java can be grouped into packages. You can have even higher levels of grouping, since packages can also contain other packages. In fact, the entire standard Java API is implemented as one large package, which is named “java”. The java package, in turn, is made up of several other packages, and each of those packages contains a number of classes.
The java package includes several other sub-packages, such as java.io, which provides facilities for input/output, java.net, which deals with network communication, and java.applet,
71 which implements the basic functionality of applets. The most basic package is called java.lang, which includes fundamental classes such as String and Math.
Let’s say that you want to use the class java.awt.Button in a program that you are writing. One way to do this is to use the full name of the class. For example, java.awt.Button stopBttn;
to declare a variable named stopBttn whose type is java.awt.Button. Of course, this can get tiresome, so Java makes it possible to avoid using the full names of classes. If you put import java.awt.Button;
at the beginning of your program, before you start writing any class, then you can abbreviate the full name java.awt.Button to just the name of the class, Button. This would allow you to say just Button stopBttn;
to declare the variable stopBttn. (The only effect of the import statement is to allow you to use simple class names instead of full “package.class” names; you aren’t really importing anything substantial. If you leave out the import statement, you can still access the class — you just have to use
its full name.) There is a shortcut for importing all the classes from a given package. You can import all the classes from java.awt by saying import java.awt.*;
72 In fact, any Java program that uses a graphical user interface is likely to begin with this line. A program might also include lines such as “import java.net.*;” or “import java.io.*;” to get easy access to networking and input/output classes. Because the package java.lang is so fundamental, all the classes in java.lang are automatically imported into every program. It’s as if every program began with the statement “import java.lang.*;”. This is why we have been able to use the class name String instead of java.lang.String, and Math.sqrt() instead of java.lang.Math.sqrt().
It would still, however, be perfectly legal to use the longer forms of the
names.
Programmers can create new packages. Suppose that you want some classes that you are writing to be in a package named utilities. Then the source code file that defines those classes must begin with the line “package utilities;”. Any program that uses the classes should include the directive “import utilities.*;” to obtain access to all the classes in the utilities package. Unfortunately, things are a little more complicated than this. Remember that if a program uses a class, then the class must be “available” when the program is compiled and when it is executed. Exactly what this means depends on which Java environment you are using. Most commonly, classes in a package named utilities should be in a directory with the name utilities, and that directory should be located in the same place as the program that uses the classes. If a class is not specifically placed in a package, then it is put in something called the default package, which has no name.
More about Access Modifiers private
The most restrictive access level is private. A private member is accessible only to the class in which it is defined. Use this access to declare members that should only be used by the class. protected The next access level
specifier is protected, which allows the class itself, subclasses, and all classes in the same package to access the members. public
73 Any class, in any package, has access to a class’s public members. Declare public members only if such access cannot produce undesirable results if an outsider uses them. Package The package access level is what you get if you don’t explicitly set a member’s access to one of the other levels. This access level allows classes in the same package as your class to access the members. This level of access assumes that classes in the same package are trusted friends. static Java technology defines away of sharing sharing data between objects, on a class scope basis. Such data is not bound to an instance nor stored in one, but to a class. Such methods and data are declared as static. No instances are needed to use static methods or data of a class, although they can be accessed through them. final variables : You can declare a variable in any scope to be final. The value of a final variable cannot change after it has been initialized. Such variables are similar to constants in other programming languages. Ex. final int aFinalVar = 0;
The previous statement declares a final variable and initializes it, all at once. Subsequent attempts to assign a value to aFinalVar result in a compiler error. You may, if necessary, defer initialization of a final local variable. Simply declare the local variable and initialize it later, like this: final int blankfinal; … blankfinal = 0; volatile The volatile keyword is used to prevent the compiler from performing certain optimizations on a member.
1.6
JAVA EXCEPTIONS
74
An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions. Any abnormal condition that
disturbs the normal program flow while the program is in execution is an error or an exception. The errors are serious conditions from which we better avoid recovery of the program. Exceptions are mild error conditions and rather than terminating the program we can handle such exceptions and continue program execution.
• • • •
User input errors – This is some incorrect data entered by the user Device error – The hardware may have some problems which need to be reported to the user. For example, printer may be off, printer out of paper etc…
Physical limitation – Program runs out of available memory or Hard disk is full. Code errors – A method may not perform correctly. java.lang.Object
java.lang.Throwable
java.lang.Error
java.lang.Exception
java.lang.IOException
java.lang.RuntimeException
75 Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this 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.
An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are
abnormal conditions. The ThreadDeath error, though a “normal” condition, is also a subclass of Error because most applications should not try to catch it.
The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch. RuntimeException is the superclass of those exceptions that can be thrown during the normal operation of the Java Virtual Machine. A method is not required to declare in its throws clause any subclasses of RuntimeException that might be thrown during the execution of the method but not caught. IOException signals that an I/O exception of some sort has occurred. This class is the general class of exceptions produced by failed or interrupted I/O operations.
Error indicates a severe problem from which recovery is difficult, if not impossible eg. running out of memory where as RuntimeException indicates a design or implementation error. When a dynamic linking failure or some other “hard” failure in the virtual machine occurs, the virtual machine throws an Error. Typical Java programs should not catch Errors. In addition, it’s unlikely that typical Java programs will ever throw Errors either.
Runtime exceptions represent problems that are detected by the runtime system. This includes arithmetic exceptions (such as when dividing by zero), pointer exceptions (such as trying to access an object through a null reference), and indexing exceptions (such as attempting to access an array element through an index that is too large or too small).
76
Runtime exceptions can occur anywhere in a program and in a typical program can be very numerous. Typically, the cost of checking for runtime exceptions exceeds the benefit of catching or specifying them. Thus the compiler does not require that you catch or specify runtime exceptions, although you can. Because runtime exceptions are so ubiquitous and attempting to catch or specify all of them all the time would be a fruitless exercise (and a
fruitful source of unreadable and unmaintainable code), the compiler allows runtime exceptions to go uncaught and unspecified.
A method can detect and throw a RuntimeException when it’s encountered an error in the virtual machine runtime. However, it’s typically easier to just let the virtual machine detect and throw it. Normally, the methods you write should throw Exceptions, not RuntimeException.
Similarly, you create a subclass of RuntimeException when you are creating an error in the virtual machine runtime (which you probably aren’t).
Otherwise you should subclass Exception. Do not throw a runtime exception or create a subclass of RuntimeException simply because you don’t want to be bothered with specifying the exceptions your methods can throw.
Checked Exceptions
ClassNotFoundException InstantiationException Class not found Attempt to create an object of an abstract class or interface. IllegealAccessException NoSuchMethodException NoSuchFieldException InterruptedException Access to a class is denied. A requested method does not exsist. A requested field does not exsist One thread has been interrupted by another thread. CloneNotSupportedException Attempt to clone an object that does not
77 implement Cloneable interface
Checked exceptions represent useful information about the operation of a legally specified request that the caller may have had no control over and that the caller needs to be informed about–for example, the file system is now full, or the remote end has closed the connection, or the access privileges don’t allow this action.
Unchecked Exceptions
ArithmeticException NegativeArraySizeException NullPointerException IllegealArgumentException ClassCastException
Arithmetic error, such as divide by zero. Array created with a negative size. Invalid use of a null reference. Illegeal argument used to invoke a method. Invalid class cast
Runtime exceptions can occur anywhere in a program and in a typical program can be very numerous. The cost of checking for runtime exceptions often exceeds the benefit of catching or specifying them. Thus the compiler does not require that you catch or specify runtime exceptions, although you can. Checked exceptions are exceptions that are not runtime exceptions and are checked by the compiler; the compiler checks that these exceptions are caught or specified.
The Throwable Class
By convention, class Throwable and its subclasses have two constructors, one that takes no arguments and one that takes a String argument that can be used to produce an error message. 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.
78
fillInStackTrace()
Fills in the execution stack trace. This method records within this Throwable object information about the current state of the stack frames for the current thread. This method is useful when an application is re-throwing an error or exception.
fillInStackTrace()
Fills in the execution stack trace. This method records within this Throwable object information about the current state of the stack frames for the current thread. This method is useful when an application is re-throwing an error or exception.
getMessage()
Returns the errort message string of this throwable object.
Throwable fiilInStackTrace() Returns a Throwable object containing a complete stack trace. void printStackTrace() String toString() Displays the stack trace Returns a String object containing a description of the exception, called by println().
String getMessage() public class Demo { { Returns a description of the exception.
public static void main(String [] args) int i = 0;
String greetings [] = { “Hello”,”Hi”,”Bye” };
while ( i < 4 )
{
79
System.out.println(greetings[i]); i++; } } }
Note that the continuation condition, “i < A.length”, implies that the last value of i that is actually processed is greetings.length-1, which is the index of the final item in the array. It’s important to use ” Dimension d = someComponent.getSize(); int height = d.height; int width = d.width;
Note: With the Java 2 Platform, you can directly access the width and height using the getWidth() and getHeight() methods. This is more efficient, as the component doesn’t need to
create a new Dimension object. For example:
int height = someComponent.getHeight(); // Java 2 Platform only! int width = someComponent.getWidth(); // Java 2 Platform only!
If you’re using the Java 2 platform, you should only use getSize() if you really need a Dimension object! • getLocation() – Gets position of
component, relative to containing component, as a
Point.
• • • Point p = someComponent.getLocation(); int x = p.x; int y = p.y;
Note: With the Java 2 Platform, you can directly access the x and y parts of the location using getX() and getY().
This is more efficient, as the component doesn’t have to create a new Point
object. For example:
int x = someComponent.getX(); // Java 2 Platform only! int y = someComponent.getY(); // Java 2 Platform only!
If you’re using the Java 2 platform, you should only use getLocation() if you really need a Point object! • getLocationOnScreen() – Gets the position of the component relative to the upper-left
corner of the computer screen, as a Point.
133
• • • • • • • • • Point p = someComponent.getLocationOnScreen(); int x = p.x; int y = p.y; getBounds() – Gets current bounding Rectangle of component. Rectangle r = someComponent.getBounds(); int height = r.height; int width = r.width; int x = r.x; int y = r.y;
This is like a combination of calling getLocation() and getSize().
Note: If you’re using the Java 2 Platform and don’t really need a Rectangle object, you should use getX(), getY(), getWidth(), and getHeight() instead.
•
setEnabled(boolean) – Toggles the state of the component. If set to true, the component
will react to user input and appear normal. If set to false, the component
will ignore user interaction, and usually appear ghosted or grayed-out.
•
setVisible(boolean) – Toggles the visibility state of the component. If set to true, the
component will appear on the screen if it is contained in a visible container. If false, the component will not appear on the screen. Note that if a component is marked as not visible, any layout manager that is responsible for that component will usually proceed with the layout algorithm as though the component were not in the parent container! This means that making a component invisible will not simply make it disappear while reserving its space in the GUI. Making the component invisible will cause the layout of its sibling components to readjust.
•
setBackground(Color)/setForeground(Color) – Changes component background/foreground
colors.
134
• setFont(Font) – Changes font of text within a component.
Containers
A Container is a Component, so may be nested. Class Panel is the most commonlyused Panel and can be extended to partition GUIs. Class Applet is a specialized Panel for running programs within a browser.
Common Container Methods
Besides the 100-plus methods inherited from the Component class, all
Container subclasses inherit the behavior of about 50 common methods of Container (most of which just override a method of Component).
While the most common method of Container used add(), has already been briefly discussed, if you need to access the list of components within a
container, you may find the getComponentCount(), getComponents(), and getComponent(int) methods helpful.
ScrollPane
The ScrollPane container was introduced with the 1.1 release of the Java Runtime Environment (JRE) to provide a new Container with automatic scrolling of any one large Component. That large object could be anything from an image that is too big for the display area to a bunch of spreadsheet cells. All the event handling mechanisms for scrolling are managed for you. Also, there is no LayoutManager for a ScrollPane since there is only a single object within it.
The following example demonstrates the scrolling of a large image. Since an Image object is not a Component, the image must be drawn by a component such as a Canvas . import java.awt.*;
135
import java.applet.*; class ImageCanvas extends Component { private Image image; public ImageCanvas(Image i) { image = i; } public void paint(Graphics g) { if (image != null) g.drawImage(image, 0, 0, this); } }
public class ScrollingImage extends Applet { public void init() { setLayout(new BorderLayout()); ScrollPane sp = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS); Image im = getImage(getCodeBase(), “./images/SMarea.gif”); // give any local image url sp.add(new ImageCanvas(im)); add(sp, BorderLayout.CENTER); } }
Event Handling
Events
Beginning with the 1.1 version of the JRE, objects register as listeners for events. If there are no listeners when an event happens, nothing happens. If there are twenty listeners registered, each is given an opportunity to process the event, in an undefined order. With a Button, for example, activating the button notifies any registered ActionListener objects.
136 Consider SimpleButtonEvent applet which creates a Button instance and registers itself as the listener for the button’s action events:
import java.awt.*; import java.awt.event.*; import java.applet.Applet;
public class SimpleButtonEvent extends Applet implements ActionListener { private Button b;
public void init() { b = new Button(“Press me”); b.addActionListener(this); add(b); }
public void actionPerformed(ActionEvent e) { // If the target of the event was our Button // In this example, the check is not // truly necessary as we only listen to // a single button if ( e.getSource() == b ) { getGraphics().drawString(“OUCH”,20,20); } } }
Notice that any class can implement ActionListener, including, in this case, the applet itself. All listeners are always notified. If you don’t want an event to be processed further, you can call the AWTEvent.consume() method. However each listener would then need to check for consumption using the isConsumed() method. Consuming events primarily stops events from
137 being processed by the system, after every listener is notified. So, if you want to reject keyboard input from the user, you can consume() the KeyEvent. All the KeyListener implementers will still be notified, but the character will not be displayed. (Consumption only works for InputEvent and its subclasses.)
So, here is how everything works:
o Components generate subclasses of AWTEvent when something interesting happens.
o Event sources permit any class to be a listener using the addXXXListener() method,
where XXX is the event type you can listen for, for example addActionListener().
You can also remove listeners using the removeXXXListener() methods. If there is an add/removeXXXListener() pair, then the component is a source for the event when the
appropriate action happens.
o In order to be an event handler you have to implement the listener type, otherwise,
you cannot be added, ActionListener being one such type.
o Some listener types are special and require you to implement multiple methods. For
instance, if you are interested in key events, and register a KeyListener, you have to implement three methods, one for key press, one for key release, and one for both, key typed. If you only care about key typed events, it doesn’t make sense to have to stub out the other two methods. There are special classes out there called adapters that implement the listener interfaces and stub out all the methods. Then, you only need to subclass the adapter and override the necessary method(s).
AWTEvent
138 Events subclass the AWTEvent class. And nearly every event-type has an associated Listener interface, PaintEvent and InputEvent do not. (With PaintEvent, you just override paint() and update(), for InputEvent, you
listen for subclass events, since it is abstract.
Low-level Events
Low-level events represent a low-level input or window operation, like a key press, mouse movement, or window opening. The following table displays the different low-level events, and the operations that generate each event (each operation corresponds to a method of the listener interface):
ComponentEvent Hiding, moving, resizing, showing ContainerEvent FocusEvent KeyEvent MouseEvent WindowEvent Adding/removing component Getting/losing focus Pressing, releasing, or typing (both) a key Clicking, dragging, entering, exiting, moving, pressing, or releasing Iconifying, deiconifying, opening, closing, really closed, activating, deactivating
For instance, typing the letter ‘A’ on the keyboard generates three events, one for pressing, one for releasing, and one for typing. Depending upon your interests, you can do something for any of the three events.
Semantic Events
Semantic events represent interaction with a GUI component; for instance selecting a button, or changing the text of a text field. Which components generate which events is shown in the next section.
139
ActionEvent Do the command
AdjustmentEvent Value adjusted ItemEvent TextEvent State changed Text changed
Event Sources
The following table represents the different event sources. Keep in mind the object hierarchy. For instance, when Component is an event source for something, so are all its subclasses:
Semantic Events Low-Level Events
Button ComponentListener FocusListener Component KeyListener MouseListener MouseMotionListener Container Window ContainerListener WindowListener Choice Checkbox Checkbox CheckboxMenuItem List Scrollbar TextArea TextField AdjustmentListener TextListener ItemListener List MenuItem TextField ActionListener
Notice that although there is only one MouseEvent class, the listeners are spread across two interfaces. This is for performance issues. Since motion mouse events are generated more frequently, if you have no interest in them, you can ignore them more easily, without the performance hit.
140
Event Listeners
Each listener interface is paired with one event type and contains a method for each type of event the event class embodies. For instance, the KeyListener contains three methods, one for each type of event that the KeyEvent has: keyPressed(), keyReleased(), and keyTyped().
Summary of Listener interfaces and their methods
Interface ActionListener AdjustmentListener Method(s) actionPerformed(ActionEvent e) adjustmentValueChanged(AdjustmentEvent e) componentHidden(ComponentEvent e) componentMoved(ComponentEvent e) ComponentListener componentResized(ComponentEvent e) componentShown(ComponentEvent e) componentAdded(ContainerEvent e) ContainerListen er componentRemoved(ContainerEvent e) focusGained(FocusEvent e) FocusListener focusLost(FocusEvent e) ItemListener itemStateChanged(ItemEvent e) keyPressed(KeyEvent e) KeyListener keyReleased(KeyEvent e) keyTyped(KeyEvent e) MouseListener mouseClicked(MouseEvent e)
141
mouseEntered(MouseEvent e) mouseExited(MouseEvent e) mousePressed(MouseEvent e) mouseReleased(MouseEvent e) mouseDragged(MouseEvent e) MouseMotionLi stener mouseMoved(MouseEvent e) TextListener textValueChanged(TextEvent e) windowActivated(WindowEvent e) windowClosed(WindowEvent e) windowClosing(WindowEvent e) WindowListener windowDeactivated(WindowEvent e) windowDeiconified(WindowEvent e) windowIconified(WindowEvent e) windowOpened(WindowEvent e)
Event Adapters
Since the low-level event listeners have multiple methods to implement, there are event adapter classes to ease the pain. Instead of implementing the interface and stubbing out the methods you do not care about, you can subclass the appropriate adapter class and just override the one or two methods you are interested in. Since the semantic listeners only contain one method to implement, there is no need for adapter classes.
public class MyKeyAdapter extends KeyAdapter { public void keyTyped(KeyEvent e) { System.out.println(“User typed: ” + KeyEvent.getKeyText(e.getKeyCode())); }
142
}
Button Pressing Example
The following code demonstrates the basic concept a little more beyond the earlier example. There are three buttons within a Frame, their displayed labels may be internationalized so you need to preserve their purpose within a command associated with the button. Based upon which button is pressed, a different action occurs. import java.awt.*; import java.awt.event.*;
public class Activator { public static void main(String[] args) { Button b; ActionListener al = new MyActionListener(); Frame f = new Frame(“Hello Java”); f.add(b = new Button(“Hola”), BorderLayout.NORTH); b.setActionCommand(“Hello”); b.addActionListener(al); f.add(b = new Button(“Aloha”), BorderLayout.CENTER); b.addActionListener(al); f.add(b =
new Button(“Adios”), BorderLayout.SOUTH); b.setActionCommand(“Quit”); b.addActionListener(al); f.pack(); f.show(); } }
143
class MyActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { // Action Command is not necessarily label String s = e.getActionCommand(); if (s.equals(“Quit”)) { System.exit(0); } else if (s.equals(“Hello”)) { System.out.println(“Bon Jour”); } else { System.out.println(s + ” selected”); } } }
Since this is an application, you need to save the source (as Activator.java), compile it, and run it outside the browser. Also, if you wanted to avoid checking which button was selected, you can associate a different ActionListener to each button, instead of one to all. This is actually how many Integrated Development Environments (IDEs) generate their code.
Adapters Example
The following code demonstrates using an adapter as an anonymous inner class to draw a rectangle within an applet. The mouse press signifies the top left corner to draw, with the mouse release the bottom right. import java.awt.*; import java.awt.event.*;
public class Draw extends java.applet.Applet { public void init() { addMouseListener( new MouseAdapter() {
144
int savedX, savedY; public void mousePressed(MouseEvent e) { savedX = e.getX(); savedY = e.getY(); } public void mouseReleased(MouseEvent e) { Graphics g = Draw.this.getGraphics(); g.drawRect(savedX, savedY, e.getX()-savedX, e.getY()-savedY); } } ); } }
Applications and Menus GUI-based Applications
To create a window for your application, define a subclass of Frame (a Window
with a title, menubar, and border) and have the main method construct an instance of that class. Applications respond to events in the same way as applets do. The following example, BasicApplication, responds to the native window toolkit quit, or closing, operation: import java.awt.*; import java.awt.event.*;
public class BasicApplication extends Frame { public BasicApplication() { super(“BasicApplication Title”); setSize(200, 200); // add a demo component to this frame add(new Label(“Application Template…”,
145
Label.CENTER), BorderLayout.CENTER); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { setVisible(false); dispose(); System.exit(0); } }); }
public static void main(String[] args) { BasicApplication app = new BasicApplication(); app.setVisible(true); } }
Consider an application that displays the x,y location of the last mouse click and provides a button to reset the displayed x,y coordinates to 0,0: import java.awt.*; import java.awt.event.*; public class CursorFrame extends Frame { TextField a, b; Button btn; public CursorFrame() { super(“CursorFrame”); setSize(400, 200); setLayout(new FlowLayout()); add(new Label(“Click the mouse…”)); a = new TextField(“0”, 4); b = new TextField(“0”, 4); btn = new Button(“RESET”); add(a); add(b); add(btn); addMouseListener(new MouseAdapter() {
146
public void mousePressed(MouseEvent e) { a.setText(String.valueOf(e.getX())); b.setText(String.valueOf(e.getY())); } }); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { setVisible(false); dispose(); System.exit(0); } }); btn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { a.setText(“0”); b.setText(“0”); } }); } public static void main(String[] args) { CursorFrame app = new
CursorFrame(); app.setVisible(true); } }
This application provides anonymous classes to handle mouse events, application window closing events, and the action event for resetting the text fields that report mouse coordinates. When you have a very common operation, such as handling application window closing events, it often makes sense to abstract out this behavior and handle it elsewhere. In this case, it’s logical to do this by extending the existing Frame class, creating the specialization AppFrame:
147
import java.awt.*; import java.awt.event.*;
public class AppFrame extends Frame implements WindowListener { public AppFrame(String title) { super(title); addWindowListener(this); } public void windowClosing(WindowEvent e) { setVisible(false); dispose(); System.exit(0); } public void windowClosed(WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} public void windowActivated(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowOpened(WindowEvent e) {} }
AppFrame directly implements WindowListener, providing empty methods for all but one window event, namely, the window closing operation. With this definition, applications such as CursorFrame can extend AppFrame instead of Frame and avoid having to provide the anonymous class for window closing operations:
148
Applications: Dialog Boxes
A Dialog is a window that requires input from the user. Components may be added to the Dialog like any other container. Like a Frame, a Dialog is initially invisible. You must call the method setVisible() to activate the dialog box. import java.awt.*; import java.awt.event.*;
public class DialogFrame extends AppFrame { Dialog d;
public DialogFrame() { super(“DialogFrame”); setSize(200, 100); Button btn, dbtn; add(btn = new Button(“Press for Dialog Box”), BorderLayout.SOUTH); d = new Dialog(this, “Dialog Box”, false); d.setSize(150, 150); d.add(new Label(“This is the dialog box.”), BorderLayout.CENTER); d.add(dbtn = new Button(“OK”), BorderLayout.SOUTH); btn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { d.setVisible(true); } }); dbtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { d.setVisible(false); } });
149
d.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { d.setVisible(false); } }); } public static void main(String[] args) { DialogFrame app = new DialogFrame(); app.setVisible(true); } }
Again, you can define anonymous classes on the fly for: 1. Activating the dialog window from the main application’s command button. 2. Deactivating the dialog window from the dialog’s command button. 3. Deactivating the dialog window in response to a native window system’s closing operation.
Although the anonymous class functionality is quite elegant, it is inconvenient to have to repeatedly include the window-closing functionality for every dialog instance that your applications instantiate by coding and registering the anonymous window adapter class. As with AppFrame, you can define a specialization of Dialog that adds this functionality and thereafter simply use the enhanced class. For example, WMDialog provides this functionality: import java.awt.*; import java.awt.event.*; public class WMDialog extends Dialog implements WindowListener { public WMDialog(Frame ref, String title, boolean modal) { super(ref, title, modal); addWindowListener(this); }
150
public void windowClosing(WindowEvent e) { setVisible(false); } public void windowClosed(WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} public void windowActivated(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowOpened(WindowEvent e) {} }
Applications: Menus
An application can have a MenuBar object containing Menu objects that are comprised of MenuItem objects. Each MenuItem can be a string, menu, checkbox, or separator (a line across the menu).
To add menus to any Frame or subclass of Frame:
o Create a MenuBar
MenuBar mb = new MenuBar();
o Create a Menu
Menu m = new Menu(“File”);
o Create your MenuItem choices and add each to the Menu , in the order you want them
to appear, from top to bottom.
m.add(new MenuItem(“Open”)); m.addSeparator(); // add a separator
m.add(new CheckboxMenuItem(“Allow writing”)); // Create submenu Menu sub = new Menu(“Options…”);
151
sub.add(new MenuItem(“Option 1”)); m.add(sub); // add sub to File menu
o Add each Menu to the MenuBar in the order you want them to appear, from left to
right.
mb.add(m); // add File menu to bar
o Add the MenuBar to the Frame by calling the setMenuBar() method. setMenuBar(mb); // set menu bar of your Frame
The following program, MainWindow, creates an application window with a menu bar and several menus using the strategy outlined above: import java.awt.*; import java.awt.event.*;
// Make a main window with two top-level menus: File and Help. // Help has a submenu and demonstrates a few interesting menu items. public class MainWindow extends Frame { public MainWindow() { super(“Menu System Test Window”); setSize(200, 200);
// make a top level File menu FileMenu fileMenu = new FileMenu(this); // make a top level Help menu HelpMenu helpMenu = new HelpMenu(this);
// make a menu bar for this frame // and add top level menus File and Menu MenuBar mb = new MenuBar(); mb.add(fileMenu); mb.add(helpMenu); setMenuBar(mb);
152
addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { exit(); } }); } public void exit() { setVisible(false); // hide the Frame dispose(); // tell windowing system to free resources System.exit(0); // exit } public static void main(String args[]) { MainWindow w = new MainWindow(); w.setVisible(true); } } // Encapsulate the look and behavior of the File menu class FileMenu extends Menu implements ActionListener { MainWindow mw; // who owns us? public FileMenu(MainWindow m) { super(“File”); mw = m; MenuItem mi; add(mi = new MenuItem(“Open”)); mi.addActionListener(this); add(mi = new MenuItem(“Close”)); mi.addActionListener(this); add(mi = new MenuItem(“Exit”)); mi.addActionListener(this); } // respond to the Exit menu choice public void actionPerformed(ActionEvent e) { String item = e.getActionCommand();
153
if (item.equals(“Exit”)) mw.exit(); else System.out.println(“Selected FileMenu ” + item); } } // Encapsulate the look and behavior of the Help menu class HelpMenu extends Menu implements ActionListener { MainWindow mw; // who owns us? public HelpMenu(MainWindow m) { super(“Help”); mw = m; MenuItem mi; add(mi = new MenuItem(“Fundamentals”)); mi.addActionListener(this); add(mi = new MenuItem(“Advanced”)); mi.addActionListener(this); addSeparator(); add(mi = new CheckboxMenuItem(“Have Read The Manual”)); mi.addActionListener(this); add(mi = new CheckboxMenuItem(“Have Not Read The Manual”)); mi.addActionListener(this);
// make a Misc sub menu of Help menu Menu subMenu = new Menu(“Misc”); subMenu.add(mi = new MenuItem(“Help!!!”)); mi.addActionListener(this); subMenu.add(mi = new MenuItem(“Why did that happen?”)); mi.addActionListener(this); add(subMenu); } // respond to a few menu items public void actionPerformed(ActionEvent e) {
154
String item = e.getActionCommand(); if (item.equals(“Fundamentals”)) System.out.println(“Fundamentals”); else if (item.equals(“Help!!!”)) System.out.println(“Help!!!”); // etc… } }
2.5
JFC AND SWING Introduction
The GUI programming in Java 1.0 was basically done using AWT. The AWT
provides a set of primitive user interface components. These include elements such as buttons, labels and text fields. AWT also provided higher user level interface components such as lists and choice boxes, classes for creating frames and dialog boxes and classes to support menus that may be attached to a frame.
The original AWT was a peer-based toolkit, meaning that each java AWT component created a peer that was implemented in native code for the
platform on which Java Virtual Machine (JVM) was executing. The peer in turn was created in the window in which the component was displayed. The process of requiring each interface component to create its own window made the AWT classes difficult to port to different platforms. This puts a constraint on the set of components that can be displayed on different platforms. For e.g. if any component is not supported by a particular platform, then this component cannot be a sub set of AWT. AWT also contained support classes like AWT Layout Manager classes and geometry classes, which were not directly user interface components.
With Java 1.1 lightweight components were introduced in the AWT. A lightweight component is created by extending the AWT Component or Container class directly. The
155 lightweight components are not dependent on their peers in the target platforms. The earlier AWT mainly consisted of heavyweight components, which are dependent on the peers in the target platforms. With the introduction of lightweight components, pure Java lightweight component toolkits was now possible. Eliminating the requirement for native peers provided a way for the single Java implementation of a component that can be used for all targeted platforms.
Java Foundation Classes
Sun Microsystems is leveraging the technology of Netscape Communications, IBM, and Lighthouse Design (now owned by Sun) to create a set of Graphical User Interface (GUI) classes that integrate with JDK 1.1.5+, are standard with the Java 2 platform and provide a more polished look and feel than the standard AWT component set. The collection of APIs coming out of this effort, called the Java Foundation Classes (JFC), allows developers to build full-featured enterprise-ready applications. So, JFC is set of APIs for building the java GUI components. JDK 1.2 integrates JFC 1.1 as core API and adds the Java 2D and Drag and Drop APIs.
JFC is composed of five APIs: AWT, Java 2D, Accessibility, Drag and Drop, and
Swing. The AWT components refer to the AWT, as it exists in JDK versions 1.1.2 and later. Java 2D is a graphics API designed to provide a set of classes for two dimensional graphics and imaging. The Accessibility API provides assistive technologies, like screen magnifiers, speech recognition for disabled users. Drag and Drop provides interoperability between non java applications and java applications.
Swing is a new feature provided by the JFC 1.1. Actually Swing was the code name used by the JavaSoft project team (that was involved in developing swings) for the next generation of AWT. Swing extends AWT by supplying many more types of GUI components, providing 100% pure Java implementations of these components, and gives the
156 capability to change the appearance and behavior of these components on different platforms. The Swing components are 100% pure Java. This means that they don’t depend on the native window implementation to support them. Swing components are available and consistent across all platforms. Although Swing components are implemented in terms of the underlying AWT, these components do not use AWT components. In fact, all the traditional AWT components are re-implemented as Swing components.
With the use of Model View Architecture (MVC) which is implemented in Swing, you can change the look and Feel of your application. For example, you can have your application designed or running on Microsoft windows to have a look as if its running on the Unix windows platform. This feature of Swing is known as Pluggable Look and Feel (PLAF).
Swing PLAF architecture makes it easy to customize both the appearance and the behavior of any Swing control. It also comes with several predefined Look And Feel. eg. MotifPluggableLookAndFeel, windowsPluggableLookAndFeel,
MetalPluggableLookAndFeel and MacPluggableLookAndFeel.
Swing Package Overview
javax.swing
The high level swing package primarily consists of components, adapters, default component models, and interfaces for all the delegates and models.
javax.swing.border
The border package declares the Border interface and classes, which define specific border rendering styles.
javax.swing.colorchooser
The colorchooser package contains support classes for the color chooser component.
javax.swing.event .
The event package is for the Swing-specific event types and listeners. In addition to the java.awt.event types, Swing
157
components can generate their own event types.
javax.swing.filechooser .
The filechooser package contains support classes for the file chooser component
javax.swing.plaf.*
The pluggable look-and-feel (PLAF) packages contain the User Interface (UI) classes (delegates) which implement the different look-and-feel aspects for Swing components. There are also PLAF packages under the javax.swing.plaf hierarchy.
javax.swing.table
The table package contains the support interfaces and classes the Swing table
component.
javax.swing.text .
The text package contains the support classes for the Swing document framework
javax.swing.text.html.*
The text.html package contains the support classes for an HTML version 3.2 renderer and parser.
javax.swing.text.rtf
The text.rtf package contains the support classes for a basic Rich Text Format (RTF) renderer.
javax.swing.tree
The tree package contains the interfaces and classes which support the Swing tree component.
javax.swing.undo .
The undo package provides the support classes for implementing undo/redo capabilities in a GUI
javax.accessibility
The JFC Accessibility package is included with the Swing classes.
Component Hierarchy: Part 1–AWT Similar
158
Component Hierarchy: Part 2-New And Expanded Components
Jcomponent
Swing components are implemented as subclasses of the JComponent class, which inherits from the Container class. Swing components inherit the following functionality from JComponent:
Tool Tips -By specifying a string with the setToolTipText( ) method, you can provide help to users of a component. When the cursor pauses over the component, the specified string is displayed in small window that appears near the component.
159 Look and Feel -Subject to the security restrictions, you can choose the look and feel used by all Swing components by invoking the UIManager.setLookAndFeel( ) method.
Borders -Using the setBorder() method, you can specify the border that a component displays around its edges.
JFrame
The JFrame class is an extension to the AWT Frame class. An instance of the JFrame class is a heavyweight component. It creates a top-level window that can be positioned and sized independently of other windows. The JFrame instance is managed by the system window manager.
To create an instance of a JFrame class, you can write:
JFrame frame=new JFrame (“My Frame”); frame.setSize(300,300); // to give size to a frame frame.setVisible(true); // to make frame visible
It contains the default Java icon on the far left of the titlebar, title in the center, the minimize and maximize buttons, as well as a close button to the far right of the titlebar, The icon can be changed to any image by using the setIconImage method. The current image can be queried with the getIconImage method.
Using JFrame, you can to add the child to the JFrames contentPane as:
frame.getContentpane().add(child);
Note: Content Pane is a layer on some of the swing components eg. JFrame and Japplet and acts like a container for other components, when it is necessary to have a different look and feel for the same components.
160
Jinternal Frame
The JInternalFrame class provides the lightweight container that provides many of the features of a native frame, including dragging, closing, becoming an icon, resizing, title display, and support for a menu bar. The fundamental difference between the two classes is that you can add JInternalFrame instance to other frames, and a JFrame instance is a top-level window. The JInternalFrame class extends the JComponent class, while the JFrame class extends the AWT, Frame class.
Jpanel
It is a lightweight Panel object offering built-in support for double buffering. When buffering is enabled, through the constructor, all the drawing operations of components within the panel will be drawn to an off-screen drawing area prior to being drawn to the screen. The JPanel class is used in most of the examples in this section.
Icons
The second component, Icon, isn’t really a component at all. However, you can use it with almost all Swing components.An Icon is used to describe fixed-size pictures, or glyphs. Typically, you embed icons in a JButton or other JComponent. Objects that can act as icons implement the Icon interface, shown below. It contains a paintIcon() method that specifies a drawing origin. You render the picture specified in the paintIcon() method
in a rectangle whose size cannot exceed a rectangle with an origin at (x, y), a width of getIconWidth(), and a height of getIconHeight().
The Component parameter to paintIcon() is not usually used, unless you need to
specify additional information, such as a font or color.
161
public interface Icon { void paintIcon( Component c, Graphics g, int x, int y); int getIconWidth(); int getIconHeight(); }
The ImageIcon class is an implementation of Icon that creates an Icon from an Image. Icon tinyPicture = new ImageIcon(“TinyPicture.gif”);
Alternatively, the ImageIcon constructor can take an Image or URL object or byte array as its parameter, with an optional String description parameter. One nice thing about ImageIcon is it checks a cache before retrieving the image file. Swing uses ImageIcon rather than Image for two reasons: An Image loads asynchronously, creating the need to monitor the loading process (with MediaTracker).
An Image is not serializable.
In addition to using ImageIcon, you can implement the interface yourself to create your own icons: public class RedOval implements Icon { public void paintIcon (Component c, Graphics g, int x, int y) { g.setColor(Color.red); g.drawOval (x, y, getIconWidth(), getIconHeight()); } public int getIconWidth() { return 10; } public int getIconHeight() { return 10; } }
162
JLabel
A JLabel is a single line label similar to java.awt.Label. Additional functionality that a JLabel has is the ability to: Add an Icon Set the vertical and horizontal position of text relative to the Icon Set the
relative position of contents within component
public class LabelPanel extends JPanel { public LabelPanel() { // Create and add a JLabel JLabel plainLabel = new JLabel(“Plain Small Label”); add(plainLabel); // Create a 2nd JLabel JLabel fancyLabel = new JLabel(“Fancy Big Label”); // Instantiate a Font object to use for the label Font fancyFont = new Font(“Serif”, Font.BOLD | Font.ITALIC, 32); // Associate the font with the label fancyLabel.setFont(fancyFont); // Create an Icon Icon tigerIcon = new ImageIcon(“SmallTiger.gif”); // Place the Icon in the label fancyLabel.setIcon(tigerIcon); // Align the text to the right of the Icon fancyLabel.setHorizontalAlignment(JLabel.RIGHT); // Add to panel add(fancyLabel); } }
JButton
163 A JButton can be instantiated and used in a GUI just like a java.awt.Button. It behaves like an AWT 1.1 Button, notifying ActionListener list elements when pushed. public class ButtonPanel extends JPanel { public ButtonPanel () { JButton myButton = new JButton(“Tiger”); add(myButton); } }
Also, the JButton has support for an embedded Icon, specified in the constructor, or via the setIcon() method. This creates an image button; here, with the label Tiger: public class ButtonPanel extends JPanel { public ButtonPanel() { Icon tigerIcon = new ImageIcon(“SmallTiger.gif”); JButton myButton = new JButton(“Tiger”, tigerIcon); add(myButton); } }
JCheckBox
A JCheckBox is similar to an AWT Checkbox that is not in a CheckboxGroup. Although Swing provides a default graphic to signify JCheckBox selection, you also can specify your own Icon objects for both the checked and unchecked state.
public class CheckboxPanel extends JPanel {
Icon unchecked = new ToggleIcon (false); Icon checked = new ToggleIcon (true);
public CheckboxPanel() {
// Set the layout for the JPanel setLayout(new GridLayout(2, 1)); // Create checkbox with its state
164
// initialized to true JCheckBox cb1 = new JCheckBox(“Choose Me”, true); cb1.setIcon(unchecked); cb1.setSelectedIcon(checked); // Create checkbox with its state // initialized to false JCheckBox cb2 = new JCheckBox( “No Choose Me”, false); cb2.setIcon(unchecked); cb2.setSelectedIcon(checked); add(cb1); add(cb2); } class ToggleIcon implements Icon { boolean state; public ToggleIcon (boolean s) { state = s; } public void paintIcon (Component c, Graphics g, int x, int y) { int width = getIconWidth(); int height = getIconHeight(); g.setColor (Color.black); if (state) g.fillRect (x, y, width, height); else g.drawRect (x, y, width, height); } public int getIconWidth() { return 10; } public int getIconHeight() { return 10; } }
165
JRadioButton
In AWT, radio buttons are checkboxes that belong to the same CheckboxGroup; which ensures that only one checkbox is selected at a time. Swing has a separate widget called a JRadioButton. Each JRadioButton is added to a ButtonGroup so the group behaves as a set of radio
buttons. Like CheckboxGroup, ButtonGroup is a functional object that has no visual representation.
public class RadioButtonPanel extends JPanel {
public RadioButtonPanel() { // Set the layout to a GridLayout setLayout(new GridLayout(4,1));
// Declare a radio button JRadioButton radioButton;
// Instantiate a ButtonGroup for functional // association among radio buttons ButtonGroup rbg = new ButtonGroup();
// Create a label for the group JLabel label = new JLabel(“Annual Salary: “); label.setFont(new Font( “SansSerif”, Font.BOLD, 14)); add(label);
// Add a new radio button to the pane radioButton = new JRadioButton(“$45,000”); add (radioButton); // set key accelerator radioButton.setMnemonic (KeyEvent.VK_4);
// Add the button to the ButtonGroup rbg.add (radioButton);
166
// Set this radio button to be the default radioButton.setSelected(true);
// Set up two more radio buttons radioButton = new JRadioButton(“$60,000”); radioButton.setMnemonic (KeyEvent.VK_6); add (radioButton); rbg.add (radioButton); radioButton = new JRadioButton(“$75,000”); radioButton.setMnemonic (KeyEvent.VK_7); add (radioButton); rbg.add (radioButton); } }
Technically speaking, you can add JCheckBox or JToggleButton (described next) components to a CheckboxGroup. At most, one will be selected while in the group.
JToggleButton
The JToggleButton class is the parent to both JCheckBox and JRadioButton. It doesn’t have an AWT equivalent. The JToggleButton works like a Button that stays pressed in when toggled on. When a JToggleButton is toggled off, you cannot tell it from a regular Button or JButton class.
public class ToggleButtonPanel extends JPanel { public ToggleButtonPanel() { // Set the layout to a GridLayout setLayout(new GridLayout(4,1, 10, 10)); add (new JToggleButton (“Fe”)); add (new JToggleButton (“Fi”)); add (new
JToggleButton (“Fo”)); add (new JToggleButton (“Fum”)); }
167
}
JScrollPane
Like the AWT 1.1 ScrollPane, JScrollPane handles automatic horizontal and vertical scrolling of content. It lays out components using a ScrollPaneLayout, described in more detail under Swing Layouts. The key thing to know when using a JScrollPane is that Swing provides a JViewport for adding the object to scroll.
To get a handle to the viewport, JScrollPane has a getViewport() method. Then, to add a component to the viewport, the JViewport class has an add method. JViewport vport = someScrollPane.getViewport(); vport.add(someComponent);
Or, more commonly, the two lines are combined:
someScrollPane.getViewport().add(someComponent);
Another option is to provide the component to scroll to the constructor: JScrollPane pane = new JScrollPane(someComponent);
public class ScrollPanel extends JPanel {
public ScrollPanel() { setLayout(new BorderLayout()); Icon bigTiger = new ImageIcon(“BigTiger.gif”); JLabel tigerLabel = new JLabel(bigTiger); JScrollPane scrollPane = new JScrollPane(tigerLabel); add(scrollPane, BorderLayout.CENTER); } }
168
Viewports
The JViewport offers a view into a much larger area then can be seen without it. It can be either used within the JScrollPane component or as a
standalone widget, where you control all the scrolling functionality yourself. Normally, you wouldn’t want to do all the scrolling functionality yourself, but the capability is available. (Besides JScrollPane, JViewport is used internally within the Swing text components to handle scrolling of text.)
JTextComponents
JTextComponent is a generalized text class that contains all the features you would expect from
a simple editor. Some of its methods include:
copy() cut() paste() getSelectedText() setSelectionStart() setSelectionEnd() selectAll() replaceSelection() getText() setText() setEditable() setCaretPosition()
169 Although you won’t instantiate a JTextComponent object directly, you will often use these methods, many of which are not available in AWT text widgets. JTextComponent objects in Swing can be placed in a panel in a fashion nearly identical to AWT text widgets.
There are three basic subclasses of JTextComponent: JTextField, JTextArea, and JEditorPane. JPasswordField and JTextPane are sub-subclasses that are also of interest. If you want your users
to be able to see content that exceeds the screen display area, you must place the component inside of a JScrollPane to support scrolling to the extra content.
JTextField & JTextArea Other than having to add a JTextArea to a JScrollPane for scrolling, JTextField and JTextArea behave very similarly to their AWT counterparts: java.awt.TextField and java.awt.TextArea: // Instantiate a new TextField JTextField tf = new JTextField(); // Instantiate a new TextArea JTextArea ta = new JTextArea(); // Initialize the text of each tf.setText(“TextField”); ta.setText(“JTextArea\n Allows Multiple Lines”); add(tf); add(new JScrollPane(ta));
The JTextField also supports setting of text justification with setHorizontalAlignment().
The three available settings are LEFT, CENTER, and RIGHT, where LEFT is the default.
JTextPane
JTextPane is a full-featured text editor that supports formatted text, word wrap, and image
display. It uses a linked list of objects that implement the Style interface to specify formatting and supplies some convenience methods for formatting text. JTextPane tp = new JTextPane(); MutableAttributeSet attr = new SimpleAttributeSet();
170
StyleConstants.setFontFamily(attr, “Serif”); StyleConstants.setFontSize(attr, 18); StyleConstants.setBold(attr, true); tp.setCharacterAttributes(attr, false); add(new JScrollPane(tp));
public class TextPanel extends JPanel { public TextPanel() { // Set the layout to a BorderLayout setLayout(new BorderLayout());
// Create the three basic text components JTextField textField = new JTextField(); JTextArea textArea = new JTextArea(); JTextPane textPane = new JTextPane();
//Set the textpane’s font properties MutableAttributeSet attr = new SimpleAttributeSet(); StyleConstants.setFontFamily(attr, “Serif”); StyleConstants.setFontSize(attr, 18); StyleConstants.setBold(attr, true); textPane.setCharacterAttributes(attr, false);
add(textField, BorderLayout.NORTH); add(new JScrollPane(textArea), BorderLayout.CENTER); add(new JScrollPane(textPane), BorderLayout.SOUTH); } }
JPasswordField The JPasswordField is a JTextField that refuses to display its
contents openly. By default, the mask character is the asterisk (‘*’).
However, you can change this with the setEchoChar() method. Unlike java.awt.TextField, an echo character of (char)0 does not unset the mask.
171
class PasswordPanel extends JPanel { PasswordPanel() { JPasswordField pass1 = new JPasswordField(20); JPasswordField pass2 = new JPasswordField(20); pass2.setEchoChar (‘?’); add(pass1); add(pass2); } }
JEditorPane
The JEditorPane class is a specialized JTextComponent for displaying and editing HTML 3.2 tags or some other format like RTF (rich text format), as determined by the input. It is not meant to provide a full-fledged browser, but a lightweight HTML viewer, usually for the purpose of displaying help text. You either construct the pane with a URL parameter (via a String or URL), or change pages with the setPage() method. For HTML content, links within the HTML
page are traversable with the help of a HyperlinkListener.
JScrollBar
JScrollBar offers a lightweight version of the java.awt.Scrollbar component.
public class ScrollbarPanel extends JPanel {
public ScrollbarPanel() { setLayout(new BorderLayout()); JScrollBar scrollBar1 = new JScrollBar ( JScrollBar.VERTICAL, 0, 5, 0, 100); add(scrollBar1, BorderLayout.EAST); JScrollBar scrollBar2 = new JScrollBar ( JScrollBar.HORIZONTAL, 0, 5, 0, 100);
172
add(scrollBar2, BorderLayout.SOUTH); } }
JSlider
JSlider functions like a JScrollBar; however, it adds the ability to display major and minor tick
marks, as well as display a Border around the slider.
public class SliderPanel extends JPanel { public SliderPanel() { setLayout(new BorderLayout()); JSlider slider1 = new JSlider (JSlider.VERTICAL, 0, 100, 50); slider1.setPaintTicks(true); slider1.setMajorTickSpacing(10); slider1.setMinorTickSpacing(2); add(slider1, BorderLayout.EAST); JSlider slider2 = new JSlider (JSlider.VERTICAL, 0, 100, 50); slider2.setPaintTicks(true); slider2.setMinorTickSpacing(5); add(slider2, BorderLayout.WEST); JSlider slider3 = new JSlider (JSlider.HORIZONTAL, 0, 100, 50); slider3.setPaintTicks(true); slider3.setMajorTickSpacing(10); add(slider3, BorderLayout.SOUTH); JSlider slider4 = new JSlider (JSlider.HORIZONTAL, 0, 100, 50); slider4.setBorder( BorderFactory.createLineBorder(Color.blue)); add(slider4, BorderLayout.NORTH); } }
173 In addition to plain tick marks, with JSlider you can place labels along the axis as either a serious of numbers or components. For numeric labels, by just calling setPaintLabels (true), the slider will generate and use a series of labels based on the major tick spacing. So, if
the slider range is 0 to 100 with tick spacing of 10, the slider would then have labels of 0, 10, 20, … 100. On the other hand, if you want to generate the labels yourself, you can provide a Hashtable of labels. The hashtable key would be the Integer value of the position. The hashtable
value would be a Component to use for display of the label. The following demonstrates both:
public class SliderPanel2 extends JPanel { public SliderPanel2() { setLayout(new BorderLayout()); JSlider right, bottom; right = new JSlider(JSlider.VERTICAL, 1, 9, 3); Hashtable h = new Hashtable(); h.put (new Integer (1), new JLabel(“Mercury”)); h.put (new Integer (2), new
JLabel(“Venus”)); h.put (new Integer (3), new JLabel(“Earth”)); h.put (new Integer (4), new JLabel(“Mars”)); h.put (new Integer (5), new JLabel(“Jupiter”)); h.put (new Integer (6), new JLabel(“Saturn”)); h.put (new Integer (7), new JLabel(“Uranus”)); h.put (new Integer (8), new JLabel(“Neptune”)); h.put (new Integer (9), new JLabel(“Pluto”)); right.setLabelTable (h); right.setPaintLabels (true); right.setInverted (true); bottom = new JSlider(JSlider.HORIZONTAL, 0, 100, 25); bottom.setMajorTickSpacing (10); bottom.setPaintLabels (true); add(right, BorderLayout.EAST); add(bottom, BorderLayout.SOUTH); } }
174
JComboBox
The JComboBox works like AWT’s Choice component, but renames some methods and offers an editable option. For times when a fixed-list of choices isn’t enough, you can offer a JComboBox with a list of default choices, but still permit the entry of another value. The nicest
part about this control is that when the user presses the key for the first letter of an entry, it changes the highlighted selection. You can enhance this behavior by providing your own KeySelectionManager, a public inner class of JComboBox. public class ComboPanel extends JPanel { String choices[] = { “Mercury”, “Venus”, “Earth”, “Mars”, “Jupiter”, “Saturn”, “Uranus”,”Neptune”, “Pluto”}; public ComboPanel() { JComboBox combo1 = new JComboBox(); JComboBox combo2 = new JComboBox(); for (int i=0;ijavac Calculator.java
Implementation
262 Next, you write the implementation for the remote service. This is the CalculatorImpl class: public class CalculatorImpl extends java.rmi.server.UnicastRemoteObject implements Calculator {
// Implementations must have an explicit constructor in order to declare the RemoteException exception public CalculatorImpl() throws
java.rmi.RemoteException { super(); } public long add(long a, long b) throws java.rmi.RemoteException { return a + b; }
public long sub(long a, long b) throws java.rmi.RemoteException { return a – b; }
public long mul(long a, long b) throws java.rmi.RemoteException { return a * b; }
public long div(long a, long b) throws java.rmi.RemoteException { return a / b; } }
Again, store this code into your directory and compile it.
263 The implementation class uses UnicastRemoteObject to link into the RMI system. In the example the implementation class directly extends UnicastRemoteObject. This is not a requirement. A class that does not extend UnicastRemoteObject may use its exportObject() method to be linked into RMI. When a class extends UnicastRemoteObject, it must provide a constructor that declares that it may throw a RemoteException object. When this constructor calls super(), it activates code in UnicastRemoteObject that performs the RMI linking and remote object initialization.
Stubs and Skeletons
You next use the RMI compiler, rmic, to generate the stub and skeleton files. The compiler runs on the remote service implementation class file. >rmic CalculatorImpl
Try this in your directory. After you run rmic you should find the file Calculator_Stub.class and, if you are running the Java 2 SDK, Calculator_Skel.class.
Options for the JDK 1.1 version of the RMI compiler, rmic, are: Usage: rmic where includes: -keep Do not delete intermediate generated source files
-keepgenerated (same as “-keep”) -g Generate debugging info
-depend Recompile out-of-date files recursively -nowarn Generate no warnings -verbose Output messages about what the compiler is doing -classpath Specify where to find input source and class files
-d Specify where to place generated class files -J Pass argument to the java interpreter
The Java 2 platform version of rmic add three new options:
-v1.1 Create stubs/skeletons for JDK 1.1 stub protocol version -vcompat (default) Create stubs/skeletons compatible with both JDK 1.1 and Java 2 stub protocol versions
264
-v1.2 Create stubs for Java 2 stub protocol version only
Host Server
Remote RMI services must be hosted in a server process. The class CalculatorServer is a very simple server that provides the bare essentials for hosting. import java.rmi.Naming; public class CalculatorServer {
public CalculatorServer() { try { Calculator c = new CalculatorImpl(); Naming.rebind(“rmi://localhost:1099/CalculatorService”, c); } catch (Exception e) { System.out.println(“Trouble: ” + e); } } public static void main(String args[]) { new CalculatorServer(); } }
Client
The source code for the client follows:
import java.rmi.Naming; import java.rmi.RemoteException; import java.net.MalformedURLException; import java.rmi.NotBoundException;
public class CalculatorClient {
public static void main(String[] args) { try {
265
Calculator c = (Calculator) Naming.lookup( System.out.println( c.sub(4, 3) ); System.out.println( c.add(4, 5) ); System.out.println( c.mul(3, 6) ); System.out.println( c.div(9, 3) ); } catch (MalformedURLException murle) { System.out.println(); System.out.println(“MalformedURLException”); System.out.println(murle); } catch (RemoteException re) { System.out.println(); System.out.println(“RemoteException”); System.out.println(re); } catch (NotBoundException nbe) { System.out.println(); System.out.println(“NotBoundException”); System.out.println(nbe); } catch ( java.lang.ArithmeticException ae) { System.out.println(); System.out.println(“java.lang.ArithmeticException”); System.out.println(ae); } } } “rmi://localhost/CalculatorService”);
Running the RMI System
266 You are now ready to run the system. You need to start three consoles, one for the server, one for the client, and one for the RMIRegistry. Start with the Registry. You must be in the directory that contains the classes you have written. From there, enter the following: >rmiregistry
The registry will start running and you can switch to the next console. In the second console start the server hosting the CalculatorService, and enter the following: >java CalculatorServer
It will start, load the implementation into memory and wait for a client connection. In the last console, start the client program. >java CalculatorClient
If all goes well you will see the following output:
1 9 18 3
That’s it; you have created a working RMI system. Even though you ran the
three consoles on the same computer, RMI uses your network stack and TCP/IP to communicate between the three separate JVMs. This is a full-fledged RMI system.
Parameters in RMI
You have seen that RMI supports method calls to remote objects. When these calls involve passing parameters or accepting a return value, how does RMI transfer these between JVMs? What semantics are used? Does RMI support pass-by-value or pass-by-reference? The answer depends on whether the parameters are primitive data types, objects, or remote objects.
Parameters in a Single JVM
First, review how parameters are passed in a single JVM. The normal semantics for Java technology is pass-by-value. When a parameter is passed to a method, the JVM makes a
267 copy of the value, places the copy on the stack and then executes the method. When the code inside a method uses a parameter, it accesses its stack and uses the copy of the parameter. Values returned from methods are also copies.
When a primitive data type (boolean, byte, short, int, long, char, float, or double) is passed as a parameter to a method, the mechanics of pass-by-value are straightforward. The mechanics of passing an object as a parameter are more complex. Recall that an object resides in heap memory and is accessed through one or more reference variables. And, while the following code makes it look like an object is passed to the method println() String s = “Test”; System.out.println(s);
in the mechanics it is the reference variable that is passed to the method. In the example, a copy of reference variable s is made (increasing the reference count to the String object by one) and is placed on the stack. Inside the method, code uses the copy of the reference to access the object. Now we will see how RMI passes parameters and return values between remote
JVMs.
Primitive Parameters
When a primitive data type is passed as a parameter to a remote method, the RMI system passes it by value. RMI will make a copy of a primitive data type and send it to the remote method. If a method returns a primitive data type, it is also returned to the calling JVM by value. Values are passed between JVMs in a standard, machine-independent format. This allows JVMs running on different platforms to communicate with each other reliably.
Object Parameters
When an object is passed to a remote method, the semantics change from the case of the single JVM. RMI sends the object itself, not its reference, between JVMs. It is the object that is passed by value, not the reference to the object. Similarly, when a remote method returns an object, a copy of the whole object is returned to the calling program.
268
Unlike primitive data types, sending an object to a remote JVM is a nontrivial task. A Java object can be simple and self-contained, or it could refer to other Java objects in complex graph-like structure. Because different JVMs do not share heap memory, RMI must send the referenced object and all objects it references. (Passing large object graphs can use a lot of CPU time and network bandwidth.)
RMI uses a technology called Object Serialization to transform an object into a linear format that can then be sent over the network wire. Object serialization essentially flattens an object and any objects it references. Serialized objects can be de-serialized in the memory of the remote JVM and made ready for use by a Java program.
Remote Object Parameters
RMI introduces a third type of parameter to consider: remote objects. As you have seen, a client program can obtain a reference to a remote object through the RMI Registry program. There is another way in which a client can obtain a remote reference, it can be returned to the client from a method call. In the following code, the BankManager service getAccount() method is used to obtain a remote reference to an Account remote service. BankManager bm; Account try { bm = (BankManager) Naming.lookup( “rmi://BankServer/BankManagerService” ); a = bm.getAccount( “ACCOUNT A” ); // Code that uses the account } catch (RemoteException re) { } a;
In the implementation of getAccount(), the method returns a (local) reference to the remote service.
269
public Account getAccount(String accountName) { // Code to find the matching account AccountImpl ai = // return reference from search return ai; }
When a method returns a local reference to an exported remote object, RMI does not return that object. Instead, it substitutes another object (the remote proxy for that service) in the return stream. The following diagram illustrates how RMI method calls might be used to: • • • •
Return a remote reference from Server to Client A Send the remote reference from Client A to Client B Send the remote reference from Client B back to Server
Notice that when the AccountImpl object is returned to Client A, the Account proxy object is substituted. Subsequent method calls continue to send the reference first to Client B and then back to Server. During this process, the reference continues to refer to one instance of the remote service. It is particularly interesting to note that when the reference is returned to Server, it is not converted into a local reference to the implementation object. While this would result in a
270 speed improvement, maintaining this indirection ensures that the semantics of using a remote reference is maintained.
3.5
JAVA DATA BASE CONNECTIVITY Application architecture
One of the most important design issues when developing a Java database application
is the overall system architecture; in particular, how many different components should be deployed. Traditionally, this is characterized by how many tiers, or layers, the application requires. There are two basic architectural models that can describe a system: the two-tier model and the n-tier model.
The two-tier model
The two-tier model is the traditional client-server framework; it has a client tier and a server tier. This simple model requires that the client be intimately aware of the database server. Thus, for example, the client needs database-specific code resulting in a tight coupling between the two tiers. This tight coupling has several advantages. First, it can decrease development time due to the fact the overall system is considerably simpler and smaller. Second, the tight coupling can potentially improve system performance as the client can easily take advantage of specific server functionality that might not be available to a less tightly coupled system. On the other hand, this tight coupling can lead to several problems. Most notably, system maintenance can become more difficult because changes in the server can break the client or visa versa. Furthermore, if the database changes, all of the client code will need to be modified. If the client is highly distributed, propagating changes throughout the system can be difficult, and in some scenarios impossible. As a result, two-tier applications can be useful in a corporate LAN environment where complete control of all clients is achieved.
271
The n-tier model
The n-tier model has a client tier, at least one server tier, and at least one middle layer. Because of the extra tier, many of the problems that affected the two-tier model are no longer an issue. For example, the middle layer now maintains the database connection information. This means the clients only need to know about the middle tier. Because the middle tier is generally operating at the same physical location as the server (for instance, both components can be behind the same firewall), maintaining the middle tier is considerably easier than maintaining several hundred client installations. Another advantage of the n-tier approach is that the overall system can easily scale to handle more users. All one needs to do is add more middle tiers or server tiers, depending on the results of the profiling operations. Because middle tiers are typically implemented using Web servers — using JavaServer Pages and Servlets technologies — it is simple to add load-balancing or even new hardware components. Java language provides many of the necessary components, pre-built, for constructing n-tier applications.
Introduction to JDBC
SQL is a language used to create, manipulate, examine, and manage relational databases. Because SQL is an application-specific language, a single statement can be very expressive and can initiate high-level actions, such as sorting and merging data. SQL was standardized in 1992 so that a program could communicate with most database systems without having to change the SQL commands. Unfortunately, you must connect to a database before sending SQL commands, and each database vendor has a different interface, as well as different extensions of SQL.
ODBC, a C-based interface to SQL-based database engines, provides a consistent interface for communicating with a database and for accessing database metadata (information about the database system vendor, how the data is stored, and so on).
Individual
272 vendors provide specific drivers or “bridges” to their particular database management system. Consequently, thanks to ODBC and SQL, you can connect to a database and manipulate it in a standard way.
Though SQL is well suited for manipulating databases, it is unsuitable as a general application language and programmers use it primarily as a means of communicating with databases–another language is needed to feed SQL statements to a database and process results for visual display or report generation. Unfortunately, you cannot easily write a program that will run on multiple platforms even though the database connectivity standardization issue has been largely resolved. For example, if you wrote a database client in C++, you would have to totally rewrite the client for each platform; that is to say, your PC version would not run on a Macintosh. There are two reasons for this. First, C++ as a language is not portable for the simple reason that C++ is not completely specified, for example, how many bits does an int hold? Second and more importantly, support libraries such as network access and GUI libraries are different on each platform.
You can run a Java program on any Java-enabled platform without even recompiling that program. The Java language is completely specified and, by definition, a Java-enabled platform must support a known core of libraries. One such library is JDBC (Java Database Connectivity), which you can think of as a Java version of ODBC. JDBC allows us to construct SQL statements and embed them inside Java API calls. Database vendors are already busy creating bridges from the JDBC API to their particular systems. JavaSoft has also provided a bridge driver that translates JDBC to ODBC, allowing you to communicate with legacy databases that have no idea that Java exists. Using Java in conjunction with JDBC provides a truly portable solution to writing database applications. The JDBC-ODBC bridge driver is just one of four types of drivers available to support JDBC connectivity. It comes packaged with the JDK 1.1 or as a separate package for use with 1.0 systems.
JDBC driver fundamentals
273 A casual inspection of the JDBC API quickly shows the dominance of
interfaces within the API, which might lead a user to wonder where the work is done. Actually this is strength of the approach that the JDBC developers took because the actual implementation is provided by JDBC Driver vendors, who in turn provide the classes that implement the necessary interfaces. Following figure shows the classes and interfaces of java.sql, the JDBC API package.
From a programming perspective, there are two main classes responsible for establishing a connection with a database. The first class is DriverManager, which is one of the few actual classes provided in the JDBC API. DriverManager is responsible for managing a pool of registered drivers, essentially abstracting the details of using a driver so that programmer does not have to deal with them directly. The second class is the actual JDBC Driver class. These are provided by independent vendors. The JDBC Driver class is responsible for establishing the database connection and handling all of the communication with the database. JDBC drivers come in four different types.
Registering a JDBC driver
274 The first step in the process of creating a connection between a Java application and a database is the registration of a JDBC driver with the Java virtual machine (JVM) in which the Java application is running. The connection and all database communications are controlled by the Driver Manager object. To establish a connection, a suitable JDBC driver for the target database must be registered with the DriverManager object.
The JDBC specification, states that JDBC drivers are supposed to register themselves with the DriverManager object automatically when they are loaded into a JVM. For example, the following code snippet uses a static initializer to first create an instance of the persistentjava JDBC driver and then register it with the DriverManager.
static { java.sql.DriverManager.registerDriver(new com.persistentjava.JdbcDriver()) ; }
Registering a driver is simply a matter of loading the driver class into the JVM, which can be done in several different ways. One way to do this is with the ClassLoader Class.forName(com.persistentjava.JdbcDriver) ;. Another method, which is not as well
known, uses the jdbc.drivers system property. This method can be used in one of three different ways:
* From the command line:
java -Djdb.drivers=com.persistentjava.JdbcDriver Connect
* Within the java application:
System.setProperty(“jdbc.drivers”, “com.persistentjava.JdbcDriver”) ;
* By setting the jdbc.drivers property in the System property file, which is generally system dependent
275 By separating the drivers with a colon, multiple drivers can be registered using the aforementioned system property technique. One of the benefits of using the system property technique is that drivers can be easily swapped in and out of the JVM without modifying any code (or at least with minimal code changes).
If multiple drivers are registered, their order of precedence is: 1) JDBC drivers registered by the jdbc.drivers property at JVM initialization, and 2) JDBC drivers dynamically loaded. Because the jdbc.drivers property is only checked once upon the first invocation of a DriverManager() method, it’s important to ensure all drivers are registered correctly before establishing the database connection.
Not all JVMs are created equal, however, and some JVMs do not follow the JVM specification. As a result, static initializers do not always work as advertised. This results in multiple ways to register a JDBC driver, including: * Class.forName(“com.persistentjava.JdbcDriver”).newInstance(); * DriverManager.registerDriver(new com.persistentjava.JdbcDriver()) ;
One final issue is that Class.forname() can throw a ClassNotFoundException, so you need to wrap the registration code in an appropriate exception handler.
JDBC driver URLs
Once a JDBC driver has been registered with the DriverManager, it can be used to establish a connection to a database. But how does the DriverManager select the right driver, given that any number of different drivers might actually be registered? The technique is quite simple: each JDBC driver uses a specific JDBC URL (which has the same format as web addresses) as a means of self-identification. The format of the URL is straightforward: jdbc:sub-protocol:database locator. The sub-protocol is specific to the JDBC driver and
276 can be odbc, oracle, db2, and so on depending on the actual JDBC driver vendor. The database locator is a driver-specific indicator for uniquely specifying the database with which an application wants to interact. Depending on the type of driver, this locater may include a hostname, a port, and a database system name.
When presented with a specific URL, the DriverManager iterates through the collection of registered drivers until one of the drivers recognizes the specified URL. If no suitable driver is found, an SQLException is thrown. The following list demonstrates several specific examples of actual JDBC URLs: * jdbc:odbc:jdbc * jdbc:oracle:thin:@persistentjava.com:1521:jdbc; * jdbc:db2:jdbc
Many drivers, including the JDBC-ODBC bridge driver, accept additional parameters at the end of the URL such as username and password. The method for obtaining a database connection, given a specific JDBC URL, is to call getConnection() on the DriverManager object. This method comes in several flavors: * DriverManager.getConnection(url) ; * DriverManager.getConnection(url, username, password) ; * DriverManager.getConnection(url, dbproperties) ;
Here, url is a String object that is the JDBC URL; username and password are String objects that are the username and password that the JDBC application should use to connect to the data source; and dbproperties is a Java properties object that encapsulates all of the parameters (possibly including username and password) that a JDBC driver requires to successfully make a connection. Now that we have the driver basics in hand, we can examine the individual driver types in more detail.
Type one drivers
277
Type one drivers come in one variety: they all use the JDBC-ODBC bridge, which is included as a standard part of the JDK. Type one drivers are differentiated by the ODBC (Open Database Connectivity) driver attached to the JDBCODBC bridge. To connect to a different data source, you simply have to register (or effectively bind) a different ODBC data source, using the ODBC Administrator, to the appropriate data source name.
The problem for type one drivers is their use in distributed applications. Because the bridge itself does not support distributed communication, the only way type one drivers can work across a network is if the ODBC driver itself supports remote interactions. For simple ODBC drivers, this is not an option, and while big databases do typically have ODBC drivers that can work remotely, they cannot compete performance-wise with the pure Java JDBC drivers.
The
class
name
for
the
JDBC-ODBC
bridge
driver
is
sun.jdbc.odbc.JdbcOdbcDriver and the JDBC URL takes the form jdbc:odbc:dsn, where dsn is the Data Source Name used to register the database with the ODBC Administrator. For
example, if a database is registered with an ODBC data source name of d_source, a username of java, and a password of sun, the following code snippet can be used to establish a connection. String url = “jdbc:odbc:d_source” ; Connection con ; try { Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”) ;
278
} catch(java.lang.ClassNotFoundException e) { System.err.print(“ClassNotFoundException: “) ; System.err.println(e.getMessage()) ; return ; } try { con = DriverManager.getConnection(url, “java”, “sun”); } catch(SQLException ex) { System.err.println(“SQLException: ” + ex.getMessage()); } finally { try{ con.close ; } catch(SQLException ex) { System.err.println(SQLException: ” + ex.getMessage()) ; } }
Type two drivers
Type two drivers are also known as partial Java drivers, in that they translate the JDBC API directly into a databasespecific API. The database client application (the host that is running the JVM) must have the
appropriate database client library, which might include binary code installed and possibly running. But, using a type two model restricts the developer to client platforms and operating systems supported by the database vendor’s client library. This model can work effectively, however, when the client base is tightly controlled. This typically occurs in corporate LANs. One example of a type two driver is the DB2 JDBC
279 application driver. The following example demonstrates how to establish a connection using a DB2 driver. String url = “jdbc:db2:d_source” ; try { Class.forName(“COM.server1.db2.jdbc.app.DB2Driver”) ; } catch(java.lang.ClassNotFoundException e) { System.err.print(“ClassNotFoundException: “) ; System.err.println(e.getMessage()) ; return ; } …
Type three drivers
Type three drivers are pure Java drivers that transform the JDBC API into a databaseindependent protocol. The JDBC driver does not communicate with the database directly; it communicates with a middleware server, which in turn communicates with the database. This extra level of indirection provides flexibility in that different databases can be accessed from the same code because the middleware server hides the specifics from the Java application. To switch to a different database, you only need to change parameters in the middleware server. (One point of note: the database format you are accessing must be supported by the middleware server.)
The downside to type three drivers is that the extra level of indirection can hurt overall system performance. On the other hand, if an application needs to interact with a variety of database formats, a type three driver is an efficient approach due to the fact that the same JDBC driver is used regardless of the underlying database. In addition, because the middleware server can be installed on a specific hardware platform, certain optimizations can be performed to capitalize on profiling
280 results.
Type four drivers
Type four drivers are pure Java drivers that communicate directly with the database. Many programmers consider this the best type of driver, as it typically provides optimal performance and allows the developer to leverage database-specific functionality. Of course this tight coupling can hinder flexibility, especially if you need to change the underlying database in an application. This type of driver is often used in applets and other highly distributed applications.
A Complete JDBC Example
Running through a simple example will help you grasp the overall concepts of JDBC. The fundamental issues encountered when writing any database application are: Creating a database. You can either create the database outside of Java, via tools
supplied by the database vendor, or via SQL statements fed to the database from a Java program. Connecting to an ODBC data source. An ODBC data source is a database that is
registered with the ODBC driver. In Java you can use either the JDBC to ODBC bridge, or JDBC and a vendor-specific bridge to connect to the datasource. Inserting information into a database. Again, you can either enter data outside of
Java, using database-specific tools, or with SQL statements sent by a Java program. Selectively retrieving information. You use SQL commands from Java to get results
and then use Java to display or manipulate that data.
281
Creating a Database
For this example, consider the scenario of tracking coffee usage at the XYZ University Cafe. A weekly report must be generated for University management that includes total coffee sales and the maximum coffee consumed by a programmer in one day. Here is the data:
Coffee Consumption at Cafe Jolt, XYZ University
Programmer Day # Cups Amith Bhavani Prashanth Ramesh Rajesh Sumanth John Peter Sandhya Sudha Sathish Mon 1 Mon 2 Tue 8 Tue 2 Tue 3 Wed 2 Thu 3 Thu 1 Fri Fri Fri 9 3 4
To create this database, you can feed SQL statements to an ODBC data source via the JDBC-ODBC bridge. First, you will have to create an ODBC data source. You have many choices—you could, for example, connect an Oracle or Sybase or MS Access database. For simplicity create a MS Access ODBC data source to use for this example. Call this ODBC data source CafeJolt.
282
NOTE: There are basically two steps in creating a MS Access data source: The first is to create an ODBC-accessible data source to use, and the second is register it with your system. Perform the following tasks: 1. Pick a system to use as the data source. o
Microsoft Access Make a directory for the database Create a new database file, testdb.mdb
2. From Windows 95/NT 4.0:
o
Bring up Control Panel Select the Start button Select the Settings menu item Select the Control Panel menu item
o
Find and double-click on the ODBC Icon (32-bit/with 32 on it).
This brings up the ‘Data Sources’ window.
o o
Select Add. This brings up the Add Data Source window. Select the driver for the type of driver you want. If you selected Microsoft Access, the ODBC Microsoft Access 7.0 Setup window appears. Name the data source Fill in a description. Click on the Select button to bring up a file dialog. Locate the directory created in task 1. Select OK to accept new driver.
To enter the data into the testdb database, create a Java application that follows these steps:
1. Load the JDBC-ODBC bridge. You must load a driver that tells the JDBC classes how to talk to a data source. In this case, you will need the class JdbcOdbcDriver:
283 Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); This can also be specified from the command line via the jdbc.drivers system property: java -Djdbc.drivers=sun.jdbc.odbc.JdbcOdbcDriver AProgram
2. Connect to a data source. A URL is used to connect to a particular JDBC data source. Given that you have loaded the JDBC-ODBC bridge driver, URLs should be in the following form jdbc:odbc:data-source-name. Using the DriverManager class, you request a connection to a URL and the DriverManager selects the appropriate driver; here, only the driver JdbcOdbcDriver is loaded. Connection con = DriverManager.getConnection(URL, username, password); Where the username and password are empty strings, “”, in this case. Or they can be left out., by just entering the URL. Example : Connection con = DriverManager.getConnection(Jdbc:odbc: CafeJolt);
3. Send SQL statements to create the table. Ask the connection object for a Statement object: Statement stmt = con.createStatement();
Then, execute the following SQL statement to create a table called JoltData. create table JoltData ( programmer varchar (32), day char (3), cups integer, variety varchar (20));
The Java code to do this is: stmt.execute( “create table JoltData (“+ “programmer varchar (32),”+
284 “day char (3),”+ “cups integer);” ); After you have created the table, you can the insert the appropriate values such as: insert into JoltData values (‘Gilbert’, ‘Mon’, 1); insert into JoltData values (‘Wally’, ‘Mon’, 2); insert into JoltData values (‘Edgar’, ‘Tue’, 8); … Here is the Java source for a complete application to create table JoltData and insert the required rows.
import java.sql.*; //import all the JDBC classes
public class CreateJoltData {
static String[] SQL = { “create table JoltData (“+ “programmer varchar (32),”+ “day varchar (3),”+ “cups integer);”, “insert into JoltData values (‘Amith’, ‘Mon’, 1);”, “insert into JoltData values (‘Bhavani’, ‘Mon’, 2);”, “insert into JoltData values (‘Prashanth’, ‘Tue’, 8);”, “insert into JoltData values (‘Ramesh’, ‘Tue’, 2);”, “insert into JoltData values (‘Rajesh’, ‘Tue’, 3);”, “insert into JoltData values (‘Sumanth’, ‘Wed’, 2);”, “insert into JoltData values (‘John’, ‘Thu’, 3);”, “insert into JoltData values (‘Peter’, ‘Thu’, 1);”, “insert into JoltData values (‘Sandhya’, ‘Fri’, 9);”, “insert into JoltData values (‘Sudha’, ‘Fri’, 3);”, “insert into JoltData values (‘Sathish’, ‘Fri’, 4);”,
285
};
public static void main(String[] args) { String URL = “jdbc:odbc:CafeJolt”; String username = “”; String password = “”; // Users can leave out the user
// name and password
try { Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); } catch (Exception e) { System.out.println(“Failed to load JDBC/ODBC driver.”); return; }
Statement stmt = null; Connection con=null; try { con = DriverManager.getConnection ( URL, username, password); // username ,password can be left out stmt = con.createStatement(); } catch (Exception e) { System.err.println(“problems connecting to “+URL); }
try { // execute SQL commands to create table, insert data for (int i=0; i