JAVA Programming

Contents
Chapter 1. An Overview of Java
1.1. Introduction to Java 1.2.Java and Object-Oriented Programming 1.3.The Java Language 1.4.Java in Practice

Chapter 2.

Java for C++ Developers

2.1.Java and C++ 2.2.Overview of Classes 2.3.Simplifying C++ 2.4.Javadoc

Chapter 3.

Object-Oriented Principles and JAVA

3.1.Procedural Programming Vs OOP 3.2.Fundamentals of OOP 3.3.Classes, Methods, and Messages

Chapter 4.

Introduction to the Java Language

4.1.Java Syntax 4.2.Operators and Flow Control 4.3.Java Object Orientation

Chapter 5.

Anatomy of Java Classes

5.1.Overall Structure of a Class 5.2.Object Life Cycle 5.3.Interfaces, Casting, and Reflection 5.4.Exception Handling

Chapter 6.

Java User Interface Programming

6.1.User Interface Architecture 6.2.Event Handling 6.3.Useful AWT Utilities 6.4.Java Applications

Chapter 7.

Features of Java

7.1.Java Packages 7.2.Threads and Synchronization 7.3.Internationalization

Chapter 8.

Java Security, Networking, and the Internet

8.1.Java Security 8.2.The Applet Execution Environment 8.3.Networking 8.4.Server Issues

Chapter 9.

JavaBeans

9.1.Components 9.2.Working with Components 9.3.Creating and Using Beans

Chapter 10. Java Database Connectivity
10.1.Overview of JDBC 10.2.JDBC Configuration 10.3.Connections 10.4.Querying the Database

Contents
Chapter 11. Java and Distributed Objects
11.1.Overview of Distributed Objects 11.2.An Overview of Java RMI 11.3.Creating RMI Application 11.4.Java IDL

Chapter 12. Java Animation and Images
12.1.Working with Images 12.2.Animation 12.3.Image Manipulation 12.4.Working with Sound

Chapter 13. Java Tips and Techniques
13.1.Features of the Language 13.2.Advanced GUI Features 13.4.Using the Java Packages

1 An Overview of Java
1.1. Introduction to Java • What is Java:
Java is a powerful and versatile object-oriented programming language. It has many of the best features of languages such as C and C++. However it also adds many new features of its own, which make it uniquely suited to today's computing requirements. The Java programming language was developed by a team at Sun Microsystems in the early 1990s. The original aim of Java's development team was to produce a language that could be used to control a wide variety of consumer electronic devices. To achieve this, the language had to be portable - in other words, capable of running on a variety of processors. It also had to be small, efficient, and easy to program in. For a number of reasons, the use of Java to control consumer devices never became a commercial reality. However by 1994 the World Wide Web had established itself as the most significant and rapidly growing part of the Internet. It soon became apparent that the very real benefits that Java could deliver were ideally suited to the Web's requirements. Programs that are small and efficient can be downloaded quickly from the Internet. And because the programs are portable, they can execute on the many different types of computers connected to the Internet. Java is a powerful language, but it is simpler to learn and to program in than other high-level languages. Its syntax is very similar to C, and programmers who use C and C++ will find it easy to convert to Java. The designers of Java deliberately left out a number of features that are significant in C++. The reasons that they omitted these features include the following: The features had not proved as useful as originally intended They make programming in C++ more difficult and error-prone They make it less secure C++ is an object-oriented derivative of the C programming language. However, Java was designed from the ground up to be an object-oriented language. Virtually everything in Java - apart from simple data types - is an object. Java's object model is simple to understand and easy to extend. It does not support some of the object-oriented features of other languages. For example, it doesn't have multiple inheritance, a feature which can be confusing and difficult to use. Instead, it uses interfaces, which are simpler to use and achieve the same effect. Java has a rich object environment. It has an extensive collection of classes, which are grouped together in packages. These packages deal with areas such as I/O and networking, and simplify the programming required to develop an application.

Page 1

Go To INDEX

Java programs are architecture-neutral - they can run on any computer platform that supports Java. You can also describe Java programs as platform-independent. When a Java program is written, the source code is compiled to bytecodes. These bytecodes are similar to machine instructions, but they are not specific to any particular processor. The bytecodes are then interpreted by a Java runtime environment, which is often embedded in a Web browser. So it is possible to write a Java program once, and execute it on a number of different platforms, without having to recompile the source code. Security is one of the most important features of the Java language. When a Java runtime environment, such as a browser, loads a Java program, it verifies the bytecodes as safe before it allows the program to execute. In addition, the program can't call external functions or access system resources such as the hard disk. These features make Java a safer and more secure language than any others currently available. In addition to being a secure language, Java is also extremely robust. This means that it is easy to write reliable, bug-free programs using Java. One of the reasons for Java's robustness is that the code is checked both at compile time and at run-time. Another reason is that Java automatically allocates and deallocates memory as needed, thus freeing the programmer from this responsibility. The process of automatic memory deallocation in Java is called garbage collection. In other programming languages, inefficient memory management is a major source of errors. Java's robustness is also enhanced by its simple object-oriented exception handling.

JAVA and the Web:

The Internet is a worldwide system of linked computer networks. It is a heterogeneous network, meaning that many different types of computers are connected to it. Among the computers connected to the Internet are: Different types of mainframes UNIX workstations and servers IBM-compatible PCs Apple Macintoshes The common factor linking all computers on the Internet is that they use the TCP/IP protocol to communicate. TCP/IP stands for Transmission Control Protocol/Internet Protocol. Since the mid-1990s the World Wide Web has been the fastest-growing part of the Internet. The Web uses an Internet standard called Hypertext Transfer Protocol (HTTP) to locate and download resources on remote computers. These resources are usually HTML files - often called Web pages - or multimedia elements such as graphics and sound files. HTML stands for Hypertext Markup Language. Web pages are viewed using a browser, which displays text and - usually graphics. The browser also provides easy-to-use navigation, both within the document, and to other Web pages connected by hypertext links. Several factors have been crucial to the phenomenal growth of the Web, among them:

Page 2

Go To INDEX

Simple, transparent navigation between documents Rich multimedia content Ease of creation of Web pages that can then be read by many people Despite their advantages, however, Web pages are essentially static documents. To incorporate any kind of interactivity into a simple HTML Web page, it is necessary to write a CGI script, which executes on the server computer. CGI stands for Common Gateway Interface. CGI scripts suffer from two main disadvantages: They impose an extra workload on the Web server They consume bandwidth Up to now, their use has mainly been restricted to accepting input from users through on-screen forms. This data can then be used to query remote databases. Java was originally designed to control consumer electronic devices. To do this, Java had to be platform-independent, secure, and reliable. However these requirements also made Java an ideal language for developing Internet applications. In 1994 the Java design team used the Java language to implement a Web browser. This browser is called HotJava, and it is capable of displaying Web pages and graphics. However HotJava has an ability that no other browser then had - it could download and execute Java programs. Once the HotJava browser had demonstrated the feasibility of executing Java programs, other Java-enabled browsers were quickly developed. Netscape Navigator first implemented Java in its Version 2.0. More recently, Microsoft Internet Explorer 3.0 and Spyglass can also run Java programs. These programs execute on the web client, thus conserving processing power and bandwidth on the remote Web server. The development of Java has changed the face of the Web. It is now possible to create dynamic, interactive Web pages that incorporate Java programs. Java programs can perform many useful functions in a Web page by: Enhancing the user interface Validating user input Performing calculations Guiding the user through remote Web sites The key to Java's power on the Web is its ability to distribute executable content. The reason it can do this is because of its cross-platform capabilities. Once a Java program has been written it is compiled into platform-independent bytecodes. It can then run on any platform that supports the Java Virtual Machine. The Java Virtual Machine (JVM) is a theoretical computer that interprets bytecodes. The JVM is usually implemented in software, in which case it sits between the Java program and the computer being used. On the Web, this means any computer that can run a Java-enabled browser. Downloading any program from the Internet poses important security issues. For example, programs downloaded from untrusted web sites might contain hostile code that would wipe data from hard disks. However, several of Java's design features were explicitly included to stop such an event occurring. For example, in Java, the standard security model is to deny the downloaded applet access to the local hard disk.
Page 3 Go To INDEX

The Java Environment:

The Java environment contains both compile-time and runtime elements. After Java source code is written it is compiled into bytecodes. Bytecodes are instructions for a Java Virtual Machine, which is a virtual computer that runs in memory. In the Java Virtual Machine, bytecodes are executed by the Interpreter, which operates in conjunction with the runtime system. Alternatively the bytecodes can be converted into native machine code instructions by a just-in-time (JIT) compiler. JIT compilers often give execution times nearly as fast 90% - as native C or C++ programs. You can create and run Java programs with the set of tools included in the Java Development Kit (JDK). The latest release of the JDK can be downloaded from Sun's Web site at http://java.sun.com. The tools in the latest release of the JDK - currently at 1.1 - include the following: Javac - the Java compiler Java - the Java runtime interpreter Jdb - a Java debugger Javadoc - generates documentation Appletviewer - this runs Java programs outside a Web browser A Java source file can be created with any text editor. It must contain one or more class definitions and nothing else, as Java doesn't permit global variables or functions. In object-oriented programming, a class describes the general attributes and behavior of an object. The source file will have a java filename extension. Suppose you have written a simple Java program - the source code is in a file called FirstApp.java. To compile this, you run the Java compiler javac and pass the name of the source file on the command line C:\> javac FirstApp.java The compiler now creates a file called FirstApp.class and stores it in the same directory as the source file. The FirstApp.class file is a platform-independent bytecode version of your program. To run the program, you use the runtime interpreter java as follows: C:\> java FirstApp In this case, you pass the class name FirstApp as an argument on the command line, not the filename FirstApp.class. The runtime interpreter now executes the FirstApp program and prints the message to the screen. The debugger for the Java environment is called jdb. It is a command-line tool that can be used to debug files on the local system or remote systems. The javadoc tool is used to create documentation from Java source code files. It does this by searching for special comment tags embedded in the files. It then creates an HTML file, which can be viewed with any Web browser. The two most important types of program you can create with Java are applications and applets. Java applications are standalone programs that execute in conjunction with a runtime interpreter. Java applets, on the other hand, are typically smaller programs that

Page 4

Go To INDEX

are embedded in an HTML document. They execute within a Java-enabled browser such as HotJava or Netscape Navigator. The Appletviewer included in the JDK enables you to run Java applets you are developing without having to launch a browser. The syntax to use the Appletviewer is as follows: Appletviewer URL The URL points to an HTML file that contains a tag for the relevant applet. URL stands for Uniform Resource Locator, an address that uniquely identifies a resource on the Internet.

Java Applications and Applets:

Because Java is a general-purpose programming language, it can be used to produce many types of programs. You can use Java to create stand-alone applications, just like those produced with C and C++. However you can also use it to produce applets, which are small programs that execute within a Java-enabled Web browser. Applications are stand-alone programs that are generally launched from the command line. With Java, you can create applications that have a graphical user interface (GUI). Most end-user applications now available have a GUI interface. But you can also use Java to create command line applications that do not use GUI features. Java makes it easy to create portable GUI applications that run on any supported platform. For example the same Java bytecodes that will run on a Windows 95 computer will also work on computers running Solaris and OS/2. So one set of source code produces one set of bytecodes that run identically anywhere. You don't need to write any custom code to give your program the native look and feel of each operating system it will use, as you will get this automatically. A Java application is a class that has a main method. In object-oriented programming, a method is a function associated with a particular object. A Java applet, on the other hand, does not have a main method. Applets are small Java programs that are usually downloaded from the Internet or some other network. They can only execute within a Java-enabled browser such as HotJava or Netscape Navigator. They can also execute within Appletviewer, which is part of the Java Development Kit (JDK). Applets usually fulfill certain specific functions. Calculators, data entry forms, and navigational image maps have been implemented as applets. Because they are usually small, they can be downloaded quite quickly. Despite their size, applets can perform many useful roles with a minimum of code. This is because they can take advantage of the features that are already built into Web browsers. For instance, to display a graphic format such as a GIF file, the applet uses functionality already built in to the browser. A Java applet is normally invoked from a Web page. The relevant HTML file contains an <APPLET> tag and at least three attributes: Code - the name of the file that contains the main class of the applet Width - initial width in pixels Height - initial height in pixels

Page 5

Go To INDEX

If the applet is located at a different URL from the source HTML file, you specify its location with the codebase attribute. Other attributes can be used to specify the applet's position and appearance on the Web page. Applets can be used in many ways to enhance Web pages. For example applets are now being used to perform the following functions: Create animations Play sound effects Enhance user interaction Perform computations Because applets are often downloaded from the Internet, it is not safe to assume that they are free from hostile code. Therefore, some important restrictions have been placed on applets' functionality. These security policies can vary, depending on the browser and its settings. Applets are not normally given access to the local machine's file system. In this they differ from Java applications, which can read and write files on the hard disk. This is a major limitation for applets, but it is a necessary one from a security standpoint. If an applet could read and write files, it would be easy to create one that could corrupt or destroy data on the disk. Another restriction that is placed on applets is network connectivity. The only network connection an applet can normally make is with the host computer from which it was downloaded. Again, allowing an applet unrestricted network access would pose too great a security risk. In line with Java's object-oriented nature, an applet is itself an object. More precisely, an applet is an extension of the java.applet.Applet class. Because an applet is a class, it can be used as part of another applet or an application.

The Java Language:

One of the design aims of Java's developers was to keep the language as simple as possible. They looked at the features and design of existing programming languages. They incorporated many of the best of these features in Java. But they also discarded many features that had not proved worthwhile in practice. C and its object-oriented descendant C++ are two of the most powerful and widely used programming languages today. So it's not surprising that Java's syntax is very similar to the syntax of these languages. Programmers who use C and C++ will find it easy to move to Java programming. And, because of its relative simplicity, programmers with different backgrounds will not find Java as difficult to learn as C or C++. Java is highly object-oriented - everything in the language is an object, apart from the simple data types. These data types can be divided into the following categories: Numeric data types Character data type Boolean data type In Java, even the primitive data types can be encapsulated as objects if required. Java's integer numeric data types are always signed. The integer data types are as follows, regardless of platform: Byte - 8-bit
Page 6 Go To INDEX

Short - 16-bit Int - 32-bit Long - 64-bit Java implements floating-point numbers according to the IEEE-754 standard. The float type is a 32-bit value, while double is 64 bits long. Java's implementation of its numeric data types helps its cross-platform capabilities. For example, different C or C++ compilers can interpret an int value as 16-bit, 32-bit, or even 64-bit, depending on the computer platform. In contrast, Java always handles an int as a 32-bit signed quantity, regardless of the platform. Java's character data type char defines an unsigned 16-bit Unicode value. The 16-bit Unicode character set contains thousands of characters from many different world languages. This is unlike most other programming languages, which use an 8-bit value to store a character. Using the Unicode character set standard makes it much easier to internationalize and localize Java programs. Java has a boolean data type, which corresponds to the logical values of true or false. It is not possible to convert a Java boolean value into any of the other simple data types. Java's operators are similar to all the C and C++ operators. A useful addition to Java is that the + operator can be used to concatenate strings. In Java, as in other languages, arrays are groups of same-type variables. You can declare an array of any type, and you can also use arrays of arrays. Unlike C and C++, Java arrays are actually real objects. Java also treats strings as objects, and provides two classes to handle them. The String class is used for constant strings, while the StringBuffer class is for strings you may wish to modify. As in most programming languages, a string is written as text enclosed between double quotes. Memory management is one of the biggest challenges for programmers using traditional languages. Inefficient and incorrect memory management is often a cause of crashes and poor performance. In Java, the task of explicitly managing memory is removed from the programmer - the Java run-time environment does it instead. Java does not have pointers, or any explicit command to free memory. You can, however, invoke the garbage collector if required. The Java run-time system manages memory by keeping track of objects and their references. When there are no more references to an object, it becomes a candidate for automatic garbage collection. Java's garbage collector is a process that runs in the background, collecting and compacting unused memory. It runs in a low-priority thread, waiting for higher-priority threads to relinquish the CPU. A Thread is a portion of a program that can execute concurrently with other threads. Typically this happens when a user pauses while interacting with an application. The garbage collector is just one example of Java's use of threads. In fact, Java was designed from the beginning to support multithreading. It does this both at the level of the language itself and through support from the run-time environment and thread objects. This means that Java makes it easy to create applications that have several concurrent processes running. The Java multithreading model thus makes more efficient use of the computer's resources.

Page 7

Go To INDEX

Java's object environment:

In recent years object-oriented programming (OOP) has come to dominate the programming world. In OOP, an object can be thought of as a software model or representation of a real-world object. Although this seems a simple idea, it can be a very powerful and productive approach when properly applied. Many programming languages now claim to be object-oriented. However they often don't deliver the benefits promised by object orientation. This may be because an object model has not proved as successful in practice as expected. Or it may be because of compatibility requirements with older languages. Java was designed from the ground up to be an object-oriented language. Apart from simple data types, virtually everything in Java is an object. But in addition to the language itself, the Java environment contains a number of key classes that enable the rapid development of sophisticated programs. In object-oriented programming, a class is a template that defines the properties of a particular object. Java's classes are kept in a number of libraries or packages, which contain related classes. The most important of these are the core classes, which are a required part of all Java environments on any platform. The following are the core classes: The language package, java.lang The utilities package, java.util The Input/Output (I/O) package, java.io The networking package, java.net The language package is central to the Java language. It contains many classes that support the built-in aspects of Java. For example, it contains the Object class, which is a superclass of all Java classes. This means that any object you create in Java will inherit the variables and methods of the Object class. The language package contains two string classes for manipulating text. These are String (for constant text) and StringBuffer. There are many ways to create string objects and, once created, the string classes provide many powerful methods for working on them. The language package contains classes for wrapping the simple data types as objects. It also contains a powerful Math class, which provides many mathematical functions and constants. The utilities package java.util provides a number of classes that implement complex data structures. These structures represent such things as hash tables, stacks, and vectors. These are particularly useful for more advanced applets and applications. The I/O package java.io uses a stream model to represent all forms of I/O. For example, there are a number of classes for handling input such as: InputStream BufferedInputStream DataInputStream FileInputStream The input stream classes are mirrored by the output classes such as OutputStream, FileOutputStream, and PrintStream. Java's I/O classes are very comprehensive. They make it easy to handle normal I/O activity, but provide a large variety of tools to deal with
Page 8 Go To INDEX

special circumstances. The abstract window toolkit (AWT) is a very important package of Java classes. It simplifies the development of graphical user interfaces (GUIs) for both applets and applications. The AWT classes provide most of the functionality and components that are found in modern windowing systems. Because of Java's cross-platform capabilities, programs developed with the AWT can run on systems as diverse as X/Motif, Mac OS, OS/2, and Windows 95 and NT. In each case, the program will have the look and feel of the platform it is running on, without requiring any special coding for that platform. The java.net package contains classes that support network programming. Several of these classes deal with the TCP/IP protocols used on the Internet. You can implement programs that use FTP (File Transfer Protocol), HTTP (HyperText Transfer Protocol), and SMTP (Simple Mail Transfer Protocol). Java's classes make it easier to use these protocols than ever before, because they hide much of the low-level detail. Web programming is simplified by the URL and URLConnection classes. URL represents a uniform resource locator object. URLConnection accesses the content addressed by a URL object. Other java.net classes provide access to the bare network interface. These include the following: Socket - TCP socket ServerSocket - server TCP socket InetAddress - host IP address DatagramSocket - UDP (userdatagram protocol) socket

Java Security:

Java's cross-platform capabilities make it ideal for creating programs that can be distributed over the Internet. Many such programs are already available. Most of them are applets that can be downloaded from the Web and executed in a suitable browser. The wide availability of applets and the ease with which they can be executed poses special security problems. For example, the source of the applet is often unknown. This is in contrast to conventional software, where the manufacturer is known and the safety of the product can be assumed. An applet that has been developed maliciously can work in several ways. The most obvious of these is to disable or degrade the performance of the computer on which it runs. A more insidious form of attack is where the integrity of data on the computer is compromised. The most sophisticated type of malicious program can access data and send it to a remote computer, possibly that of a business rival. The designers of Java were very conscious of the types of security risk that could be posed. For this reason they built in several layers of security in the Java environment. The result is that it is very difficult to create a Java program that can act in a malicious way. The Java compiler provides the first line of defense in Java's security. While converting Java source code to bytecodes, it ensures that the language's safety rules are obeyed. The Java compiler ensures that accesses are not allowed beyond the end of an array. In Java, arrays remain the same size from their creation to their destruction. The compiler also performs strict type checking. This ensures that the compile-time type and the runtime type of variables are compatible. The Java compiler
Page 9 Go To INDEX

does not perform any memory layout functions - this only happens at runtime. Memory layout will depend on the hardware and software characteristics of the platform on which the program executes. Another difference is that Java doesn't have pointers like C and C++. Java compiled code uses symbolic handles instead. These handles are resolved to real memory addresses at run time by the Java interpreter. Java's lack of pointers or explicit memory management provides a major security benefit. It means that a Java programmer does not have the explicit control of memory that is sometimes used to create malicious programs. As you have seen, the Java compiler provides several features that ensure secure bytecodes. However there is no guarantee that an applet downloaded from the Web has been produced by an official compiler. Therefore another line of defense is provided by the Java runtime environment. When bytecodes are loaded into the runtime environment, they are subjected to a thorough examination by the bytecode verifier. It performs a number of checks on the code and will only pass it for execution if they are all completely satisfied. The bytecode verifier checks that the code conforms to the Java specification. It ensures that the code does not contain forged pointers, and doesn't access memory directly. It also checks for system access violations. When the code has been passed by the verifier, users can be confident that it meets a number of conditions: Parameter types of all bytecode instructions are correct There are no stack overflows or underflows Object member accesses are legal The bytecode verifier is very thorough and obviously takes a certain amount of time to carry out its checks. However when the bytecodes are passed, the interpreter can then run much faster, as it doesn't have to repeat any checks. A Java program that is running may request that other classes be loaded. Naturally these classes are checked by the bytecode verifier. They are then placed in separate name spaces according to the origin of the class. Bytecodes that originated on the local system are placed in the local name space. These bytecodes can be given more privileges if they are known to be safe. Bytecodes that were downloaded from the network are placed in a different name space devoted to that network source. When a class references another class, it first looks for it in the local name space that holds the built-in classes. If it doesn't find it there it searches the name space of the referencing class. This compartmentalization of classes adds a further layer of security to the Java environment. For example, imported classes can't emulate built-in classes, which may have higher security privileges. A number of security features are implemented in Java-enabled browsers. Compared to other programs, an applet's access to system resources is severely restricted. This ensures that applets downloaded from the Internet can't destroy or corrupt data on the local computer. Applets have only limited access to the host operating system. They can only access a limited number of system properties to help them perform their task. These include the vendor name and version number, and the filename separator character. An applet is only permitted to make a network connection to the host computer from which it

Page 10

Go To INDEX

was downloaded. These restrictions ensure that it would be almost impossible for an applet to transmit sensitive data to a remote computer.

1.2. Java and Object-Oriented Programming: • Object-oriented programming:

Object-oriented programming (OOP) is a powerful methodology for designing and creating reliable software systems. In OOP, an object is a software representation that models the characteristics of a real-world object. These characteristics may describe the state of an object.

For example, a car object might have properties that describe its color, engine size, and top speed. A software object can also describe the behavior of a real-world object. For example, a car object could have the following functions: Starting Accelerating Braking Turning The process of combining state and behavior information in a software object is called encapsulation. It is one of the fundamental principles of object-oriented programming.

An object's state information is stored in its instance variables. These are generally not accessible from outside the object. The behavior of an object is defined by its methods. A method is essentially a function associated with a particular object. Methods can be used to change the state of an object's instance variables. They can also be used to create new objects. Java uses the concept of a class to define objects. A class is an abstraction that represents a set of objects with similar
Page 11 Go To INDEX

characteristics. An object is a particular example, or instance, of a class. So we can regard a class as a template for creating objects. An object is created by instantiating a previously defined class. Its methods and the types of its variables are defined by the class. Because the object is now a real instance of the original class, its variables contain real values. This is why they are called instance variables.

A Java class's methods and variables may be marked public or private. Public methods and variables may be manipulated by code outside the object. In contrast, private methods and variables can't be accessed in this fashion. An object's instance variables are usually declared private. If it is necessary to change or access them, this is done through the object's methods. In this way the exact details of the object are hidden from the rest of the system. Instead, a clearly defined public interface is presented to the outside world. The ability to hide data behind a public interface is a key strength of encapsulation. For example, the implementation details of a class could be changed to more accurately model a real-world object. As long as the public interface remains the same, any programs using the class will be unaffected by the changes. So modifying an object-oriented program should be less problematic than modifying one written in a procedural language. Another benefit of encapsulation is that it encourages code reuse. Classes can be treated as black boxes with known inputs and outputs. A programmer need not be concerned by the inner workings of a class as long as the public interface is understood. A class defines the attributes and behavior of a potential object. It is possible to derive a new class from an existing class - a process called inheritance. Deriving a new class from an existing class is also called subclassing. The new class will inherit all the attributes and methods of the existing class. But it can also incorporate new attributes and methods to define a more specific type of the object being modeled. And it can also override the methods of the parent class, thereby changing its behavior. Using an automotive example, we could start with a class called Vehicles. This class would have a basic set of attributes and behavior common to all vehicles. We could then derive two subclasses - Car and Truck - from the Vehicles superclass. These will have specific attributes in addition to those of the Vehicle class.

Page 12

Go To INDEX

We could further subclass Car into SportsCar and FamilyCar.

The process can be repeated as often as necessary, building up a class hierarchy. Some object-oriented programming languages allow classes that inherit characteristics from more than one parent class. This feature is called multiple inheritance. Java doesn't allow multiple inheritance - it only allows single inheritance. This is because Java's designer's believed that multiple inheritance causes too many problems in practice. Although Java does not allow multiple inheritance, you can achieve many of its effects by using interfaces. An interface definition is a declaration of a set of methods. It does not explicitly specify how the methods should be implemented. Java permits classes to have multiple interfaces. In an object-oriented system, an object causes another object to perform an action by sending a message to it. The second object then invokes the appropriate method to perform the action. The parameters passed to the method are input values to the function it performs. In java, the objects passed to a method must match those expected. Alternatively they may inherit from, or interface with, the required types. This makes Java what is known as a strongly typed language. In a procedural language, you normally need two functions with different names to carry out two different tasks.

Page 13

Go To INDEX

In an object-oriented system a single message can invoke different methods, depending on the kind of object receiving the message. This is an example of polymorphism, which is a fundamental feature of an object-oriented language. Polymorphism means "one object, many shapes".

The origins of java

The origins of Java go back to work carried out at Sun Microsystems in 1990. A small team of software developers got together to develop a new programming language. The original purpose of the language was to control consumer electronic devices. Existing programming languages such as C and C++ were not considered suitable for this purpose. Consumer electronic devices such as TV set-top boxes and PDAs use a wide variety of processor chips. A program written in C or C++ has to be compiled to run on a particular processor. Therefore a program written in one of those languages has to be recompiled each time a new processor is introduced. Another disadvantage facing users of other languages is the small amount of program space available in consumer devices. The Java design team decided that a new approach was necessary. The new language would have to be small, fast, and robust. It would also have to be able to support widely differing types of hardware. The designers began by looking at the best features of C++ and similar programming languages. They developed an object-oriented language incorporating many of these features. The first application of the language was to control a small hand-held computer they built called the *7 (pronounced "star seven"). The user interface of *7 was a touch-sensitive color screen, which could be used to remotely control household appliances. The next application of the new language was to control TV set-top boxes. These devices were to be used to deliver interactive video-on-demand. The *7 and interactive video controllers did not evolve into commercial products. However they helped Java to develop and mature as a reliable and effective language. The next stage in Java's evolution is related to the development of the World Wide Web. The Web started as an Internet-based hypertext medium in 1990. By 1993, it was moving from a text-based interface to a graphical one, spurred by the development of the NCSA Mosaic browser. This development resulted in a huge increase in the use of the Web, which has since become the most popular part of the Internet. The development team soon realized that Java would be an ideal language for developing Internet applications. This was because many of the design aims they started out with coincided with the nature of the Internet. For example, the Internet is a heterogeneous collection of different computers, with many types of processors. These include IBM-compatible PCs, UNIX servers and workstations, and Macintoshes. Many of the PCs connected to the Internet have limited memory and processing power. Therefore a small, efficient, and platform-independent language like Java is ideal for programming Web applications. The first Web program developed by the Java team was a graphical browser. Originally this was called WebRunner, but it was later renamed HotJava. It was written entirely in Java, and functionally it was similar to other graphical browsers then available. However, it was unique in that it could load and run Java applets. HotJava was important in showing what the Java programming language was capable of.

Page 14

Go To INDEX

Its availability on the Internet generated a lot of interest among the developer community. Java technology was made public in early 1995 at the SunWorld conference. At the same time, Netscape announced that its Navigator browser would support Java applets. This support was delivered with Netscape Navigator 2.0, which arrived in late 1995. Netscape Navigator is the world's most popular Web browser. The fact that it now supports Java means that millions of people can run Java applets. This has been a powerful stimulus to the development of Java programs that perform many roles. It has also led to the continued enhancement of the Java programming language and its environment.

Java and C++

To understand the relationship between Java and C++, it is necessary to look at the history of C++ and its predecessor C. C is a structured programming language that has been around for over 25 years. Initially its success was linked to that of the UNIX operating system. UNIX and its associated shell tools were written in C. But C's many strengths helped it to become a very widely used programming language. The fast, compact, and portable code it produces can extract maximum performance from most processors. C has been used to write some of the most complex applications ever developed. However, large programs written in C very often have inadequate structure. This makes the programs difficult to write, debug, and maintain. To overcome these difficulties, the principles of object-oriented programming were applied to C. C++ is an object-oriented development of the C programming language. C++ uses the object-oriented principle of encapsulation to bind code and data into classes. C++ was originally called "C with Classes". C++ is also backward compatible with C. A program written in C is also a valid C++ program, and it will be compiled by any C++ compiler. The backward-compatibility of C++ enabled many C programmers to quickly move to it. However it also means that C++ is not as object-oriented as it could otherwise be. The designers of Java investigated C++ very thoroughly. When they were developing Java they used many of the best features of C++. They used the compact, elegant syntax of C++, and many of its object-oriented features. They also used features from other languages such as Eiffel, SmallTalk, and Objective C. For a number of reasons, Java's designers deliberately did not use some of the features of C++. Some features had been included to ensure compatibility with C. Other features that seemed fine in theory had never proved very beneficial in practice. Java is a new programming language - it is not a development of C++. However it draws on the best features of that powerful programming language. And its similarity in syntax means that C++ programmers find it easy to move to Java. One of the main differences between Java and C++ concerns the use of pointers. Pointers are sometimes considered one of C++'s strong points, but in practice they can cause many problems. Even a tiny error in pointer arithmetic can cause a memory location to be inadvertently overwritten. This error may not become apparent until millions of instructions later, when that memory address is used. This makes such errors very difficult to debug. The use of pointers also has security implications. Most computer viruses use pointers to modify memory locations with malicious intent. Instead of using pointers, Java uses handles, which are symbolic references to objects in memory.

Page 15

Go To INDEX

Handles are similar to pointers, but you can't increment or change a handle in the way you can change a pointer. Java does not use pointer arithmetic and doesn't allow data to be converted to a pointer type. This makes it easier to write reliable and robust code in Java. It also makes it difficult to write a malicious program such as a virus. Another area in which Java differs from C++ is memory management. C and C++ use the malloc() function to allocate memory and the free() function to deallocate it. C++ introduced the new and delete functions, which are used in a similar fashion to malloc and free. These functions often cause problems in programs written in these languages. For example, when free() is used on a pointer that has already been freed, the program usually crashes. And if a programmer forgets to free a memory location no longer in use, a memory leak occurs. Java uses the new operator to allocate space for an object on the memory heap. Instead of returning a physical address, new returns a handle for the object. The programmer does not have to worry about the actual memory location of an object - Java's memory management looks after this task. Java keeps track of the objects that are in memory and all references to them. When there are no longer any references to an object, the memory space it occupies is released for reuse. This is known as garbage collection, and it runs as a background process. Freeing the programmer from this type of housekeeping makes Java code more robust and reliable. Some of the differences between Java and C++ relate to the fact that Java is a more truly object-oriented language. Apart from simple data types, everything in Java is an object. For example Java does not have functions. Instead of creating a function, you define a class and create a method for that class. C and C++ permit you to use global variables. The use of global variables can cause many problems, especially in complex programs. When one part of a program modifies a global variable, it can have a completely unforeseen effect on a different part of the program. Java's object orientation is evident in the area of global variables. In Java, it is impossible to create a global variable that is outside the class hierarchy. Although Java allows you to have public static variables in a class, their use is not encouraged. C++ uses structures to create complex data types. Java does not have structures. If you want to create a complex data type in Java, you declare a class with the appropriate instance variables. One of the fundamental features of any object-oriented language is inheritance. C++ allows a class to inherit the characteristics of more than one class - a feature known as multiple inheritance. Java only allows inheritance from a single class. However, in Java you can achieve the equivalent of multiple inheritance by using interfaces. In this context, an interface is a declaration of a set of methods and constants no variables may be defined. One of the most misused features of many programming languages is the goto statement. It is often used simply to break out of nested loops, and its use encourages badly structured code. Java does not have a goto statement. Instead it uses the break and continue statements in conjunction with labeled blocks. One of the design aims of Java was to be a much simpler language than C++. To this end, Java does not have the following features of C++: Preprocessor
Page 16

#include <iostream>
Go To INDEX

#defines Typedefs Header files

#define pi 3.14159 typedef double (*(fp3)())[10]();

Some of these C++ features can make it very difficult for one programmer to understand another programmer's code. To understand how a C++ program works, you have to read all the associated header files, #defines, and typedefs. In Java, instead of using a typedef you simply declare a new class. Similarly, instead of using a #define you just use constants. Java doesn't need header files because the type information is compiled into a class file by the compiler. These differences between Java and C++ make Java code easier to write, understand, and reuse. C++ allows automatic coercion of a data element from one type to another, in such a way that precision can be lost. For example, the value of a floating-point variable can be assigned to an integer variable, thus losing precision. In Java you have to explicitly cast the floating-point variable to an integer before making such an assignment. Java's data types are similar to those in C++. However a problem with C++ data types is that their implementation depends on the compiler and the computer platform. For example, an integer data type may occupy 16, 32, or 64 bits, depending on the platform. In Java, each data type has a fixed size, which does not vary. This helps to ensure that Java programs will run consistently on all supported platforms.

1.3. The Java Language: • A Java applet - a step by step

Applets are small Java applications that can be downloaded over a network from a server and executed on a client machine.

The applet's name and location are inserted into an HTML document, using the <APPLET> tag. They are downloaded and executed automatically as part of the Web page. Applets are normally animated sequences of graphics or enhanced interfaces, but can be used for any purpose that Java's wide functionality will allow. Let's look at a sample applet now.
Page 17 Go To INDEX

The applet we will look at here is among the simplest possible. It displays a small message reading "Click Here" inside a rectangle on screen, and responds to a mouse click with a "Thank You" message. Additionally, some output is directed to the Java console and the status line of the browser.

The first two lines tell the browser to import two relevant packages. The first package is the AWT, or Abstract Windowing Toolkit, which contains classes and methods responsible for building the user interface and handling events. The second package, Applet, is responsible for defining the classes and methods associated with applets. Here we define a public class, called ClickHere that is an extension of the standard Applet class. The standard Applet class contains many methods that define the behavior of an applet on screen. We will see here just a small subset of those methods. "implements MouseListener" specifies that this class will handle mouse events. There are five mouse events that need to be implemented: MouseClicked MouseEntered MouseExited MousePressed MouseReleased We are only interested in mouseClicked events in this applet. DisplayString is a string variable that contains the text message we will display to the user. The initial value,
Page 18 Go To INDEX

when the applet starts, will be "Click Here". A String is the standard class used for string handling in Java. A series of methods is inherited from the Applet class. These methods are called in a predefined sequence: Init Start Update Paint Stop Destroy For the purposes of this example, we will override the init and paint methods and will let the default methods control everything else. The init method is the first method called in the sequence and is only called once - when the applet is first loaded. The init method is normally used to initialize variables. Here we tell the system to start sending any mouse events to the applet for handling. The keyword "this" refers to the current object - in this case the class ClickHere. The standard output is to the Java console, and here we have chosen to display a welcome message. If you have a browser, it will probably have an option to view the Java console. The Java console is useful for debugging applets. This is the paint method. It is called whenever the visual appearance of your applet is modified in any way. It is also called automatically when the applet starts. The paint method receives a Graphics object, which we have called g in this example. The setColor method sets the default color value. A set of predefined color values is available: black, white, red, green, blue, cyan, yellow, magenta, orange, pink, gray, darkGray, and lightGray. In this example, we have chosen to display the rectangle's border and the text messages in blue. This method call draws a blank rectangle at the uppermost left location, with a width of 150 pixels and a height of 80 pixels. The 0,0 coordinate is the uppermost left point in the applet's display area. Then we use the drawString method to place the contents of DisplayString on the screen, beginning at a point 50 pixels to the right and 20 pixels down from the origin (0,0). The mouseClicked method is called by the event handler when the user clicks the mouse button. The showStatus method sends output to the browser's status line, usually at the bottom of the browser screen. Here it presents the message "Welcome to ClickHere!". Here, we change the value of DisplayString to reflect that the mouse click event has occurred. However, the user will not see this new value until we rerun the paint method. Here we force the paint method to run again - with the repaint method call. In this example, we are not interested in handling any type of mouse event other than mouseClicked. However, the JDK 1.1 delegation event model specifies that when implementing MouseListener we must provide methods for all the possible types of mouse event. These
Page 19 Go To INDEX

four empty methods are included only to ensure compliance with JDK 1.1 when we implement MouseListener. Once you finish writing a Java applet, you need to compile it. C:\>javac ClickHere.java You do this by entering javac on your command line, followed by the applet file name. The applet file name must be identical to the class name, including the case of the letters used to type it. It must end with a java extension. So, in this example the file name is ClickHere.java. If the file compiles without errors, the compiler creates the class file. It is the same as the class name, but with a class extension. C:\>ClickHere.class To run an applet you need to call appletviewer, or embed the applet into a Web page and run your browser. To run an application, you simply type java on the command line, followed by the name of the java class. You use the java class name, rather than the java class file name as you might have expected.

Embedding an applet into an HTML document

The sample HTML document labeled FIGURE 1.3.1 illustrates how to insert an applet into a Web page. The applet is inserted into the body of the HTML document, between the <BODY> and </BODY> tags, at the place where the document's author wants it to appear. The <APPLET> and </APPLET> tags enclose all the information required for the browser to download and run the applet. This example uses just a few of the many attributes that can be associated with the <APPLET> tag. We will first look through this simple example and then go on to look in detail at the syntax of the <APPLET> tag. The first attribute to notice is CODEBASE. This tells the browser the location of the compiled class file of the applet. The CODEBASE can specify the location relative to the HTML source file or else as an absolute URL and directory. The CODE parameter specifies the name of the applet class file. This will be the compiled file of the applet. Finally, the WIDTH and HEIGHT parameters specify the size of the display rectangle containing the applet. All such size values are specified in pixels, not in inches, millimeters, or other absolute units of measurement. These sample HTML lines are enough to display simple applets. Now let's look at the full syntax of the APPLET tag, to see what other features are available. We will then see a fuller example of the APPLET tag in action.

Page 20

Go To INDEX

Words in uppercase, like CODEBASE, are the reserved attribute names. Attributes may be of mixed case in the final HTML document and they are illustrated here in uppercase for the purpose of clarity only. Attributes in square brackets [], are optional, while the others are mandatory. Let's go through each of the attributes in turn. The CODEBASE parameter specifies the base URL and directory of the file containing the applet code. Normally it is defined relative to the HTML source directory. Alternatively, it can be an absolute URL, pointing to a server and directory where the class resides. If the attribute is not specified then the default location is taken as the HTML document's base directory. The CODE attribute specifies the name of the compiled applet, and is mandatory. This value must be relative to the base URL of the applet as specified with the CODEBASE parameter, and cannot be absolute. Some browsers do not understand the <APPLET> tag at all. In that case, any HTML text between the <APPLET> and </APPLET> tags will be displayed in the normal way. Java-aware browsers will, however, ignore this untagged text and will try to execute the applet. Some browsers may not be able to run the applet, either because they are not fully Java-enabled, or because they refuse to run the applet, perhaps on security grounds. In these cases, the ALT parameter specifies alternate text, which can be displayed instead of running the applet. HTML authors are advised to use these parameters to provide alternatives for users who do not have Java-enabled browsers. The NAME parameter gives the applet a unique name within the HTML document. This allows for interaction between named applets on the page. The same applet can even be used more than once, but at different locations, in the document. The browser will only load the applet class file once. The WIDTH and HEIGHT parameters, in pixels, give the initial applet display area size. The optional ALIGN attribute specifies the alignment of the applet within the displayed page. If you are familiar with the <IMG> tag, then these values will be familiar. In fact, they are the same values and operate in the same way. The possible values of the ALIGN attribute are self-explanatory:
Page 21 Go To INDEX

Left Right Top Texttop Middle Absmiddle Baseline Bottom Absbottom The VSPACE (vertical space) and HSPACE (horizontal space) attributes specify, in pixels, the free space around the applet. This gives the HTML author greater cosmetic control of the applet. The <PARAM> tag allows the HTML author to pass parameters to the applet at run time. Parameters allow applet users - the HTML authors - to customize general-purpose applets for particular purposes. Applet authors can give their applets greater flexibility through the judicious use of parameters. The applet itself reads these values by using the getParameter() method and by specifying the name of the parameter they wish to retrieve. The two PARAM attributes are NAME and VALUE. The NAME attribute value is a string that is the name of the parameter. The VALUE attribute is the string representation of the parameter value. All values are passed to the applet as strings, so applets must convert the string to the data type they require.

Now let's see a larger example of an <APPLET> tag in an HTML document.

Page 22

Go To INDEX

Here we have specified that the middle of the applet should be aligned with the middle of the baseline of the preceding text, with 10 pixels of space surrounding it on all sides. We have also specified the text that should appear if the browser does not execute the applet for any reason. This applet takes three parameters: Speed Warning Exitmessage Their values, in turn, are: "23" "Do not click this button!" "Goodbye" Remember that the speed value, "23", is a string value, even though we may want to pass an integer. The applet developer must convert this string into its relevant value - in this case, an integer with a value of 23.

Applet example - address book

Let's look at a larger applet now. It shows how Java developers can implement a user interface and data storage across the Internet. We will look at how the applet appears to an end-user, and see how it was implemented in Java code. This applet allows you to view, add, and delete address book entries. Address book entries consist of a name, an address, and a phone number. The applet uses two buttons, Add and Delete, as well as three text fields for name, address, and phone number, and a scrollable box for listing the currently stored names.

Page 23

Go To INDEX

Here we have a full set of entries with the first name highlighted in the list. Tom's details then appear in the three fields to the right. Pressing the Add button creates a new entry at the bottom of the list. The values are initialized as "undefined". To add a name, you type it into the text box and press Enter. You can add a series of names, addresses, and phone numbers in this way. You delete entries by first selecting them in the scroll box. Then you press the Delete button, and the entry disappears. The Java code that implements the address book applet is included as a footnote. You can scroll through the footnote to see how real Java code looks. Further courses in SmartForce's Java curriculum will cover the Java language syntax and objectoriented techniques necessary for a full understanding of the address book applet code. Five classes are used in the address book applet: ValueChange Person PersonPanel PersonList PersonApplet The classes send each other messages in response to user events.

A Person object is made up of three strings, one each for name, address, and phone. It responds to messages by broadcasting changes to "Observers" (a Java core class). The ValueChange class holds a Person object, a description of the changed item, the new string, and the old string. PersonPanel displays the Person details and broadcasts changes to Person details by the user.
Page 24 Go To INDEX

PersonApplet displays two buttons (Add and Delete), a PersonList, and a PersonPanel. PersonList displays the list of Persons in the left hand side display box. It is a subclass of List, an AWT component. A List is a scrollable list of text items that a user can choose from. This code, for example, defines the ValueChange class as public and as an extension to the Object class.

It has four private variables: oDescription - a String type oObject - an Object type oValueNew - an Object type oValueOld - an Object type These private variables cannot be accessed by other classes. This code appears at the top of the PersonList file.

Notice how the first two lines import from the AWT and UTIL core class packages. Notice how the PersonList class is an extension to the List class. oStringChangedName is defined as a static String type, meaning that it is shared by all instances of the class. It is declared to be final, meaning that it cannot be changed by any inheriting subclass. Variables with the modifiers static and final before them are constants in Java. oVector is defined as a private Vector type. Vectors are Java's name for dynamically allocated lists of objects. This code appears in the PersonApplet class file.

Page 25

Go To INDEX

These lines, for example, instantiate two Button classes, called oButtonAdd and oButtonDelete. These lines add those buttons to the panel display area. These lines of code instantiate and then add a PersonList to the applet display area. This code, in the PersonApplet class, handles user events, such as clicking the Add and Delete buttons.

These lines test the event argument, aoEvent, to see if the user clicked inside the list box. If so, the code then tests to see if the Event was a LIST_SELECT event or not.

1.4. Java in Practice: • JavaBeans:
Go To INDEX

Page 26

An important advantage of object-oriented languages, such as Java, is their ability to reuse software components. Software reuse reduces development time and cost, while increasing flexibility. Also, software from third-party vendors, in forms such as tool kits, can be purchased by developers and integrated with their own software. The ability to integrate software components from different, independent sources is provided for in Java by the JavaBeans API specification. Java beans are reusable software components that are designed to be: Simple to use Compact Portable across multiple platforms They enable the reuse of existing software and the dynamic integration of independently developed software elements. In keeping with the Java philosophy, JavaBeans is a platform-neutral component architecture model. The JavaBean API is compatible with existing component architectures, like: IBM and Apple's OpenDoc Microsoft's ActiveX/COM/DCOM Netscape's LiveConnect OMG's (Object ManagementGroup's) CORBA Java beans link to these platform-specific component architectures through bridges developed and supported by JavaSoft's industry partners. Java beans can be GUI widgets, non-visual functions or services, or larger applications. The emphasis during the design of JavaBeans was on the smaller software components likely to be distributed over networks. But JavaBeans also support much larger software applications. A component model architecture typically provides five types of service: Component interface publishing and discovery Event handling Persistence Layout control Application builder support Components register their interfaces so that they can receive and process event notifications from other components. This means that the two interacting components can be linked dynamically at run time, rather than at build time. The component can export properties that can be modified or accessed by external components. These properties are accessed or modified through the component's public methods. Events are the means by which two components interact at run time. Events are raised by one component and are then delivered to the appropriate component for event handling. The persistence mechanism enables the storage of component-state information in a non-volatile medium for later retrieval. This means that the two interacting components can be linked dynamically at run time, rather than when they are built.

Page 27

Go To INDEX

An automatic Java serialization method is available for simple persistence. A layout mechanism defines how components are visually presented. This mechanism defines a component's layout, both within its own space and in relation to the other visual components of a container. A container holds an assembly of related components. Builder tools enable you to assemble an application using user-friendly GUI tools. But builder tools must have access to the internal properties of components and be able to modify them at design time. Builder tools use application builder support interfaces to determine a component's properties and available methods. A component developer can define design-time property editors within the component. Property editors allow greater flexibility in the use of components. A bean must be able to run in the design environment of the builder tool. This allows users to customize the action and appearance of the bean. JavaBeans is designed to work well for distributed applications. The JavaBeans API is optimized for local, virtual machine operation rather than network access. Three primary network access mechanisms are available to JavaBeans: Java Remote Method Invocation(RMI) CORBA IDL JDBC - Java Database Connectivity These are either established industry standards or adaptations of them. JavaBeans will extend Java's strengths in key areas, while still retaining its platform independence

What's new in JDK 1.1?

The latest Java Development Kit, JDK 1.1, includes new versions of the Java Virtual Machine, Java class libraries, and development tools. JDK 1.1 has major quality, performance, and feature enhancements, but is still fully backward compatible with JDK 1.0.2. The major new features include Internationalization Security, AWT, and networking enhancements JAR files Remote method invocation Object serialization They also include Reflection JDBC - Java database connectivity Inner classes New Java native method interface Performance enhancements Let's look at each of these new features.

Page 28

Go To INDEX

Internationalization allows the development of localizable applets. Developers can create applications that can readily be adapted to different languages and country-specific standards, like time and dates. This extends the "Write Once, Run Everywhere" Java design goal. The internationalization features include: Use of the UNICODE 2.0 character set standard A locale mechanism Localized message support Locale-sensitive date, time, time zone, and number handling They also include Collation services Character set converters Parameter formatting Support for finding character/word/sentence boundaries The previous version of the Abstract Windows Toolkit (AWT) was inadequate in many respects. JDK 1.1 features a greatly enhanced AWT, with a number of strong performance improvements, particularly for Windows environments. These changes enable Java applications to assume the look and feel of the user interface of the platform on which they are running. The AWT 1.1 lays the groundwork for the upcoming JavaBeans component model architecture. It contributes to a richer infrastructure for larger-scale GUI development. The new AWT is compatible with the earlier AWT, so existing Java applications and applets will continue to run. AWT 1.1's enhancements include: Printing Easier and faster scrolling Pop-up menus Mouseless operation Clipboard support They also include Desktop colors Separate cursors for each component Delegation-based event model Imaging and graphics extras More flexible font support for internationalization JDK 1.1 allows developers to create distributed Java-to-Java applications. The methods of Java applications on other Java virtual machines can be accessed through Remote Method Invocations (RMIs). RMIs will even connect applications on different host computers. References to remote objects must be obtained by the client, or calling, application. A reference can be obtained in one of two ways:

Page 29

Go To INDEX

By using the bootstrap-naming service provided by RMI Through receiving the reference as an argument or return value RMI relies on Object Serialization to pass parameters and method calls. Object Serialization, an extension of the core Java I/O classes, enables the encoding of Java objects into streams of bytes and their subsequent decoding. This means that RMI does not need to truncate types, thus supporting true object-oriented polymorphism. A standard SQL database access interface is provided through Java Database Connectivity (JDBC). It provides uniform access to a wide range of relational databases. JDBC is implemented in terms of the ODBC standard C API, the de facto standard for creating platform-independent interfaces to enterprise databases. The Java security API allows developers to incorporate low and high-level security features into their applications. Eventually, Java security will feature cryptography, key management, and access control. JDK 1.1 includes a subset of this functionality, comprising: Digital signatures, including the ability to sign classes, images, sounds, and other data Message digests to give unique identifiers to digital data Key management Access control lists JAR (Java ARchive) is a platform-independent file format based on the widelyavailable ZIP format. It is useful for archiving groups of data files into a single compressed file format. Additionally, it is ideal for making HTTP transfers of applets more efficient. Using this format developers can aggregate and compress multiple files associated with an applet, including images, sounds, and the class file itself. A single HTTP request will then suffice to download all of an applet's associated files, which is much more efficient than downloading them separately. JAR files are compressed using the ZIP format, further improving efficiency. Additionally, individual entries in a JAR file can be digitally signed to authenticate their origin. JAR is written in Java, so is fully extensible. JAR files are fully backward compatible with earlier applet code. The Java reflection API provides the ability to obtain information about Java objects, within certain security constraints. Certain types of applications need to use reflection to discover underlying information about classes in the Java Virtual Machine at run time. For example, Java beans might require run time access to the public variables, methods, and constructors of a component. Debuggers, interpreters, inspectors, and class browsers require run time access to the implementation of a class in the class file. JDK 1.1 allows Java developers to apply the concepts of lexical scoping and block structure to their Java code, using inner classes. Lexical scope refers to the range of code within which an object remains active or available. Block structured languages promote the hierarchical structure of code, in which some procedures are implemented within higher-level procedures and their scope, extends only to its enclosing procedures. Lexical scoping and block structuring greatly simplify many types of development problems.
Page 30 Go To INDEX

Inner classes are defined as members of other classes, either locally within a block of statements, or anonymously within an expression. Previously, all classes were members of packages and were top-level classes only. Inner classes can use both class and instance members of enclosing classes, and local variables of enclosing blocks. Inner classes are implemented at compile time; so do not require any changes to the Java VM. Inner classes can be used to implement adapter classes. An adapter class is an object's interface to the outside world. For example, an adapter might receive events from AWT and JavaBean components for passing to its enclosing object. In Java 1.1, an adapter class is most easily defined as an inner class placed inside the class, which requires the adapter. Code written in another programming language called native methods - may often need to be accessed by a Java programmer. The reasons for using native methods may include the wish to use: Existing libraries written in another language Time-critical code written in assembler Platform-dependent features accessible only through anon-Java language Use of native methods is not encouraged as it breaks the portability and security models of Java. Native methods should only be used as a last resort. JDK 1.1 introduces a new Java Native Interface (JNI), with the primary goal of having binary compatibility of native method libraries across all Java VM implementations on a given platform. Java developers should be able to write a single native application that will work, without change, with all Java VMs supporting the JNI. By programming through the JNI, you can use native methods to: Create, inspect, and update Java objects, including arrays and strings Call Java methods Catch and throw exceptions Load classes and obtain class information Perform run-time type checking Finally, JDK enhancements: 1.1 also introduces some platform-specific performance

Interpreter loop in assembly code on Win32and Solaris/Sparc Non-contiguous heap support on Mac Monitor speed-ups AWT peer class re-write for Win32 Garbage collection of classes Optional use of native threads on Solaris

The potential of Java:

Java is still an evolving language and development platform. Demands from the development community mean that Java will continue to see new core APIs, industry

Page 31

Go To INDEX

initiatives, tools, applications, and other additions. Some of these additions, which will greatly increase Java's potential, include: The 100% Pure Java initiative Proposed expansions to the Abstract WindowsToolkit (AWT) Network computers and HotJava Views Let's see what some of these initiatives will entail. 100% Pure Java is a marketing and technology initiative, which aims to ensure cross-platform compatibility of all Java network applications. All applications seeking the designation "100% Pure Java" must pass a test suite designed to ensure strict compliance with the Java specifications. All such applications will be officially certified and allowed to carry the 100% Pure Java logo. There will be assistance for Java developers trying to pass their applications through the test suite. A marketing drive for certified products is also anticipated in the near future. Products may also receive Web-site exposure through a "100% Pure Java Hall of Fame". JavaSoft see their Abstract Windows Toolkit (AWT) as a critical component of Java. The extensions to the AWT in JDK 1.1 are the first phase of a planned expansion to Java's GUI capabilities. The next phase, due in 1997, will include many proposed new features. The planned features include: A lightweight component framework Peerless components Pluggable look-and-feel Drag-and-drop capability Java 2D for complex rendering Let's look more closely at these planned features. In AWT 1.0.2 there is a one-to-one mapping between components and windows, resulting in three main problems. Firstly, windows are inefficient, so having too many of them is a bad way to implement multiple components. Secondly, windows are opaque - they do not allow transparent regions. Thirdly, consistent handling of windows across different platforms is difficult. The solution to the windowing problem is to make components that can share the windows of their containers. So, new windows do not have to be opened to display these components. These new lightweight components will ensure much lighter weight interfaces. And they are all implemented in Java code, ensuring their platformindependence. Some application developers require a common look-and-feel for their Java applications, even across different platforms. Currently, the Java AWT delegates look-and-feel responsibilities to sets of platform-dependent peer classes. Unfortunately, this sometimes makes component appearance hard to alter. The AWT must account for cross-platform inconsistencies, and there is no option for a common look-and-feel. Peerless components and pluggable look-and-feel will allow developers the option of creating cross-platform common look-and-feel. The peer option
Page 32 Go To INDEX

will be retained for those who want the applications to have the look-and-feel of native programs. Pluggable look-and-feel will allow customization of look-and-feel at several different levels. A new general data transfer architecture and an API for clipboards is included with JDK 1.1. This will be extended to include a drag-and-drop API in a future release of the AWT. Only basic rendering capabilities are included in JDK 1.1. A new Java 2D API will have many complex rendering facilities, including Arbitrary transformations, such as rotation and scaling Image composting Sophisticated text handling Java's distributed cross-platform nature qualifies it as the language of choice for distributing software to network computers (NCs). PCs and their applications were designed to cater for the broadest possible set of users. Because of this, applications are generally large, monolithic, and function-rich. These applications require large amounts of disk space. In addition, software updates are delivered as upgrades to existing applications, an expensive and often time-consuming operation. Java will change this software distribution model. It is hoped that much smaller and easier-to-maintain network computers (NCs) will replace many PCs. Network computers do not require local disk storage. Instead, they rely on retrieving application software from a central server over a network. Software is downloaded to the NC when, and if, a user requests it. Users will only use the software and functionality they absolutely require. Regular manual upgrading of application software is eliminated. Users can switch hardware while still retaining their own individual environments, which would be stored centrally. Unlike the old model of centralized mainframes serving a network of dumbterminals, network computers do their own computing. By using Java, administrators can provide users with many types of hardware, from NCs to PCs, knowing that the central application software will run across all those platforms. Thus, networks of NCs are scalable. HotJava Views is a specially written user environment for some NCs. It is drastically slimmed-down compared to a typical PC environment, with its main functionality consisting of e-mail, calendar and scheduling, a name directory, and internet browser. System administrators will benefit from HotJava Views because of: Zero client administration costs Minimal software distribution costs Easy network computer replacements Users will benefit from Ease of use Powerfully integrated, yet simple, applications A virtual user environment regardless of location

Java development environments:
Go To INDEX

Page 33

Java is still in its infancy as a language, but some important development tools have already been created for it. These tools make the full power of Java available to applications developers through sophisticated visual development aids. Most of them also include ready-made Java components for reuse by programmers. Let's have a quick look at the main features of some of these development environments now. The tools we will discuss are Sun's Java Workshop IBM's Visual Age Symantec's Visual Café Borland's Open JBuilder/Latte Microsoft's Visual J++ Sun's Java WorkShop tool is written almost entirely in Java and HTML. The tool is embedded in a browser-like environment, and its screens look like traditional web pages. Its screens are navigated using standard browser controls. This allows running and debugging of applets in the development environment, without having to launch another program. You use a Visual Java design tool to define the visual aspects of the application, and then generate code around it. Help is in HTML format and an on-line tutorial is also provided. The debugger is fully integrated with the tool's environment, providing a smooth development and debugging cycle. Because of its design features, Sun's Java WorkShop is highly portable. The Java WorkShop environment is easy to adapt to, since it relies on familiar browser interfaces. It illustrates well the Web-friendly aspects of Java. IBM's VisualAge for Java tool emphasizes support for applications that need to access centralized databases. This tool adheres to Java's JDBC standards for database access. IBM is also developing JDBC drivers for its own database architectures. Also, JavaBean components can be built from database schemas obtained through VisualAge's Data Access Builder. These beans can then be combined visually with other beans to form the finished application. Application developers using VisualAge for Java can make use of a variety of protocols, including CICS, to communicate with databases. Also, code can be altered during execution, a very useful feature when large applications are involved. Symantec's Visual Café builds on their earlier Café product. It combines a visual, component-based programming model with an advanced Just-in-Time (JIT) compiler and virtual machine. In fact, Netscape are to integrate the JIT compiler into their Web browsers, for extra efficiency and performance. The source code editor supports the highlighting of keywords for both Java and HTML. Classes are managed using two tools: The class browser The hierarchy editor Visual Café provides full support for both the new AWT and JavaBeans. Its visual interface allows you to place controls such as buttons or labels onto a screen and generate the code automatically. There is even a SlideShow component for combining
Page 34 Go To INDEX

.PNG and .JPEG files into applets. Visual Café allows the two-way control of visual objects. Changes to the visual object automatically results in changes to the underlying code - and vice versa. This is an important advantage when adjusting visual interfaces. Visual Café's advanced features include a very fast Just-in-Time (JIT) compiler, an Interaction Wizard, and a debugger. The Interaction Wizard, for example, allows you to generate code handlers. You place an object over another on screen. The wizard then presents you with a list of options to choose from. The option defines the interaction that you want for the two objects and the code is then generated automatically for you. Borland's Open JBuilder/Latte tool follows closely their earlier Delphi product. It aims to be a high-productivity rapid applications development (RAD) environment. The basic components Open JBuilder/Latte uses will all be Java beans. All Open JBuilder/Latte's tools are two-way - meaning that modifications to code are reflected immediately in the appearance of components, and vice versa. Borland have designed Open JBuilder/Latte to be freely extensible. Third-party tools can be added at will to the basic configuration. Over time, a suite of such tools is expected to enter the marketplace. Open JBuilder/Latte's database access facilities include JDBC drivers for ODBC and Borland's own BDE. Microsoft's Visual J++ builds on their Visual C++ tool. It emphasizes a traditional 3GL development paradigm and has: Enhanced source file editing Excellent debugging features Support for ActiveX components. You edit source files using a FileView tool, which features customizable syntax highlighting of Java and HTML, and keystroke recording. You can generate application and applet versions of your code by setting a simple flag. Passing parameters from HTML <APPLET> tags is also made easy, and default HTML pages can be generated for your applet. When debugging, you have full control over watchpoints and breakpoints. You can inspect a variable's values by placing the cursor over it, or by dragging it to a watch window. Visual J++ also includes Microsoft Internet Explorer 3.0, allowing you to debug multiple applets on the same HTML page. Visual J++ supports ActiveX components, and Microsoft are planning for ActiveX/JavaBeans interoperability.

Internet case studies:

The Web was not specifically designed for client/server computing. You can try to develop client/server applications using the HTML/CGI protocols. However, there are many disadvantages to this approach. Firstly, there is no client-side validation of CGI forms. Every form, once filled-in, has to be sent to the server for validation, resulting in needless network traffic and long delays. And transaction-oriented applications do not work because HTML/CGI interactions are

Page 35

Go To INDEX

"stateless". That is, once information is passed, the connection between the client and server is broken. You must explicitly manage the state of the transactions between the client and the server - a time-consuming and difficult task. You can only access database records one at a time, which means users will often have to waste time waiting for extra records to be downloaded from the server. You will not be able to scroll through lists of related records, except in the most limited of ways. Now, Java extends the Web's capabilities to include complete client/server applications. Java applets overcome the limitations of HTML pages alone, or HTML combined with CGI. Because Java is a complete language, all business-logic validation can take place on the client side, reducing network traffic and speeding up interactions considerably. Datasets can be cached locally and navigated by the Java applet within its own memory space. And state management logic can be built into the Java applets and applications. Unify Corporation has used Java in this way to create a client/server development tool that overcomes traditional HTML/CGI weaknesses. VISION/Web, an extension to Unify's mature VISION product, is a high-end 4GL development tool that generates Java code. Unify's VISION is a mature product, offering automated GUI programming and data access capabilities. The VISION run-time environment has been completely rewritten in Java to produce VISION/Web. VISION's message-based architecture means that state management is built-in and transparent to the developer. VISION/Web uses native database drivers on the server side to greatly speed up database access. Entire applets can be written without recourse to HTML, CGI, Java, or JavaScript programming. There are some minor limitations to VISION/Web when compared with the VISION tool. The VISION/Web development environment restricts screen widgets to those provided for in the Java AWT. Some 4GL statements are not translatable into Java code, such as attempts to invoke system calls or access the hard disk. VISION/Web provides A secure virtual machine environment Platform and browser independence Client-side business logic Traditional network applications suffer from several constraints such as: Limited bandwidth across the internet reduces the practical size of applications. Applications written in most languages are platform-dependent. They are constrained by the HTML/browser framework within which they run. Marimba Inc. is using the power of Java to help eliminate these problems. Their Castanet system offers a new software distribution paradigm, which will overcome bandwidth limits, platform dependence, and security concerns. Marimba Inc. was founded by members of the original Java development team and is supported by JavaFund. Kleiner, Perkins, Caufield, and Byers (KPCB), a leading
Page 36 Go To INDEX

venture capital firm focused on technology companies, announced the $100m JavaFund in August 1996. JavaFund is designed to promote Java-related ventures. Java's built-in platform independence and high security have made Castanet possible. Castanet is comprised of Channels Tuners Transmitters Channels are applications and content delivered over the internet like applets, but which operate like applications and are stored locally on the user's hard disk. Channels are ideal for applications requiring real-time regular updates to content or functionality. Tuners are client-resident programs that download, install, and request updates for channels. Transmitters deliver the channels to tuners. They use differential updating techniques. This means that only those files that change need to be redelivered to the user. You use channels much like any other locally resident application. But you will also obtain regular updates to the channel, completely transparently. You might get access to sports results, interactive games, news updates, or financial reports.

Page 37

Go To INDEX

2 Java for C++ Developers
2.1. Java and C++: • Overview of principal similarities:
If you have used C or C++ for some time, then Java syntax will appear familiar to you. Java shares its look and feel with C and C++. These similarities will ease your introduction to Java. In fact, you should be able to guess what these sample lines of Java code do, even if you have never seen a Java program before.

This is a comment, followed by a while loop with two statements in its body. Notice that this form of comment is the same in Java as in C and C++. There are two other types of comment in Java. We will cover them later. And Java also has while loops, as in C and C++. And notice the use of the “!” operator too. Incrementing a variable with the shortened C syntax - using the ++ operator - is also possible. And assignment with an operator is also possible. Braces delimit statement blocks, just as in C and C++.

Page 38

Go To INDEX

Despite these similarities at the syntactic level, there are quite significant differences between Java and C++. Java has no: Pointers Struct, union, or enum

Typedef or #define Sizeof #include But don't worry - these features and keywords, and the concepts they represent, have been implemented in a simpler and safer way in Java. C++ was designed specifically as an object-oriented extension to C. It is a superset of C, and C programs will compile as C++ code. Another way to put it is to say that C++ is backwardly compatible with C. Java is wholly new, and was not designed to be backwardly compatible with any other language, including C++. However, the designers did base much of the syntax on C/C++. Statement separators, flow control statements, operators, and array access are all largely the same. You will recognize Java keywords like: While

If, else Switch, case For

Int,

Page 39

Go To INDEX

float, double

Java and C++ have many common primitive types. However, Java is much stricter when defining implementation details and default values for these types. Numeric types have strictly defined bit sizes, for example.

And all numeric variables are set to zero in the absence of explicit initialization. The if/else, do/while, while, switch/case, and for flow control statements are all nearly identical to those in C/C++. The main difference is that Java conditions return boolean false or boolean true, not 0 or 1 as in C/C++.

Of course, you will quickly notice several key differences between Java and C++. As we have already seen, pointers are missing in Java. As a result, memory handling, linked lists, and parameter passing are all implemented differently in Java. And there is no need for the sizeof keyword either, since memory allocation for objects is handled transparently by Java. The preprocessor, header files, typedefs, and #defines have all been removed or replaced. Arrays are declared and allocated differently, but are otherwise quite similar to C/C++ arrays. Strings also bear many resemblances to C++ strings, but the + operator for string concatenation is built-in to the language. Structs, unions, and enums have all been removed. But they can all be implemented in other ways in Java. In general, Java and C++ share enough features to make it easy for you to learn Java. But you should be wary of assuming that superficially similar code is, in fact, the same.

Page 40

Go To INDEX

Common Syntax:
Most of the basic syntax in Java is identical to C/C++.

The semicolon still separates statements in Java. Braces enclose statement blocks. Parameters still appear between parentheses (). Array elements are accessed using valid integer expressions within square brackets []. Java code is free-format. You are allowed to indent your code in whatever way you like. And you can put as many spaces or lines between statements as you think appropriate. Assignment, and assignment with operators have been retained in Java. You can use three types of comment in Java. The first begins with a /* and continues until the first */ sequence is found. You are not allowed to nest /* */ comments. As in C++, you can insert a comment on a line starting with two slashes // and continuing until the end of the line is reached. A good tip is to use // comments within your code blocks, so that if you want to comment out a section of code later, you don't have to worry about inadvertently nesting /* ... */ type comments. There is a special "doc" comment beginning with /** and continuing till a */ is reached. Doc comments are usually placed before declarations. This type of comment is used in conjunction with the javadoc tool to produce online documentation. Identifiers are composed of letters and, optionally, digits. But they must begin with a letter.

Underscores (_) and dollar signs ($) count as letters too.

Identifiers are case-sensitive. So, "theLargestInteger" is not equivalent to "thelargestinteger".

Page 41

Go To INDEX

Identifiers can be of unlimited length. This allows you great flexibility in choosing meaningful names for your classes, variables, and methods.

Types and arrays in Java

Java emphasizes simplicity, platform-independence, stability, and ease of use. These characteristics are especially apparent, in comparison to C/C++, in the primitive data types. The full list of primitive types in Java is: boolean char byte short Int Long Float Double You will notice that Java has the same primitive types as C, but adds the two new types of boolean and byte.

The boolean type has only two possible values - "true" or "false". It replaces the conditional expressions of C/C++, where 1 or 0 is returned. This makes programs more readable and less open to confusion. Having a boolean primitive type means that certain "tricks" in C/C++ are no longer valid in Java. For example, testing for a decrementing counter to reach 0 is not valid. So the line "while (i--)" must be replaced with an explicit test that returns a boolean value, like "while (i-->0)".

Page 42

Go To INDEX

Booleans may not be cast to or from any other types, including integers. The new type, byte, is an integer of length eight bits. It is a useful primitive type when dealing with I/O and lower-level data manipulation. Java's implementation rules for numeric types are much stricter than those for C or C++. All integers are signed and each type occupies a strictly defined number of bits. You can be guaranteed that when you use, for example, a short integer, it will be implemented in 16 bits. This is important for ensuring platform-independence.

In C or C++, you cannot assume anything about the value of uninitialized variables. In Java, there are standard default values for each type.

If you do not explicitly initialize variables, they will be set to this default value by the interpreter. All integers (byte, short, long, and int) are signed. Variables of type byte, for example, range from -128 to 127, and short integers from -32768 to 32767. This means that you cannot use the keyword "unsigned", as in C/C++. Integer literals in Java are equivalent to those in C and C++. Here are some valid bytes as they could be initialized.

Hexadecimals and octals are defined by preceding the literals with "0x" (zero x) or "0" (zero) respectively.

Page 43

Go To INDEX

You place the letter "L", in upper or lower case, after an integer literal to specify that it is a long integer. Floating-point literals can have an "F", or "f", appended to indicate that they are of type float. "D", or "d", indicates that they are of type double.

These are examples of valid float and double literals.

The letter "e", or "E", denotes an exponential value. For example, 7e3 evaluates to a float value of 7000.00. Variables of type char are 2-byte, 16-bit Unicode characters. In most cases this makes no difference to the way you use characters or strings. The first 128 Unicode characters are identical to the ASCII set used in C. Using Unicode makes internationalization easier.

Some Unicode character literals may not be displayable on certain platforms, so Java offers a way to refer to them using the "\u" escape sequence. These are valid Unicode characters.

Notice too, the use of standard C/C++ escape sequences. String literals are defined between double quotes, as in C.

Page 44

Go To INDEX

You are also able to use the + string concatenation operator. Arrays in Java closely resemble arrays in C and C++. Arrays are non-primitive types, and share features of the Java Object class. Both objects and arrays are passed by reference, whereas primitive types are passed by value. For this reason, objects and arrays are called the reference data types. You declare and instantiate arrays in much the same way as other objects, using the "new" keyword. Square brackets in declarations, either after the type or object identifier, or after the array identifier, indicate that an array is being defined.

Arrays can also be statically initialized. The compiler automatically calculates the array size.

Multi-dimensional arrays are defined, as in C, as arrays-of-arrays.

Just like C, multi-dimensional arrays can be non-rectangular, or "triangular". Triangular arrays have to be allocated dynamically in C, while they may be allocated statically in Java. This example shows how a two-dimensional array of arrays of varying sizes is allocated statically in Java.

You access array elements exactly as in C - by putting a valid integer expression between square brackets after the array name.
Page 45 Go To INDEX

The Java interpreter checks array bounds, and if they are too low or too high it throws an exception. Exception handling in Java will be covered in a further unit. Strings are implemented as instances of a class called String, in the java.lang package. Packages and classes will be fully explained in Unit 2, Overview of classes. They are not null-terminated arrays of characters, as they are in C/C++

Having a class for strings means that you can use a full range of methods to manipulate them. You can convert booleans, integers, and floating-point numbers to strings using the valueOf method. For example, this code initializes two String types to represent an integer and a double respectively.

Strings (that is, String objects), once initialized, are immutable - there is no way for you to change their contents. You must use the StringBuffer class instead. Bounds checking takes place at run time, as with arrays, and exceptions are thrown if invalid accesses are attempted. Strings can be initialized with double quotes ( " ) and, optionally, the + operator. The + operator concatenates two strings.

Flow controls and operators:

The if/else, while, do/while, switch/case, break/continue, and for flow control statements are almost identical in Java to their C/C++ equivalents. There is a try/catch/finally statement for use in exception handling. This will be covered in Unit 3, Simplifying C++. You are, however, forced to use proper boolean expressions, that

Page 46

Go To INDEX

evaluate to either true or false, in test conditions. That means, for example, that testing for an integer to decrement to zero is not valid in Java.

Additionally, the test expression must be placed between parentheses, as in C and C++. The switch statement is the same in Java as in C/C++. Here we show a simple switch statement.

Values of type short, int, byte, or char can be used as values for the case labels. You may not use long integer types as values for case labels. The goto statement is banned in Java, and is replaced by a combination of the break and continue statements and labeled loops.

You may notice that "goto" is a reserved keyword in Java, but is not implemented. A compiler error will result if you try to use "goto" as an identifier in your code. A break statement inside a while, do, or for loop stops the execution of that loop, as in C/C++. This code, for example, shows two break statements - one inside a for loop and another inside a while loop. The key difference in Java is that you can label loops with identifiers, followed by a colon, as in this example.

Page 47

Go To INDEX

Notice the top_loop label before the while keyword. An optional label can follow the break keyword. If there is a label following the break, then control transfers out of the enclosing statement that the label refers to. You use "continue" in much the same way. It stops the execution of the current loop cycle, but restarts at the beginning of the loop again. Java supports nearly exactly the same set of operators, as does standard C.

Generally, you can assume that all operators you have used in C or C++ are usable in Java. But Java does not support one or two C/C++ operators, and has some extra ones of its own. We will consider these here. You cannot combine two or more expressions into one using the comma operator.

But you may use the comma operator to declare and initialize variables. And the "for" statement allows the comma operator, but only in its initialization and increment sections. Java has no pointers, so there is no need for the reference and de-reference operators, "*" and "&" respectively. And the sizeof operator is redundant for the same reason. Java does not treat array access and field access ("[]" and "." respectively) as operators. The + operator for Strings is a new operator. Although it looks like operator overloading, it isn't, because Java does not encourage operator overloading.
Page 48 Go To INDEX

Integer numbers are all signed.

Therefore, the right shift operator, >>, maintains sign. The >>> operator is added to allow for unsigned shifting. The “&” and “|” operators, when applied to Boolean types, evaluate both their operands, even if the result is determined after evaluating the first operand. The “&” and “|” operators applied to integral types do bitwise evaluations as in C and C++. However, “&” and “|” applied to Boolean types in Java perform Logical "AND" and Logical "OR" respectively. You use “&&” and “||” operators to perform boolean evaluations without side effects. Here, you can clearly see that (i++>100) evaluates to false, and that the whole expression then evaluates to false.

But using the “&” operator, the int j is incremented – a side effect of evaluating the second operand. The "&&" operator would not increment j, since it would cease evaluating the boolean expression after finding the first operand false.

2.2. Overview of classes: • Introduction to classes and objects:

Classes are the fundamental units in Java. A class is a collection of data and related methods that operate on that data. In fact, all data and functionality in a Java program are organized into classes. Java's insistence on organizing data and methods into classes is much stricter than C++'s object orientation. Java uses classes to implement the key object-oriented concepts of: Encapsulation Inheritance Polymorphism Encapsulation involves hiding data within a class and only allowing access to it through methods you define. Inheritance involves creating subclasses that inherit the data and methods of their ancestor classes. Polymorphism involves using the same method name to operate on multiple classes. Classes are the mechanism through which objects are defined in Java. Objects are instances of classes. Classes define a 'template' for objects, while objects are dynamically created to represent instances of those classes. So a running Java program consists of interacting objects, which are all instances of classes.

Page 49

Go To INDEX

Many instances of a single class can exist side-by-side in a Java program. Methods operate on the data in the class. Data may include primitive types or other classes. The uppermost class in the Java class hierarchy is the Object class. Another way to put it is to say that it is the root class. All other classes inherit from this Object class, either directly or indirectly. That is, they are all subclasses of Object. This means that all classes in your Java programs can use the methods defined for the Object class. For example, one useful method is called equals. It returns boolean true if object1 and object2 refer to the same object. A class that inherits from another class is called a subclass of that superclass. Subclasses inherit, or "acquire", the data and methods of their superclasses. But they can also override those inherited methods. Overriding a class means redefining its functionality. Classes are split into three basic components: Class declaration Data Methods A class declaration is composed of Optional class modifiers - public, final, abstract The keyword "class" The class name - any valid identifier Optional clauses - extends, implements

Let's look at a simple example of a class declaration. Here is the keyword, class. The class name is Fractions. The Java convention is for classes to begin with an uppercase letter. We have specified an access modifier of "public", meaning that Fractions is visible everywhere. And we explicitly state that Fractions is an extension, or subclass, of the class Object. It is not strictly necessary to specify that we are subclassing, or extending, Object. If you do not explicitly specify which class you are extending, the default is "extends Object". Here, the class name is "Employee".

Page 50

Go To INDEX

The class access modifier used in this example is "public". Then the data for the class is defined. The Employee class has two Strings for names, an employee number, and a salary amount. Finally, the methods that operate on the data in the class are defined. Objects need to be declared and then instantiated. Let's define a new Employee object and call that Employee "johnny".

Strictly speaking, "johnny" is a variable that references an object of type Employee. Precede the object identifier, johnny, with the class name, Employee. We have only declared that johnny refers to an Employee object, and have not instantiated that object yet. Objects are instantiated using the new operator. Notice how we initialized johnny's first name, surname, employee number, and salary. We are using a special method defined for this purpose in the class - a constructor method. Constructors are methods, which have the same identifier as the class name and are used to initialize new objects. Java allows us to declare and allocate objects in a single statement. You just combine the declaration and instantiation into a single line, as here. Object data is accessed using the dot operator, ".".

Page 51

Go To INDEX

So, to access the social security number of an Employee in our example, we would use this syntax. The variable currentEmployee is an int (an integer type) variable that takes on the value of johnny's employeeNumber. To invoke a method on the Employee class is also quite simple. Just use the object name, a dot, and the method name with any relevant arguments. This line increases johnny's salary by 7%, for example.

Or we can access his salary by calling the access method getSalary.

Inheritance in Java:
You can extend a class in Java to create subclasses.

In this example, we have declared a new class called PartTimeEmployee to be an extension of Employee. The class you extend is called the superclass of the subclass. In this case, the superclass is Employee and the subclass is PartTimeEmployee. You may, in turn, extend those subclasses indefinitely. For example, we can extend PartTimeEmployee to create a new class StudentEmployee.

This means that one subclass may be the superclass of another class. As you can see, the terms subclass and superclass are relative. Class declarations are of this form.

The usual convention is to place the first letter of class names in uppercase. There are only three class modifiers: Public Final Abstract

Page 52

Go To INDEX

The public class modifier means that the class is visible everywhere. The class can be subclassed and objects can be instantiated from it anywhere in a program. The final modifier means that the class cannot be subclassed. In turn, all methods of a final class are final too. Clearly, final methods cannot be overridden, since the class cannot be subclassed. Final classes and methods allow the compiler to perform certain optimizations to the code. And the interpreter does not need to dynamically look up the class and its methods. The keyword abstract indicates classes that are not fully implemented, or which contain methods that are not fully implemented. The implementation must be provided by a subclass. They must be subclassed and their abstract methods implemented before they can be used. Abstract classes cannot be instantiated, since they are missing vital implementation details. And they cannot be declared final since they must be subclassed to be of any use. There are two optional clauses in the class declaration: Extends Implements The "extends" keyword, followed by an identifier, indicates the superclass. But remember, you cannot extend a final class. A class inherits the data and functionality of its superclass, and the superclasses of its superclass. If you do not explicitly specify a superclass, as in this example, then the default superclass is Object.

Even if you extend from a class other than Object, that class or one of its ancestors will be a direct extension of the Object class. So, all classes have Object as an ancestor. Object is the only Java class that does not have a superclass. All Java classes inherit the methods of Object. All Java classes belong to a single class hierarchy beginning at Object - the root class - and continuing to the lowest-level classes. There is no multiple inheritance in Java, although it can be simulated using interfaces. For more information on interfaces, see the CBT Systems course Anatomy of Java classes. Each subclass inherits the data and methods of all its ancestors. Inheritance is subject to the access modifiers used, and is discussed later. That's why all classes inherit the methods of the Object class. Object has no data to inherit, only methods.

Page 53

Go To INDEX

The diagram illustrates a sample class hierarchy for the Employee class and its subclasses. There are three subclasses of Employee - PartTimeEmployee, ContractEmployee and FullTimeEmployee. And there is a single subclass of PartTimeEmployee - StudentEmployee. Let's imagine a simple scenario. PartTimeEmployees do not work a full week. Their salary will be specified as an hourly rate. And their weekly salary will be the rate multiplied by a new variable - called hoursWorked.

Here we have defined a class, called PartTimeEmployee, and added an extra variable - hoursWorked. This is the body of the PartTimeEmployee constructor method. There are two important keywords to mention here - "super" and "this". The super keyword refers to the immediate superclass of this class. It is used here to invoke the constructor of the Employee class. If you decide to explicitly invoke a superclass constructor, it must be the first statement in a subclass constructor. Alternatively, it can be used to call overridden superclass methods. The variable "this" refers to the currently instantiated object. It is used here to differentiate the object's hoursWorked variable from the parameter hoursWorked. Although a subclass inherits all the data and methods of its superclass it can still override the methods of its superclass. This is the salaryIncrease method, as defined in the Employee class.

Let's see how to override this class in the ContractEmployee class. Imagine a situation where it was a company policy not to give contract workers salary increases of more than 5%. Thus, we must change the salaryIncrease method to reflect this fact. This method overrides the Employee salaryIncrease method.

Page 54

Go To INDEX

Notice that their names are identical. And the types and number of their arguments must be identical too, although the argument names can be different. And notice how you can still call an overridden method, using the super keyword. Here we see the data declared for the Employee class.

Notice how these variables are either defined as public or protected. Just like classes, data and methods within a class can have access levels, or visibility levels, set for them.

Class methods and class variables:

Let's examine the data declared for the class Employee. These are what are termed instance variables. There is a separate copy of these for each instantiation of the Employee class. But there may be cases where we want a single variable to be available to all instances of the class Employee. We call these variables class variables or static variables. They are declared using the "static" modifier. Let's define a new variable called latestEmployee under the statement: protected float salary; static int latestEmployee = 0; LatestEmployee contains the employee number of the most recently instantiated Employee. Class variables are initialized when the class is loaded. Instance variables, on the other hand, are initialized when an object is instantiated.
Page 55 Go To INDEX

So, latestEmployee will be set to 0 before any instances of Employee are created. We access class variables by preceding the variable name with the class name.

Within the class, of course, the class name is unnecessary. Here we have a method in a different class that accesses the latestEmployee class variable. Constants are special cases of class variables. They are defined as "static final". Being static, the same copy of the variable is accessible to all instances of the class. Being final means that it cannot be modified after it is defined. Let's look at an example of a constant in Java. Let's say it was company policy to limit pay increases for contract workers to a maximum of 5%.

This line of code declares an increaselimit constant of 5 (percent). The methods we have seen so far have been instance methods. They are invoked through calls on objects. Class methods, on the other hand, are methods, which are called through the class. You have seen this line of code before.

It is the default method invoked when you try to execute a Java class. The static modifier keyword declares the method, main, to be a class method. Class methods have some interesting properties. They are invoked using the class name, rather than an object name. They may not invoke instance methods or refer to any instance variables in the class. And they cannot use the "this" keyword, since they are not invoked on an instance of the class. Class methods are useful when you want to define a method that will be used independently of any object in that class. If you do not refer to instance variables or instance methods in your method, then consider using a class method. Static initializers are used to initialize class variables, and are executed only once, when the class is loaded. This allows more complex initializing than allowed when declaring them. But these static initializers have some peculiar features. First, they do not need a name - since it is obvious that a class can only have a single static initializer. And they have no arguments, since the system calls them automatically. And for the same reason, there are no return values. A static initializer is declared with just the static keyword and curly braces. The code operates under the same rules as class methods: No access to "this" variable
Page 56 Go To INDEX

No access to instance variables No calls can be made to any instance methods

Packages and the import statement:

Java classes are organized into packages. Packages usually contain groups of related classes. Every class is a member of a package. The package statement appears as the first statement in a Java file. The package's fully qualified name follows the package keyword. In this case, the package is called company.people. Package names mirror the directory structure of the class files. Thus, all classes in the company.people package must appear in the path company\people (or company/people). Access to classes from within a package is different from access from external packages. For example, a protected variable is accessible to another class within a package, but inaccessible to packages from outside. Protected variables can be inherited by subclasses in other packages, but cannot be accessed by them. In other words, how you structure your packages can determine class, variable, and method access. If you do not specify a package name in your files, then your classes become part of an unnamed default package. The import statement is not to be confused with the C language directive, #include. It does not read in, or load, the referred-to packages or classes. It simply allows the programmer to refer to classes directly, without having to precede them with the package name. The java.lang package is imported by default into every Java program. So when you refer to Object, which is in the java.lang package, you do not have to refer to it as java.lang.Object: just Object will do. The import statements appear after the package statement and before anything else. There can be an unlimited number of them. There are only two forms of the import statement. Here's the syntax for the first form of import statement. Import package_name.class_name; Here we can directly refer to the class name in the import statement, without preceding it with any package name. So: import java.awt.peer.ContainerPeer; Means we can refer to ContainerPeer throughout our code; Here's the syntax for the second form of the import statement: import package_name.*; It allows us to directly refer to all classes in the relevant package. Thus, import java.util.peer.*; Allows us to access such classes as ContainerPeer and ListPeer without preceding them with the package name.

Page 57

Go To INDEX

Regardless of the package that a class belongs to, it is stored in a separate file. The name of the file is the class name, followed by a ".class" extension. A single source file can have multiple class definitions but only one public class. The name of the source file must be the name of its only public class followed by the extension “.java”. We could, for example, store all the Employee-related classes in a common file, called Employee.java.

Advanced classes:

A key concept in object-oriented programming is that of data hiding, or encapsulation. Data inside a class should only be accessed by trusted methods. This prevents illegal, unwanted, or arbitrary access to data in a class. Similarly, variables within methods should be protected from access by other methods. Local variables exist in Java, just as in C and C++. Local variables can only be accessed within the relevant method (or statement block).

In the salaryIncrease method we have defined a local variable called increase. It is allocated upon every invocation of the salaryIncrease method and does not require any access modifiers. You use access modifiers to control the level of access to your classes, methods, and variables. Access modifiers are sometimes referred to as visibility modifiers. Data and methods within a class are sometimes referred to as class members. Let's consider visibility modifiers for class members. These access levels determine which classes or subclasses can access a variable or method, and also whether a subclass can inherit that member. There are, in fact, four levels of access, or visibility modifiers, for class members: Default Public Private Protected The default visibility, indicated by the absence of a keyword, allows for access and inheritance within a package only.

Page 58

Go To INDEX

Classes outside the package cannot access or inherit the class member. When you define your class members so as to have public access, you make them completely visible to all classes, both inside and outside the package that the class belongs to.

You use public access for methods or variables that need to be accessible from anywhere. Private variables or methods are not visible anywhere outside the class in which they are defined.

They cannot be inherited by subclasses either. You should only use private visibility for members that are only used within a class and nowhere else. Protected class members are visible only within the same package.

Page 59

Go To INDEX

They are not visible to subclasses from external packages. However, they can be inherited freely by subclasses in any package. The salary variable was declared as protected in our Employee class.

This means that classes in other packages can only access salary directly if they are a subclass of the Employee class. Alternatively, other classes could use the access method specified for salary getSalary. Early versions of Java included the "private protected" access modifier. The private protected access modifier is not supported in versions after 1.0 and you should not use it in your code. Good practice dictates that you should use the strictest data hiding possible. If classes or class members do not need to be visible outside of a class, you should make them private. Every Java class has at least one constructor method. Constructor methods have the same name as the class they are defined in. There can be several constructor methods for a class, and they are differentiated by their argument lists. All object creation, using the new keyword, involves invoking one of a class's constructors. Any initialization for a new object takes place in the constructor. If you do not define a constructor, then Java provides you with a default. This default constructor takes no arguments and performs no special initialization. It merely makes a call to the superclass constructor, which has no arguments. Constructors return "this" implicitly. Returning "this" implicitly means: No return type is specified in the method declaration The "void" keyword is not needed in the declaration No return statement is required It is possible to define multiple constructors for a single class, all with the same name. They are differentiated through their argument lists, which must be different. Constructors within the same class can invoke one another. This saves having to

Page 60

Go To INDEX

duplicate very similar code between two constructors. You use the "this" keyword to refer to the constructor that corresponds to the argument list you use.

This example shows how to call another constructor in the same class using "this". The constructor is identified through its argument list - a single integer in this case.

You call a superclass constructor by inserting the super keyword followed by an argument list between parentheses. This must appear as the first statement in the constructor body. Here, the PartTimeEmployee constructor calls an Employee constructor. If you do not explicitly call a superclass constructor, then Java inserts a call to super() anyway. That is, it enforces at least one call to the constructor of the immediate superclass. If the first line of a constructor block is a call to another constructor within the class then Java postpones a call to super(). Instead, it invokes the explicit constructor in the normal way. That constructor will either explicitly invoke a superclass constructor, or else Java will enforce an implicit call to super() from that constructor. A constructor for StudentEmployee invokes, either explicitly or implicitly, the PartTimeEmployee constructor. It, in turn, invokes the Employee constructor. And that constructor invokes the Object constructor. Thus, all constructors, in a chain from the bottom of the class hierarchy to the top, are called either explicitly or implicitly. This is termed constructor chaining. When Java garbage-collects an unreferenced object, it frees up any memory allocated to the object. However, there may be resources held by the object, which you need to explicitly free. You do this through finalizers. Finalizers are a bit like constructors, but are invoked at object destruction instead of object instantiation. However, they have no arguments and return no values. Finalizers are all called "finalize". A typical example of a finalize method declaration is shown here.

Page 61

Go To INDEX

Finalizers are only ever called once in the lifetime of an object, just before garbage collection. Some finalizers may store the "this" reference somewhere, thus keeping the object alive and preventing it's garbage collection. In this case the garbage collection is cancelled. But the finalizer is not re-called when it is garbage-collected again. The compiler makes no guarantees about when, or even if, a finalizer is called. If the program finishes before all objects are dereferenced then they are not garbage collected at all. In this case, the operating system is usually responsible for freeing held resources. Garbage collection takes place transparently to the user, and cannot be controlled by the programmer. Be careful never to assume that finalizers will always be called, or called in any particular order. Unlike constructors, finalizers are not implicitly chained. You may make an explicit call to a superclass finalizer with super.finalize() instead. super.finalize() method always works because finalizers are all called finalize, have no arguments, and return nothing.

2.3. Simplifying C++: • Pointers and casting:

Several important features of C++ are not present in Java. Some of these features are omitted with the intention of making Java a safer and more robust language than C++. The most important example of this type of feature is pointer arithmetic. In C++, a pointer is a special type of variable that contains a memory address. It can be used to select and manipulate the addresses of functions and data. Pointers can be used in C++ for several purposes. For example, you can access an array element with a pointer instead of using the bracket operator []. You can also pass the address in memory of an argument to a function, rather than its value. Using pointers enables a programmer to directly access memory. It also enables dynamic memory allocation, which is useful when the size of objects is not known until run time. Despite the power of pointers, it is undeniable that they are the cause of many bugs in C++ programs. A small error in pointer arithmetic can cause a memory location to be inadvertently overwritten. This type of bug often takes a long time to find and correct. Java does not have pointer arithmetic like C++ has, and it doesn't allow data to be converted to a pointer type. However it achieves much of the functionality of pointers by making extensive use of references. A reference acts like a pointer, although it is actually a handle. A handle is a symbolic reference to an object in memory. In Java you use the object itself, rather than a pointer to it, as you would in C++. This is not a major restriction, as nearly everything in Java is an object. For example, strings and arrays are Java objects. Simple data types such as int and float are not objects. But you can treat them as objects by using the appropriate data type wrapper class. These wrapper classes can be found in the java.lang package.

Page 62

Go To INDEX

Generally speaking, only experienced programmers are able to make effective use of pointers. It is much easier to write error-free code by using references to objects than by accessing them with pointers. And extra security is provided by the Java runtime system, which performs boundary checking on array operations. Both C and C++ have a feature called automatic coercion.

This happens when one data type is implicitly converted to another type. This process can lead to a loss of precision. In this example, a floating-point value is assigned to an integer variable, thus losing the fractional part. Another case in which precision can be lost is when a data type of a certain length is converted to a type of shorter length. This happens, for example, when a 64-bit long is cast to a 32-bit int. In this case the compiler will remove the upper 32 bits of the long value, possibly causing a loss of accuracy. Unlike C and C++, Java does not permit automatic coercion of data types. If you make an assignment that could possibly cause a loss of precision, the compiler will generate an error. You must explicitly cast the variable to the required type. This is done by putting the desired type in parentheses to the left of the variable to be cast.

Certain rules should be followed when casting to avoid losing precision.

An integer can be cast to another integer type or to a floating-point type. However, a floating-point type cannot be cast to an integer type. The char type, which is a 16-bit Unicode value, can be safely cast to int, long, float, or double. Finally, it is important that the size of the destination type is never smaller than the source type.

Defining types in Java:
Two of the design aims of Java's developers were to make that language: Simpler than C++

Page 63

Go To INDEX

More object-oriented than C++ It is evident that these objectives have been achieved when you examine how the languages allow you to define data types. Java's built-in data types are very similar to those of C++. The few small differences between the languages were included to ensure that Java's data types are platform-independent. But Java's approach to user-defined types is quite different from, and much simpler than, that of C++. In Java everything apart from simple data types is an object. Therefore if you want to define your own data type, you have to write a class to define it. A Java class is very similar to a C++ class. Both Java and C++ classes consist of Data members Methods Access specifiers In both Java and C++, access specifiers can be public, private, or protected. There is a slight difference between the two languages in what protected means. In Java, all classes within the same package have access to protected variables. In C++, only subclasses have access to such variables. In a Java class you must define the access specifier individually for each variable.

Another difference is that Java does not have the C++ scope resolution operator (::).

Page 64

Go To INDEX

C++ has several constructs that enable a programmer to create user-defined data types. One of these constructs is the "struct". The "struct" was originally developed for C, where it allows you to group together several pieces of data and treat them as a unit. C++ expanded the idea of a "struct" to include member functions. For backward-compatibility a "struct" that is valid in C is also a valid C++ "struct". Java does not have any "struct"s - you simply use a class instead. You can make the instance variables and methods in your class private or public, as required. The absence of "struct"s makes the language simpler, without losing any functionality. Another feature in C++ for creating user-defined data types is the union. A union acts like a single variable, but it can store different data types at different times. The purpose of the union is to conserve memory. This is because it only occupies the space needed by the largest data type defined, not the total space needed to store all the data types it can represent. Java again simplifies matters by dispensing with unions - you can use a class instead. Java's memory management model makes the use of unions redundant. C and C++ programs use enums to associate numbers with a list of words. An enum variable is actually an int. Java does not use enums - you can achieve the same effect by defining constants.

The C++ preprocessor:

One of the most important aims of Java's designers was to make the language simple to read and understand. They based many of Java's features on C++. However they omitted features, which, they believed, made C++ difficult to read and understand. One of these features was the C++ preprocessor. The preprocessor is also used in C programs. Before C++ source code is compiled, it first runs through the preprocessor. This is a program that searches for preprocessor directives, which are commands preceded with the number sign (#). It modifies parts of the source code as directed by the preprocessor directives. Finally it writes the output to an intermediate file, prior to compilation. Three of the most widely used preprocessor commands are as follows: #include - include a library header file
Page 65 Go To INDEX

#define - define a macro #ifdef - this is for conditional compilation The preprocessor is a feature that C++ inherited from C. Java's designers dispensed with the preprocessor because it makes programs harder to understand and maintain. However Java does not forfeit any functionality by not having a preprocessor. In C++ the header files contain the external declarations for the libraries being used by the program. They are distributed along with the compiled binary code - not included inside it. This arrangement can cause many problems in practice. With large class libraries, it can be very difficult to ensure that the correct versions of the header files are being distributed with the compiled code. In addition to this compatibility problem, using header files makes the code difficult to maintain. To fully understand C++ source code, it is necessary to read all the associated header files. This can make it very difficult for one programmer to maintain another programmer's code. Java avoids these problems by dispensing with header files completely. It uses the import statement to include classes in other packages.

In Java, the import statement is similar to the C++ #include statement. However, the Java import statement simply allows a program to more easily reference other precompiled classes whereas the C++ #include statement includes the external definition of the referenced classes. The imported classes can then be referenced in the source code of the Java class file without having to specify the full package name. In C and C++, #define is often used to define a constant value with an associated name. In Java, you use the final keyword to specify a variable whose value remains constant.

The value of myHeight is now fixed at 71, and it will remain at 71 even in a subclass. The #ifdef is used in C++ programs to implement conditional compilation.

If the #ifdef yields a true result, the code between #ifdef and #endif is included in the compilation. This type of compilation is often used to enable programs to run on multiple platforms. Java doesn't have a #ifdef directive because it doesn't need it. Since Java is platform-independent you don't need to compile separate versions of your program for different platforms.
Page 66 Go To INDEX

Flow control extensions

Java's flow-control syntax is very similar to that of C and C++. This means that programmers familiar with those languages find it easy to start writing programs in Java. However there are some important areas in which Java's flow-control model extends those of C and C++. To write effective Java programs, it is necessary to understand how to use threads and exceptions. Java's use of threads enables a program to run multiple parts of itself concurrently. The ability to run threads is extremely useful in practice. It means, for example, that a program can have a process running in the background while simultaneously handling user input. A thread is similar to a process but it does not have the overhead that a process has. A thread is sometimes known as a lightweight process. C++ does not inherently support multithreading, but you can, for example, make a UNIX fork system call from a C++ program. This creates a process that is an exact copy of another process - code and variables. Java's threads, in contrast, duplicate only the code needed to run in parallel. This makes threads more efficient than running multiple processes. There are two ways in which you can make your application run in separate threads. You can extend the class Thread, which is part of the java.lang package.

You now have a new class - MyFirstThread - that has all of a thread's methods and properties. To make MyFirstThread do something useful, you implement the logic of the thread in its run() method.

A thread's run() method is the equivalent of an application's main() method. You may want to implement a thread in a class that already exists. In this case the class will not have been derived from the Thread class. To support threading in such cases Java provides the Runnable interface. Runnable is a public interface that has only one method.

To use the Runnable interface with an existing class you use the following syntax.

However you still need to use a Thread object, because that is the only class that can launch a concurrent thread. You first create an instance of the ExistingClassRunnable class.

Page 67

Go To INDEX

Then you create a new thread, passing it the Runnable object as a parameter.

This can be done in the constructor of ExistingClassRunnable. Exceptions are a means of handling potentially recoverable runtime errors. Both Java and C++ have exception-handling mechanisms. In fact, the syntax for exception handling is similar in both languages. However Java's exception-handling is easier to implement and more robust than that of C++. In Java, an exception is actually an object. By treating exceptions as standard objects, Java can handle different types of errors using a standard interface. Code for handling errors is included in the java.lang package. This package is automatically imported into all Java programs. Exceptions can be handled in several different ways in Java. If an exception is not explicitly handled in the code, it is passed on to the Java runtime system. The runtime system may be able to deal with the exception without causing the application or applet to crash. However it is better to write code that can handle foreseeable exceptions. Java has several different types of exceptions: Runtime exceptions Checked exceptions Custom exceptions Runtime exceptions occur within the Java runtime system, for example: Arithmetic exceptions - the classic example here is divide-by-zero Indexing exceptions - accessing an array element with an incorrect index These are also known as unchecked exceptions, because the compiler does not check to see if your program handles them. Checked exceptions are exceptions that you must explicitly handle in your code. These exceptions are associated with particular methods. The compiler checks that these exceptions are being handled - if they are not, a compilation error is flagged. Java's online documentation describes the exceptions associated with each package. All exceptions are members of the java.lang.Exception class. You can customize an existing exception by overriding methods and changing variables. Or you can create new exceptions by extending the java.lang.Exception class. A simple example of Java's exception handling can be created by using the keywords try, catch, and finally. The try keyword indicates that the Java program is going to execute a block of code that might generate an exception. The catch statement identifies the exception that has occurred and specifies the code to execute to handle that exception. The try statement can be followed by a number of catch statements, each specifying what action to take for a different exception. The finally statement is always executed, whether or not an exception was generated within the try statement.

Page 68

Go To INDEX

The throw keyword generates an exception. It takes as a parameter an object of the desired exception type. This can be used to generate customized error messages, while still throwing the error on to the default exception handler.

C++ Libraries:

C++ is an object-oriented derivative of the C programming language. When C++ was first developed, it included the C standard library for backward-compatibility. In procedural languages a library is a collection of related functions. It also introduced a new Stream I/O library. Since it was introduced, C++ has evolved and increased in size. In particular, its standard library has grown hugely. The C++ draft standard specification now lists over 800 items such as functions, types, and structures. These items are grouped in header files, of which there are 32 C++ headers and 18 C headers. The functionality provided by the large C++ standard library enables programmers to more easily develop complex applications. Java also has a standard library, composed of packages, which are collections of classes. In many cases the functionality provided by Java's standard library matches that of the C++ library. There are still some areas where support for particular features is stronger in C++. However Java is a young language and it is certain that these areas will be improved very soon. The C++ standard library can be divided into 10 separate sublibraries. Each of these sublibraries contains both C++ headers and C headers. C++ has a general utilities sublibrary that contains three C++ headers. <utility.h> and <functional.h> define many templates for various operations. Java does not use templates so it does not have an equivalent. <memory.h> provides templates to support memory management. Java uses a garbage collector to free up memory, so again it does not need an equivalent to this C++ feature. In the strings sublibrary C++ has a <string.h> header to help manipulate strings. The C headers <cstring.h> and <cwchar.h> also contain functions useful for text manipulation. Java provides similar capabilities in the String and StringBuffer classes. For
Page 69 Go To INDEX

diagnostic purposes C++ programmers use the <stdexcept.h> header file, which declares the standard exceptions such as length_error and runtime_error. They can also use the C headers <cerrno.h> and <cassert.h>. Java has many standard exceptions in each of its packages, which offer similar functionality. The most important set of Java exception classes is found in the java.lang package. This package is imported into every Java program by default. This makes it easier to incorporate robust error-handling into Java programs. C++ has a well-developed sublibrary for handling mathematical operations. The three C++ header files in this sublibrary are <numeric.h>,<complex.h>, and <valarray.h>. In addition the C header file <cmath.h> defines many standard mathematical functions. Java's mathematical methods are defined in the Math class, which is part of the java.lang package. This class handles most standard mathematical tasks such as exponents, trigonometric functions, and absolute values. However it does not yet offer some of the more advanced functionality provided by the C++ classes. For example it does not match the C++ <complex.h> header, which provides functions for dealing with complex numbers. C++ has several header files, which help to implement its streams approach to input/output. These include <iostream.h>, <streambuf.h>, <sstream.h>, <fstream.h>, <iomanip.h>, and several others. Java uses a similar stream approach to input/output. Much of its I/O functionality is incorporated in the InputStream and OutputStream classes of the java.io package. The containers sublibrary contains an important selection of C++ header files. These headers can be divided into the following three categories: Sequences - <vector.h>,<stack.h>, <queue.h>,<dequeue.h>, <list.h> Associative containers -<map.h>, <set.h> Bitwise operations - <bitset.h> Containers are objects that contain other objects - they play a vital role in objectoriented programming. So it's not surprising that Java matches the features offered by the C++ containers sublibrary very closely. The relevant classes are part of the java.util package. Sequence-type containers are provided by Java's Vector and Stack classes. Associative functionality is provided by the Dictionary and Hashtable classes. And bitwise operations are supported by Java's BitSet class. C++ has an important sublibrary devoted to algorithms. It consists of a C++ header <algorithms.h> and the C header <cstdlib.h>. The algorithms defined by these headers are of many types - sorting, comparing, merging, and so on. Java's algorithms can be found in its standard library. At present, they don't offer the functionality provided by the algorithms in the C++ sublibrary. C++ has a localization sublibrary that consists of a C++ header <locale.h> and a C header <clocale.h>. These headers define functions that support international formatting for dates, text, numbers, and monetary units. Java 1.1 supports different date and time formats based on locale. The java.text package provides many useful methods and constants for localizing programs. And Java's use of the Unicode character set provides another important advantage in this area.

Page 70

Go To INDEX

2.4. Javadoc • The Javadoc utilities and its parameters:

Ideally an application should be thoroughly documented as it is being developed. Many developers, however, document their code after the application is completed. This leads to documentation that is sometimes incomplete, or even misleading. Java provides a tool called javadoc that helps developers to document their code as it is written. Javadoc is sometimes referred to as the Java API Documentation Generator. The javadoc utility, which comes with the JDK, creates documentation straight from the source code. It generates information directly from the class, method, and variable declarations. But it also includes comments written using the doc comment syntax. /** This is a javadoc comment */ The javadoc utility produces documentation in the form of HTML files, which can be viewed by a Web browser. These files are hyperlinked to each other, and also to the Java API documentation. This type of linked output makes it easier to understand how the code works. You run javadoc from the command line and pass it the full name of the Java source file. If you are documenting a class, you must include the ".java" extension. javadoc classname.java You can pass multiple class names to javadoc if required. javadoc classname1.java classname2.java… You can also use javadoc to document an entire package by passing the package name as an argument. javadoc packagename When javadoc executes it generates a series of HTML files: classname.html AllNames.html Tree.html Packages.html Package-packagename.html Classname stands for the actual name of the class; packagename stands for the name of the package. The classname.html file contains detailed information on the relevant class. AllNames.html contains an alphabetical list of the attributes and methods of the class. The tree.html file shows the class hierarchy as an inheritance tree. The packages.html file gives a list of all packages in the application. It is usually empty when you are producing documentation for a class. The Package-packagename.html file lists all the classes in the relevant package. It is only produced when you are documenting a package. A number of options are available for customizing the output of javadoc. The syntax is now displayed. javadoc [-options] classname.java

Page 71

Go To INDEX

Two of the options limit the number of default files produced. The -notree option omits the class hierarchy file tree.html. And the -noindex option omits the AllNames.html index file. The syntax to direct the output to a destination directory is now displayed. javadoc –d directory classname.java You can tell javadoc where to look for the ".java" source files with the -classpath option. A path containing more than one directory can be specified by using semi-colons. javadoc –classpath c:\javasrc1;c:\javasrc2 Some of javadoc's options tell it to include certain tagged information that is omitted by default. The -author option tells javadoc to include any @author tag information. And the -version option will include the @version tag information. The -verbose option causes additional information to be printed describing javadoc's progress.

The doc comment syntax

The javadoc utility automatically documents all class, interface, method, and variable declarations. However to fully document an application you need to include your own comments. The javadoc utility lets you include your comments with the doc comment syntax. The text in a Java doc comment can span several lines. /** This is a javadoc comment That spans more than one line */ Java doc comments are only recognized in specific places in the source code. They can only be placed immediately before the declarations of Classes Constructors Methods Data member variables You can use special tags in doc comments to improve the formatting of the output. These tags are preceded by the "at" sign (@), and consist of the following: @see @version @author @param @return @exception The @see tag inserts "See Also:" before the succeeding reference. @see java.lang.Object

Page 72

Go To INDEX

More importantly, it turns the reference into a hyperlink, which can be clicked for more information. The @version tag identifies the version number of the class. You may have only one @version tag within a doc comment. @version 2.5 06/02/97 The @author tag creates an author entry - you may have multiple @author tags in a single doc comment. @author Soledad Mendez @author Johnny Leary The @version and @author tags are only used for class and interface documentation sections. Three tags are reserved for use with method and constructor documentation. These are @param, @return, and @exception. The @param tag has the following syntax: @param parameterName description This tag adds a parameter name and description to the "Parameters:" section. The @return tag adds a description of the return value to the "Returns:" section. @returns description The syntax of the @exception tag is displayed here. @exception fully-qualified-className description This adds the exception name and description to a section called "Throws:". The exception name is hyperlinked to its entry in the API documentation. You can also use the @see tag when documenting methods and constructors. The @see tag is the only tag that can be included in a comment for a variable. To enhance the documentation you can embed most standard HTML tags in your doc comments. For example, you can include hypertext links and graphic images. You should not use the following HTML tags in a doc comment: The horizontal rule tag <HR> Heading tags <H1> through <H6> This restriction applies because javadoc itself uses these tags in a certain way to format the output.

Generating class documentation with Javadoc
To see javadoc in action, let's use it to document a class called Employee.

Page 73

Go To INDEX

This class contains basic employee data, and methods needed to operate on them. The source file contains several doc comments. The first of these precedes the class declaration, and briefly describes the Employee class. The doc comment is defined between /** and */. The extra asterisks shown are included merely to make the source code look neater. The doc comment for the class spans several lines. It includes two tags that can only be included in a class doc comment: @version @author The next doc comment is for the salary variable. Then there is a comment for the constructor of the Employee class. The doc comment for the salaryIncrease method contains two tags that only appear in method and constructor comments: @param, describing the parameter passed to the method @return, describing the method's return value

Page 74

Go To INDEX

You run javadoc from the command line and pass it the full name of the source file. C:\javafile\>javadoc employee.java You must use the .java extension, or javadoc will treat the file as a package. If you want to document a number of classes simultaneously, you simply pass javadoc all the file names. Javadoc omits the @version and @author tags by default. If you want to include this information in the output, you must use the following options: C:\javafile\>javadoc –version –author employee.java The javadoc utility now creates an HTML file named after the source file. If you are documenting several classes simultaneously, javadoc produces one HTML file for each class, and names them accordingly. In this example, javadoc produces a file called Employee.html, and three additional files: Packages.html - this will be empty as no packages were used AllNames.html - alphabetical index of methods and fields Tree.html - class hierarchy Let's look at the Employee.html file in a browser:

Page 75

Go To INDEX

Under the class name you can see a diagram showing where the Employee class fits in the class hierarchy. Because Employee was created without extending any other class, it is implicitly descended from java.lang.Object. Then you can see the access modifier for the class. The next section shows the information included in the doc comment for the Employee class. You can see that the @version and @author tags neatly format the output. The variable index section shows a list of the declared variables.

Each entry is hyperlinked to another part of the HTML document that gives more detail on that variable.
Page 76 Go To INDEX

In this example, only the salary variable has been commented. The constructor index section provides a link to the constructor's declaration, along with the relevant comment.

The method index section provides similar links to the class methods' details.

The next section is called fields, and it contains details of the class's variables.

Page 77

Go To INDEX

These variables are color coded as follows: Instance variables are denoted by a purple ball Static variables are denoted by a blue ball There are no static variables in this example. The constructor section gives details of the constructor of the Employee class.

As this example does not use method overloading, there is only a single entry. Constructors are denoted by a yellow ball. The final section of the documentation generated by javadoc gives details of the class methods.

Page 78

Go To INDEX

Instance methods are denoted by a red ball, and static methods are denoted by a green ball. You can see that the salaryIncrease method includes the doc comment and the @param and @return tag entries. The AllNames.html file is an alphabetically ordered index of all the fields and methods in the class.

You can use the underlined letters to jump to entries beginning with that letter. For example, clicking on S brings you to the section with the salary variable and the salaryIncrease method. These entries are, in turn, linked to their declarations in the Employee.html file.

Page 79

Go To INDEX

At the top of the AllNames.html file there are two hypertext links. All Packages is linked to the packages.html file, which in this case, is an empty file. Class Hierarchy is linked to tree.html, which shows that Employee is a subclass of java.lang.Object.

The Index link at the top of tree.html is linked back to AllNames.html. Employee is linked to Employee.html, while Object is linked to Java's API documentation. Employee.java is a simple class that does not generate a lot of documentation. The real benefits of using javadoc become apparent when you are documenting: A large class

Page 80

Go To INDEX

A number of classes simultaneously A package When you are documenting a large class it will generally have many calls to the Java API. Javadoc automatically creates links to the existing API documentation. This makes it easier to read and understand the code. When documenting a number of classes simultaneously, javadoc creates a class file for each, named after the respective classes. However it only generates a single AllNames.html file and a single tree.html file, each containing details for all the classes. So these files will be larger and more informative than those in the Employee.java example.

Javadoc in practice

For javadoc files to work correctly, certain conditions have to be met. The Java API documentation must be present - in HTML format - in the same directory as the javadocgenerated files. This directory must also contain the images subdirectory, which contains graphic files that enhance javadoc's output. There is, however, a problem with having javadoc-generated files in the same directory as the API documentation files. When javadoc executes, among the files it creates are an AllNames.html file, a tree.html file, and a packages.html file. The problem is that the standard API documentation already contains an AllNames.html file, a tree.html file, and a packages.html file. If you move the javadocgenerated files into the API documentation directory, you will overwrite the existing files. One way to overcome this problem is to rename the javadoc-generated files. Suppose you want to document a Java class called Customer. First you create a new temporary directory - let's call it javatemp. You then copy the Java source file Customer.java to this directory. Now run javadoc on the source file with the following command: javadoc Customer.java The javadoc executable file normally resides in the \java\bin directory. Your PATH variable must point to this directory. After running javadoc you will have four new files in javatemp: Customer.html Allnames.html Tree.html Packages.html Because the Customer class does not use any packages, packages.html does not contain any information, so you can delete it. You can now rename the AllNames.html and tree.html files. A good convention to use is to prefix their names with the name of the relevant class: AllNames.html becomesCustomerAllNames.html Tree.html becomesCustomertree.html

Page 81

Go To INDEX

The Class Hierarchy link at the top of the CustomerAllNames.html document still points to a file called tree.html. Therefore you have to change it to refer to the renamed file. You open the file with a text editor and change the code to the following: <A HREF="Customertree.html"> The Index link at the top of the Customertree.html file points to AllNames.html. So again you must change the code to: <A HREF="CustomerAllNames.html">

The Index link at the top of the Customertree.html file points to AllNames.html. So, again you must change the code to: <A HREF="CustomerAllNames.html"> In this simple example, these are the only changes you have to make to maintain the integrity of the links. If other files are being used, such as packages.html, then the corresponding hyperlinks would have to be changed. Now you can create a directory to hold the javadoc documentation - let's call it javadocs. You then copy the existing API documentation into this directory. It must also contain the subdirectory "images", which holds the graphic files. You can now copy the Customer HTML files into the javadocs directory. You can create and add as many javadoc files to javadocs as long as you remember the following: Use a unique name for each new class Rename the AllNames.html file, the tree.html file and, if necessary, the packages.html File Change the links to reflect the filenames

Page 82

Go To INDEX

3 Object-Oriented Principles & Java
3.1. Procedural programming Vs OOP: • Limitations of procedural programming
Traditionally, the standard approach to programming was procedural or functional. Widely used procedural languages include: PASCAL COBOL Modula-2 FORTRAN C The procedural programming approach breaks a problem into a series of small subroutines or procedures. Procedural languages are also known as function-oriented or structured languages. Problems with the procedural method have led software developers to look for a more efficient approach to the development process. Object-oriented methods are becoming more popular at the expense of the procedural approach. There are several commercial reasons for the loss of popularity of procedural languages. First - the cost of software development in a procedural environment is relatively high. High expectations regarding quality have added to this expense. Increased demand for products requires greater development resources. Faster development methods are needed to deal with increased demand for products. Many of these commercial problems can be addressed by applying the principles of good programming. There are a number of principles of good programming that are supported in traditional environments. For instance: Modularity Reusability Extensibility Other principles include Ease of maintenance Preservation of data integrity Efficient debugging However, although procedural programming supports these principles, they have proved to be difficult to attain and maintain efficiently. Procedural programming techniques are not always successful because they do not enforce the principles of good programming and make it easy for you to disobey these principles. For instance, a

Page 83

Go To INDEX

difficulty arises from the way that modules interact with each other in a procedural program. Another problem is that procedural languages are not as effective at modeling realworld things as their object-oriented counterparts. Object-oriented programming can implement such practices with greater ease than traditional languages. The principles of good programming are generally considered to produce highquality, low-cost software in an efficient manner. Of these principles, modularity has had the most important influence on procedural programming. Dividing a program into separately named and addressable components called modules makes a large program structure easier to understand. However, the way in which procedural programming allows modules to interact with each other gives rise to problems. Perhaps the most serious difficulty a programmer encounters is the inherent complexity of modeling a real-world problem. Traditionally the focus has been heavily oriented toward the procedural side of the activities being modeled. The problem with a procedural approach is that it does not always translate well to software that is compact, easy to maintain, and reusable. In order for modularity to be effective the modules must display "module independence". Module independence is achieved by developing modules that: Perform a single task Do not interact excessively with other modules Often, software developed using procedural methods does not achieve effective module independence. A failure to achieve effective module independence means that modules are more difficult to maintain and test because: Secondary effects caused by design and code modification are more likely Errors are spread to other parts of the program Reusable modules are more difficult to develop and implement Software complexity can be measured using two criteria - coupling and cohesion. These criteria present problems for programs developed using the procedural approach to programming. Coupling refers to the complexity of interfaces between modules. Cohesion is a measure of the strength of functional relatedness of elements within a module. Coupling is a measure of the relative interdependence of modules. In software design, programmers strive for the lowest possible level of coupling. This is because simple connectivity between modules results in programs that are easier to understand. Low levels of coupling make programs less prone to errors that occur at one location and then recur through a program. The degree of coupling depends on the interface complexity between modules. It also depends on what data passes across the interface. The highest level of coupling is known as content coupling. It occurs when one module makes direct use of data that is within the boundary of another module.

Page 84

Go To INDEX

In function-oriented systems, a function in one module can access and modify data structures in another module. The use of pointers, in C for example, increases the possibilities of this happening inadvertently. Programmers using procedural languages have been much constrained by having to write functions so that they do not modify data outside their boundary or scope. If functions change data outside of their scope, testing becomes very difficult. One of the most difficult problems with using the procedural approach is to write all functions in such a way that they do not modify data outside their boundary. It is almost impossible to completely eliminate coupling. It is an inherent problem with the procedural approach, and a certain amount of coupling is inevitable. However, a programmer can achieve low coupling by: Minimizing the number of interfaces between modules Minimizing the amount of information that moves across an interface Cohesion is a measure of the relative functional strength of a function. It is also a measure of the modular strength of a module. A simple function is one, which performs a single task and is therefore highly cohesive. Cohesion is a natural extension of a concept called information hiding. Information hiding allows modules to be designed so that each module hides from other modules the way that it performs its task. The advantages of information hiding depend on there being high cohesion between modules. High cohesion is seen in a module that performs a single distinct procedural task. In practice, high levels of cohesion are not always easy to achieve under the procedural paradigm. Let's see how information hiding affects the level of cohesion. For each task in a program there is a corresponding module or function. Each function is supposed to perform a task that uses inputs to generate output results. Tasks are connected by function calls that pass data through a list of attached parameters. These functions are meant to be like a "black box" in the sense that you should not be able to see what is going on inside them. To use an existing function from a library a programmer should only need to know: What task the function performs What parameters need to be passed to it to make it perform its task What values are returned by the function With some procedural languages this information is stored in a definition module. All implementation detail - that is, the code that performs the function - is contained in an implementation module.

Page 85

Go To INDEX

The implementation detail does not need to be visible to the programmer using the function. Information hiding ensures effective modularity is achieved. The independent modules communicate to each other only that information which is necessary for the modules to perform their function. If you were writing a program and you needed a function to perform a particular task, you might simply use an existing function written by another programmer. As more functions are written they can be added to a library of functions and made available for other programmers to use. The advantage of information hiding and functional strength is that code is more reusable. Ideally, a new application should reuse a number of components (routines), which have already been tested and used in previous applications. So, while the first software applications may be expensive to develop subsequent ones will gradually become cheaper. This re-use makes sense. Otherwise it would be like a car manufacturer having to reinvent the wheel every time a new car was to be built. A cohesive module should stand-alone and require little interaction with procedures being performed in other parts of a program. Combining unrelated functions into a single module increases the likelihood of error propagation. A module, which exhibits high cohesion is said to be "functionally bound". In practice such modules are not always easy to develop using procedural techniques. The problem of low cohesion in the procedural paradigm affects the testing and maintenance of software. This is because a bug in a program cannot be totally isolated. It can spread to other parts of a program and "infect" them too. Another idea behind modularity and information hiding is that extending a module should not cause other modules that call it to be rewritten. It should be easy to add to code that is already in use, without extensive rewriting. The use of preprocessor directives in procedural languages like C causes further problems. If preprocessor directives are used excessively in a program the code, will not be easily understood by other programmers. If you misuse preprocessor directives other programmers will need to understand a large amount of context before they can understand your code. This means that each time a programmer writes code he effectively creates his own language first. A programmer who wants to modify what another programmer has written is then faced with a more complex task. It is less likely that reliable, efficient programs can be produced quickly and maintained without great cost.

Aims of object-oriented programming

The term "Software Crisis" has been coined to describe problems that are faced by the software industry. For instance, traditional software development has been characterized by high-cost, low quality, and ever-increasing demand. These problems have been both caused and exacerbated by increasing software complexity. A new approach to software development is needed to increase the productivity of the software industry. Productivity can be greatly enhanced by reducing software complexity. Objectoriented programming (OOP) is being heralded as the methodology with which programmers can reduce complexity and overcome these problems. As the 1980s came
Page 86 Go To INDEX

to a close, OOP began to mature into a practical and powerful approach to software development. It was originally used in the field of engineering and in academic institutions. Although it had been around for a period of thirty years it was not until relatively recently that OOP found its way into the mainstream. The focus of OOP is not on the procedures of the modeled world, but on the objects that appear in it. This approach is a radical departure from the procedural model. At first glance the aims of object-oriented programming (OOP) are like those of procedural programming. Both methodologies aim to facilitate the production of high-quality, low-cost software in the most efficient manner possible. OOP aims to uphold the same principles of good programming as the procedural approach. However, OOP upholds these principles more easily than procedural programming. OOP observes the following principles of good programming: Modularity Reusability Extensibility OOP also observes the following principles of good programming: Ease of maintenance Preservation of data integrity Efficient debugging The unique methodology of object technology supports these qualities in a way that produces less complex software. OOP Emphasizes "black box" functions Embraces structured development Encourages better programming design Within a procedural environment the combination of software complexity together with tight quality controls can result in considerable cost. OOP breaks complex tasks into simple, manageable, independent modules. Simplifying complex problems in this way: Improves quality Reduces cost However, OOP is not merely a new way to organize your source code. You can achieve results using OOP that would be impossible with procedural techniques. To be truly "object-oriented", a programming language should support these characteristics: Inheritance Data abstraction (encapsulation and data hiding) Polymorphism and dynamic binding In theory you can develop object-oriented software using any conventional language - say C or PASCAL. However, in practice, support for object-oriented
Page 87 Go To INDEX

approaches should be built directly into the programming language that you are using to implement your design. Java and C++ are examples of languages that have direct support for object-oriented concepts. Software development using object-oriented principles aims to: Support the principles of good programming Provide software of high-quality Fulfill user expectations Make code more portable and more reliable Reduce development costs Make code better represent real-world objects One of the strengths of using OOP is that you can model the real-world better than you can with other methodologies. The concept of an object allows you to do this. Objects are a natural thing to model because the world can be viewed as being made up of them. Cars, dogs, and computers can all be thought of as objects. When you look around the real-world you can see a set of physical objects, which can be identified, classified, and defined. Although it is not as obvious, a similar way of approaching the development of a software solution will make its realization easier. Software requirements analysis focuses on what function new software is to provide. During this stage, as well as the design stage, it is useful for you to think in terms of what objects are involved in the software problem. Under the OOP paradigm, objects are represented in a system, not just their associated data structures. In object-oriented programming objects can represent: Entities Roles Data structures By identifying suitable real-world objects you can create an accurate representation of your problem domain. You can then map this representation into a solution domain that is the final program. The object-oriented approach results in a program design that is unlike that of any other approach. Data and processing operations are interconnected in a way that modularizes information as well as processing. It is the fundamental concept of an "object" that facilitates this approach. Let's look at how the concept of an object fits in to OOP. Each object in the real-world and in the problem space of new software has a set of attributes that describe it. For example, you can describe a desk in terms of its weight, color, dimensions, cost, and the material it is made of. "Desk" is an object, and these things that describe it are its attributes. Each object is also a member of a broader group of objects known as a class. A chair is another object, which you can describe in terms of its weight, color, dimensions, cost, and the material it is made of. This is because all furniture can be described using these characteristics. So furniture is a class, which has a set of, generic attributes associated with every object in the class.

Page 88

Go To INDEX

Each individual object in a class is called an instance of that class. A fundamental concept of OOP is inheritance. A new class can be defined which inherits all the attributes that have been defined for an existing class. This new class can change or extend the functionality of the original class. The object-oriented environment is better able to model real life than other approaches to programming. This helps reduce the complexity of your resulting program structure. Objects are not just composed of data. They also include the methods, which relate and manipulate the data. A method is an operation that can be used to manipulate an object. For example, each object of the furniture class can be manipulated in a number of ways. For instance they could be painted, moved to a different room, or even sold. In OOP, rather than processing data using a function, you pass a message to an object telling it what to do. A message will invoke operations or methods, which can modify the value of attributes associated with the object. An object's attribute values are its data. Using messages to communicate means that data is not passed around a system openly. In this way OOP achieves its aim of preserving data integrity. An object encapsulates both the data and the operations associated with it. Encapsulation implements information hiding and modularity. In OOP, classes are independent stand-alone modules. They define both the data and the methods that may operate on that data. Classes are "decoupled" from each other. In other words interface complexity between classes is less than that between modules in procedural programming. Another fundamental concept of OOP is polymorphism. The word polymorphism is derived from Greek words meaning "Multiple" and "Shape". Polymorphism enables you to perform the same operation on different types of classes as long as they share a common trait. This helps avoid complexity and redundancy in your code. Another important mechanism closely linked to polymorphism is dynamic binding. Dynamic binding means that a message can be sent to an object even though its specific type may not be known until run time. In other words, it adapts to particular circumstances rather than being predefined and rigid. This provides maximum flexibility when your program is executing. Polymorphism and dynamic binding are very beneficial to networkbased systems. Because objects might come from anywhere, possibly across a network, messages need to be sent to them even if their specific type is unknown. The needs of distributed, client/server based systems coincide with the encapsulated, message-passing paradigms of object-based software. Programming systems must adopt object-oriented concepts to function within increasingly complex, network-based environments. In particular, object-oriented principles promote the development of code that is highly portable. OOP aims to reduce code development time. Encapsulation and inheritance make this possible. The reduction in development time occurs because you can extend existing classes or derive new ones from existing ones. This saves you from having to start at the beginning each time you need a new class. Because of the importance that object-oriented environments attach to code reuse, libraries can be built which can then be extended to provide new behavior. Library objects can provide functionality ranging from basic data types through I/O and network interfaces to GUI toolkits.

Page 89

Go To INDEX

Another important benefit of OOP is the reduction in testing and debugging time. This occurs because of the inherent localization of bugs. Problems in one part of a program will not infect other parts, and as a result can be traced more easily. Object-oriented principles encourage the creation of objects and classes that you can easily extend and maintain. Both maintenance and extensibility are made easy in OOP because each object can be dealt with in isolation. As a result there are no consequential detrimental effects in other parts of your program. You also have more power to keep interfaces consistent. OOP aims to fulfill user expectations. Application programs should be written so that they achieve their purposes without exposing the user to the implementation details of the application. A program designed using object-oriented programming practices should be easier to use because it models the real-world.

3.2. Fundamentals of OOP • Data encapsulation

At their simplest level, programs consist of two things: data and code. In traditional programming models, data and code are treated as separate entities. Data is allocated in memory and manipulated by code contained in subroutines or functions. In an objectoriented environment, data is closely associated with the code that acts on the data. The code or procedures that act on data are also known as "methods". A method is invoked by a message requesting that some action be carried out on an object's data. The combined unit of data and code (or methods) is called an object. And the process of packaging an object's data together with its code is termed "data encapsulation". Data encapsulation forms the basis of object-oriented programming. It enables programmers to more accurately represent real-world objects in a software environment. Just like objects in the real world, encapsulated objects exhibit two common characteristics - state and behavior. The data and methods that make up an object express everything that the object represents (state) along with everything it can do (behavior). Let's examine a real-world object, such as a car, to get an idea of how this works. In programming terms, the state of an object is represented by its data. Like any software object, a car has data variables that indicate its current state. Data variables describing the state of a car might include the car's engine type, its current speed, its make, and its model. A car also has methods operating on its data. For example, one method might allow it to brake, a second might cause it to accelerate, and another might enable it to change gears. So the behavior of the car is defined by the methods acting on the car's data. Data encapsulation provides two significant benefits for programmers: The ability to hide data Increased modularity First, let's look at the concept of hiding data. Objects are composed of internal (private) sections and external (public) sections. The private section is typically a combination of internal data and methods. Any data variable or method in an object may
Page 90 Go To INDEX

be marked private or public. The external section of an object is often referred to as its "interface". It represents everything that the external users of the object need to know, or are allowed to know. And it acts as an object's point of communication with other objects. Within a given object, the object's methods have full access to its data. So pressing the accelerator of a car (its method) can act on the car's speed (its data). But by default, object data is invisible, or inaccessible, to other objects. Any interaction between objects must be handled through their interfaces. This is known as "data hiding". Limiting communication to an object's external interface protects the internal portion of the object from unwanted external access. The second significant feature provided by data encapsulation is modularity. Encapsulated objects are described as modular because the source code for their internal sections are maintained separately from their published interface. Modularity enables programmers to maintain an object independently of other objects. This makes it easier to distribute objects throughout a system. And modularity enables programmers to make modifications to an object's internal code without corrupting the communication interface.

Inheritance

As an object-oriented program grows, the amount of object variables and methods that must be managed simultaneously becomes complex. One way to simplify the programming structure is to group similar objects together within a "class". A class is a template that defines a set of objects. As you know, objects are made up of data and methods. In a similar way, classes are created by defining all the data and associated methods for a given set of objects. You can view classes as a kind of blueprint, or outline, for objects that share a similar structure or behavior. When we describe cars in terms of software objects, we say that they have attributes such as engines and transmissions. And we describe them as having certain behavioral characteristics, like the ability to accelerate and brake. This description of a car's structure and behavior represents a class definition of cars. As the number and diversity of objects in a system increases, classes are subdivided to form a hierarchical structure. For example, the car class might be logically split into two further classes based on an engine's power source. Using the principle of "inheritance" this becomes a relatively simple step. Programmers can create new gas and electric car classes based on the properties of the existing car class. In effect, they are using an existing class as a template for the new classes. The class that is "inheriting" properties is referred to as the subclass, or child. The class providing the inherited information is referred to as the superclass, or parent. Since gas and electric cars are simply more precisely specified cars, they inherit both data and methods from the car superclass. But each new class also has specific attributes associated with it. For example, the gas car class might specify a fuel tank and gas cap. And the electric car class's definition might specify a battery and a plug for recharging. These unique, additional features distinguish subclasses from one another and from their superclass. While encapsulation provides the benefits of modularity and data hiding, inheritance provides the benefit of code reusability. Programmers can create new subclasses that reuse most of the code in existing superclasses.
Page 91 Go To INDEX

Polymorphism

In most functional programming languages, programmers need to create two separate functions with different names to complete the same task on two different entities. This creates a great deal of code complexity and redundancy. Using polymorphism, programmers can avoid this problem. Polymorphism lets programmers define a generic command, which is implemented by a number of related classes. The methods which implement the command in each class are tailored to suit their specific requirements. Remember, using the process of inheritance programmers can define a class structure so that all the objects in the structure have the same fundamental properties. For example, all the objects in this animal hierarchy are able to move. But each animal moves in a different way. For example, the fish swims, the dog runs, and the bird flies. Regardless of the specific way in which the animal moves, all animals in this hierarchy will respond to the generic message "move". "Move" is described as a polymorphic command - it can be understood by different classes of object. But each class will respond to the same command in a different way. To understand how the process of polymorphism works, let's look at how software objects interact with one another. When one object wants another object to do something, it sends a message to that object. When an object sends a message to another object, it's requesting that a method carries out some action. In object-oriented programming, messages and methods are synonymous. But in many cases the receiving object needs specific information to carry out an action. For example, if a driver object sends a message telling the car object to accelerate, the car object needs to know by how much. The extra information included in a message is known as a "message parameter". In fact, a method can be viewed as a parameterized message. And objects anywhere in a system can communicate with one another through these messages. In Java, polymorphic behavior can take two forms: Method overloading Method overriding Method overloading enables programmers to specify different types of information (parameters) in the message being sent to an object. For example, suppose you send the message "move" to the bird subclass. If one of the parameters specified in the message is "cat", then the bird, sensing danger, is likely to fly away. Now suppose you send the same "move" message - but this time the associated parameter is "food". The bird is likely to hop towards the food source specified in the message. The same method gives rise to completely different behaviors, depending on the parameters included in the message. To overload a method, programmers declare another version with the same name but with different parameters. When a call to a method is encountered in a program, the compiler checks the name and the parameters to determine which overloaded method is being called. Method overloading is a way for a single class to deal with different parameters and objects in a uniform way. As the code is being compiled, the compiler determines which method to call. This is known as static binding. But since it is often desirable to extend a program after much of the source code is developed, it can be inflexible. Method overriding provides a more dynamic and flexible
Page 92 Go To INDEX

form of polymorphism. It can do this because it utilizes "dynamic binding". The term binding describes an association between two things. If binding occurs before run time, as with method overloading, it's called static binding. If it occurs during run time, as with method overriding, it's called dynamic binding. Dynamic binding ensures that a polymorphic message is bound to the method at run time. So the program uses the actual instance of an object to decide which method to call. If you define a subclass of bird - for example you could subdivide this class on the basis of species - you can override the "move" method so that it behaves differently for each species. A flightless species of bird will not be able to fly, even if the message includes a cat parameter. All subclasses have the ability to override inherited methods and substitute different ones for them. Dynamic run-time polymorphism is one of the most powerful mechanisms provided by object-oriented design. It facilitates code reuse and robustness because it enables programs to use the existing code to call methods on new classes and objects. So the source code doesn't have to be rewritten or recompiled.

3.3. Classes, methods, and messages: • Overview of classes:

The most basic unit in a Java program is a class. A class is a template that defines the properties of a set of objects. As you know, individual objects encapsulate data and a set of methods that manipulate that data. Each object is derived from a class. So each class can be viewed as a collection of objects, or more specifically, a collection of data and methods. Where encapsulated objects provide the benefits of modularity and data hiding, classes provide the benefit of reusability. Programmers can reuse the code of a given class many times to create many objects. Each new object gets its own data but shares a single set of methods with other objects in its class. Any concept you want to represent in your Java program is encapsulated in a class. Within a program, classes form a hierarchical structure of superclasses and subclasses. Every class you declare must be derived from a superclass. In Java, all classes are derived from a system superclass called Object. This built-in class is at the root of the Java class hierarchy. Unless you explicitly specify a different superclass, you automatically inherit from Object. Object is special because it is the only Java class that does not have a superclass. And because it is the root class, the methods defined within Object can be used by all Java objects. Declaring a class in Java is relatively simple. To declare a class, you need a source file with the class keyword in it. The class keyword is case-sensitive in Java. The class keyword is followed by a valid identifier, specifying the name of the new class. The body of the class is delineated with braces. Typically, the classBody is made up of data variables and methods associated with the class. The data variables of a class are called instance variables. Let's look at a class declaration for the class Dog.

Page 93

Go To INDEX

The state of the Dog object is defined by three instance variables, representing the color, age, and breed of the dog. At the moment the Dog class isn't all that useful. It needs some methods acting on its data. Methods are declared inside a class definition at the same level as instance variables. This is the basic format for declaring methods within a class.

In some cases, methods may return results. The returnValueType lets you declare the datatype for an object's response to a message. If no return value is required, then the returnValueType "void" appears at the beginning of the declaration. The methodName stated is a valid identifier specifying the name of the method. The parameterList statement specifies the input parameters associated with the method, if any exist. Parameters in this list are separated by commas. If the method does not receive any parameters, then an empty set of parentheses follow the method name. The declarations and statements enclosed within braces form the methodBody. The methodBody is sometimes referred to as a block. Using this format you can define methods in new classes. You can also change the behavior of inherited methods. Here's a method declaration for the bark method in the Dog class.

The returnValueType "void" indicates that no return value is desired. You can see that the bark method includes the parameter age, which takes the form of an integer. The age value is used to determine the amount of barking that the dog does. According to this declaration, the younger the dog, the more likely it is to bark. If you make the bark method a member of the Dog class, then the age parameter isn't necessary.

Page 94

Go To INDEX

This is because age is already a data variable of Dog, so all this class's methods have access to it. With the addition of the bark method, the Dog class declaration looks like this.

Messages

Much of the design work in object-oriented programming involves designing classes. But you don't really benefit from the class structure until you create instances, or objects, of those classes. For example, once you've defined the class Dog, you need to create a particular dog to work with. In other words, you need an instance of the class - a single dog object. An instance is an individual occurrence of a class, with its own set of data called instance variables. An instance of a class can be referred to as an object. In fact, the term instance and object are used interchangeably. When you declare a class, the class declaration states what type of object is being described. But the object isn't actually created until the "new" operator is used. Here's an example of a new instance of dog being created and stored in the variable Dog1. The new operator creates a single instance of a named class and returns a reference to that object. So Dog1 is a reference to an instance of Dog. The variable is a reference to the object. It doesn't actually contain it. Instances require some type of communication mechanism in order to interact. In an object-oriented environment, software objects interact with each other through messages. Passing messages between objects is known as "method calling". This is because when an object sends another object a message, it is actually calling a method of that object. So a method can be viewed as a message requesting that some action be carried out on an object's data. If a driver object wants a car object to accelerate, it sends a message to the car object. This message initiates the car's accelerate method. However, in order to carry out the request effectively, the car needs information about the degree of acceleration required. Sometimes the object receiving the message needs extra information so that it knows exactly what to do. This extra information is known as a message parameter. Message parameters are actually method parameters. To effectively define a message for an object, the message must consist of three components: The name of the receiving object

Page 95

Go To INDEX

The name of the method to be performed on that object Any values, or parameters, the method needs to know in order to carry out the action Any value returned by the method The object receiving the message uses this information to invoke the appropriate methods with the specified values. So in the case of the driver object sending a message to tell the car to accelerate, the message needs to include the name of the car object, the accelerate method, and the requested speed. These three parameters provide sufficient information to fully describe the message for the car object. Encapsulation facilitates the process of sending messages because it allows you to send messages to any object without having to know how the object works. Since the implementation details are hidden within the objects themselves, all you have to know is what parameters a method will accept. This means you can drive a car effectively without knowing details about how engines, transmissions, and brakes work internally. Remember, using the principle of polymorphism, a single message will give rise to different behaviors depending on the object it's being sent to. For example, if the same "accelerate" message is sent to a car and a tractor, each one will carry out the action in a slightly different way. And if the accelerate message included a speed parameter, then the degree of acceleration would be dependent on this parameter.

Interfaces to classes

When creating new classes, programmers sometimes derive the class code from previously defined superclasses. In this way, the new classes automatically inherit the data and methods of an existing class. This process is known as inheritance and it facilitates code reuse. Some programming languages, such as C++, enable you to derive a single class from multiple superclasses. While this is a powerful feature, it can lead to a very complex class hierarchy. Java supports single - not multiple - inheritance. This means that each class in the Java hierarchy - with the exception of the Object root class - has only one superclass. So any class you create can extend or inherit the methods of only a single class. If you have programmed in a language that supports multiple inheritance, single inheritance might seem like a limitation. But in fact it's quite the opposite. A Java program avoids the difficulties of multiple inheritance. But it still allows a new class to share the characteristics of multiple existing classes. Java supports the multiple inheritance of class methods through the use of an "interface". An interface is a type of abstract class that can be implemented by a number of real classes. Abstract classes are superclasses that act purely as a template for more usable subclasses. For example, a car class might be a candidate for an abstract class. You might never want to create a car object - it is too general - but it serves as a logical superclass for more specific car classes like electric cars and gas-powered cars. An abstract superclass contains one or more abstract methods, which are deliberately left unimplemented. This means they have been declared but have no methodBody.

Page 96

Go To INDEX

These methods are actually implemented in the subclass, which is derived from the abstract class. Interfaces allow you to declare abstract methods, which can be implemented by unrelated members of a class hierarchy. An instance of any class in the hierarchy can use these interfaces through the process of polymorphism. Remember, polymorphism allows a class to implement methods specific to it. This means that new classes are not restricted to inheriting methods from only their superclass. No objects can be created from an abstract class. This means that if you define an abstract class, you cannot instantiate it directly. Instead you create a class that implements the interface. When a class implements multiple interfaces it must provide all of the functionality for the methods defined in the interfaces. The major difference between an interface and a typical class is that an interface cannot store data. In addition, an interface does not provide an implementation for the methods in its class. But it does provide a method declaration.

Interface declarations follow this general format.

The interfaceBody refers to the abstract methods and variables that make up the interface. To implement an interface, you use the "implements" keyword.

Class declarations that use interfaces usually follow this format. Using interfaces, Java programs can offer many of the advantages of multiple inheritance without the associated problems. While the use of interfaces is not as powerful as multiple inheritance, they do allow multiple classes to inherit the same method interfaces, even when they're not related in the class hierarchy. And once an interface is supported by an application, programmers don't have to write new application code to take advantage of new classes, which implement the interface. So the use of interfaces facilitates rapid code development. In addition, because a single class can implement more than one interface, it is possible to share the same interface across several classes.

Page 97

Go To INDEX

4 Introduction to the Java Language
4.1. Java Syntax • Identifiers and keywords
Java shares a common look and feel with the C language. If you have programmed in C or C++ before, then Java syntax will appear familiar. But even if you have never programmed in C before, Java's syntax is simple to learn. Java programs are free-format. The compiler does not consider comments and white space to be meaningful. It strips the source code of these elements before parsing it. You are allowed to indent your code in whatever way you like. And you can put as many spaces or lines between statements as you think appropriate. You can use three types of comment in Java.

The first begins with a /* and continues until the first */ sequence is found. You are not allowed to nest /* */ comments. You can insert a comment on a line starting with two slashes // and continuing until the end of the line is reached. A good tip is to use // comments within your code blocks, so that if you want to comment out a section of code later, you don't have to worry about inadvertently nesting /* ... */ type comments. There is a special "doc" comment beginning with /** and continuing till a */ is reached. Doc comments are usually placed before declarations. This type of comment is used in conjunction with the javadoc tool to produce online documentation. The source code is parsed by the compiler into tokens. Tokens are the smallest meaningful elements of code that the compiler recognizes. User-defined names, known as identifiers, are an important category of token. Identifiers uniquely name all your classes, methods, or variables. It is considered good programming practice to choose identifiers that help describe what they stand for. In other words, your identifiers should help someone reading your code to better understand it. Java identifiers are composed of letters and, optionally, digits. But they must begin with a letter.

Page 98

Go To INDEX

White spaces cannot be used to separate words in your identifiers. So these are not valid Java identifiers.

Java would, in fact, treat each space-separated word as an identifier itself. Underscores "_", and dollar signs, "$", count as letters too. So these two identifiers are valid.

Identifiers are case sensitive. "theLargestInteger" is "thelargestinteger". Identifiers can be of unlimited length in Java.

not

equivalent

to

This allows you great flexibility in choosing meaningful names for your classes, variables, and methods. You cannot use identifiers that correspond to a reserved keyword in Java. For example, you cannot use 'while' as an identifier, since 'while' is a reserved Java keyword. You should try to follow Java conventions for defining identifiers. By following these conventions you make it easier for someone else to read your code. These conventions are not part of the language, but through practical experience have come to be considered an unofficial naming standard. The first convention is that identifiers should all be in lowercase, except for the first character of embedded words. Here are some examples.

Note how the first letter is in lowercase. Since you may not use spaces within identifiers, another convention used is to separate words within identifiers by using the "_", or underscore character, instead of a space. Here are some examples.

Java reserves a list of identifiers, called keywords, for itself. Here is a full list of the Java keywords, in alphabetical order.

Page 99

Go To INDEX

You may not use any of these reserved keywords as an identifier. The compiler will return an error if you attempt to use a keyword in an inappropriate manner. The keywords are used: To declare primitive types To declare classes, variables, and methods As flow control statements To define visibility levels for class methods The other reserved words include those for: Exception handling Class definitions Miscellaneous Unused reserved words If you use a development tool, then it will help you to read your Java code by highlighting reserved keywords in a different color. or variables

Types and literals

Java emphasizes simplicity, platform-independence, stability, and ease of use. These characteristics are especially apparent in the primitive data types. The full list of primitive types in Java is: Boolean Char Byte Short The list of primitive types also includes: Int Long Float Double The boolean type has only two possible values - true or false. Booleans are not integers, and true and false should not be confused with the numerals 1 or 0. Booleans replace the conditional expressions of C/C++, where 1 or 0 is returned. In fact, booleans
Page 100 Go To INDEX

cannot even be cast to or from any other primitive type, including integers. Here are two valid Boolean conditionals:

The first condition returns true, since it is greater than Zero. The second condition returns false, since j and k added together do not exceed 99. A byte is an integer of 8 bits long. It is a useful primitive type when dealing with I/O and lower-level data manipulation. Java's implementation rules for primitive types are quite strict. Strict size rules for primitive types help to enforce platform-independence. All primitives occupy a strictly defined number of bits. You can be certain that when you use, for example, a short integer, it will be implemented in 16 bits. There are also standard default values for each type. If you do not explicitly initialize variables, they will be set to this default value by the interpreter. All integers (byte, short, long, and int) are signed. Bytes, for example, range from -128 to 127, and short integers from -32768 to 32767. This means that you cannot use the C/C++ keyword "unsigned". The three types of integer literal in Java are: Decimal (base 10) Hexadecimal (base 16) Octal (base 8) Integer literals in Java are equivalent to those in C and C++. Decimal literals appear as normal whole numbers, unseparated by commas, with an optional minus sign, "-", denoting negative integers. Here are some valid integer literals as they could be used in initializing three int types.

Hexadecimals are defined by preceding the literals with "0x" (zero x).

Octals are defined with a leading zero, "0". You place the letter "L", in upper or lower case, after an integer literal to specify that it is a long integer, of 64 bits. Floating-point literals can have an "F", or "f", appended to indicate that they are of type float.
Page 101 Go To INDEX

"D", or "d", indicates that they are of type double. These are examples of valid float and double literals.

The letter "e", or "E", denotes an exponential value. For example, 7e3 evaluates to a float value of 7000.00. Char types are 2-byte, 16 bit Unicode characters. In most cases this makes no difference to the way you use characters or strings. The first 128 Unicode characters are identical to the ASCII set used in C. Using Unicode makes internationalization easier. Some Unicode character literals may not be displayable on certain platforms, so Java offers a way to refer to them using the "\u" escape sequence. These are valid Unicode characters.

These are two of the standard escape sequences in Java. Escape sequences represent special character values that cannot be displayed. Java's escape sequences are the same as those in C and C++. This is a complete list of the Java escape sequences.

These sequences can be used in both character and string literals. This string literal has a newline escape sequence at the end.

This string literal has embedded tabs. String literals are defined between double quotes, as in C.

Page 102

Go To INDEX

You are also able to use the "+" string concatenation operator when defining literals.

Arrays and strings

Arrays are non-primitive types, and share many features of the Java Object class. Arrays in Java closely resemble arrays in C and C++. Both objects and arrays are passed by reference, whereas primitive types are passed by value. Arrays that are no longer referred to are garbage collected, just like objects. For these reasons, objects and arrays are called the reference data types. Arrays, as such, are merely references to the data elements they contain. There are two steps to using arrays: Declaration Allocation Arrays are first declared to be of a particular type. Then space for their elements is allocated. These two steps can be achieved using a single line of Java code. You can declare arrays dynamically using the new keyword.

Square brackets in declarations, either after the type or object identifier, or after the array identifier, indicate that an array is being defined. Arrays can also be declared statically.

Any valid expression for the array type can appear as an element in the list. The compiler automatically calculates the array size. The first element is indexed as element "0", or zero. An array with 10 elements, therefore, is indexed from 0 to 9. In this example, "Tony" is accessed with peopleNames[0], and the fifth element,"Vonzell", with peopleNames[4]. Multidimensional arrays are defined as arrays of arrays. They are declared using multiple square brackets after the array type or array name.

These examples show two-dimensional arrays, but you can also declare arrays of three or more dimensions.

Page 103

Go To INDEX

Multi-dimensional arrays can be non-rectangular, or "triangular". Triangular arrays have to be allocated dynamically in C, while they may be allocated statically in Java. This example shows how a two-dimensional array of arrays of varying sizes is allocated statically in Java.

You access array elements by putting a valid integer expression, or integer literal, between square brackets after the array name.

The Java interpreter checks array bounds, and if they are too low or too high it throws an ArrayOutOfBoundsException exception. You can find out the length of an array by using the "length" field. So if you had an array called littleArray, you access its length by the expression littleArray.length. The length field is read-only. Strings are implemented as instances of a class called String, in the java.lang package. In Java, strings are not null-terminated arrays of characters, as they are in C/C++. Having a class for strings means that you can use a full range of methods to manipulate them. You can convert booleans, characters, integers, and floating-point numbers to strings using the valueOf method. For example, this code initializes two String types to represent an integer and a double respectively.

Strings (i.e. String objects), once initialized, are immutable - there is no way for you to change their contents. You must use the StringBuffer class instead. Bounds checking takes place at run time, as with arrays, and exceptions are thrown if invalid accesses are attempted. Strings can be initialized with the double quotes ("") and, optionally, the "+" concatenation operator. The "+" operator concatenates two strings.

Page 104

Go To INDEX

Casting, blocks, and scope

Casting is the conversion of values of one type to another, compatible, type. You will cover only the issue of casting between primitive types here. You can also cast between classes in limited ways. You may need to cast values returned from methods into another type for further processing. You may also want to cast a variable of one numeric type to another, in an expression that returns that other type. You cast from a source type to a destination type. You place the destination type reserved word between parentheses before the source expression.

In this example, we have to cast from an integer "hoursWorked" to a float before the expression can be evaluated. // where “salary” is of type float… public float get WeeklyWageBill ( int hoursWorked ) { if ( hoursWorked == 0) return salary * (float) this.hoursWorked; else return salary * (float) hoursWorked; } So we place the destination type reserved word, "float", between parentheses before the integer variable in the expression. When casting numeric values, sign is retained. But when casting from a floating point value to an integer type, you will lose the fractional part. There are some restrictions on casting between types. First, casting may result in loss of information. Casting "down" to a smaller type results in the left-most bits being eliminated. If you were, for example, casting from a long to an int, then the 32 leftmost bits are eliminated. Here is the list of casting operations, which result in no loss of information. From Type byte short char int long float To Type short, char, int, long, float, double int, long, float, double int, long, float, double long, float, double float, double double

In general, no information is lost when casting from an integer type implemented with fewer bits to one implemented with more bits, or from float to double.

Page 105

Go To INDEX

Some casting operations are illegal. You are not allowed to cast to or from a boolean type. So, boolean false will not translate to integer value 0, or vice versa. Here is a useful way to translate from integers to booleans: // 0 false, non-0 true b_var = (i_var != 0) ; // false --> 0, true --> 1 i_var = (b_var)?1:0 ; The conditional operator, "?:", will be explained in the unit Operators and flow control. All statements in Java belong to blocks. Blocks group together sets of related statements. Blocks can be nested to produce a hierarchy of blocks. Blocks appear between braces, "{}". You will notice that most Java constructs, like classes, methods, and flow control statements, use braces to group related statements together. This example shows three nested blocks of a class, a main method, and an if/else flow control statement.

Classes, methods, and flow control statements will be discussed in detail at a later stage of this course. One standard convention is to indent nested blocks by two spaces. This allows for easier visual identification of nested blocks, but has no effect on the compilation of code. You are advised to be consistent in your indenting throughout your code. Scope is the term used to define where a variable is visible, and its lifetime. Its visibility determines what code can access or use it. Once a variable's lifetime is ended it is destroyed and its value(s) lost. If you define a variable within a block, it is only visible within that block, or to subnested blocks. It is not visible to or accessible from its outer blocks. We say that the variable is local to that block. The integer ifInt is created upon entry into the if block and destroyed upon exit from that block. Similarly, the mainInt integer's lifetime covers the start to the end of the main block only. Scoping ensures that local variables are hidden from outer blocks. This is a good example of the concept of data hiding, which is encouraged by the Java language.

Page 106

Go To INDEX

4.2. Operators and flow control • Unary and binary arithmetic operators

Unary operators work on a single numeric variable and return a single numeric value. They are said to take a single operand. You can increment numerics by 1 using the "++" operator, and decrement numerics by 1 using the "--" operator. The assignment operator is "=". It assigns the right hand expression to the left hand variable. It follows the general form: variable = expression; In these examples, the assignment is implicit.

We could have replaced them with these longer, but equivalent, expressions using explicit assignment. You can also use an assignment with an operator, which you express by combining the symbol for the operator with the equals sign. These two examples are equivalent to incrementing and decrementing j and k respectively. J + = 1; K - = 1; This example shows how to multiply j by 3. J * = 3; // equivalent to J = J * 3 There are two forms of the increment and decrement operators: Prefix Postfix -- K; //decrement K, prefix J ++; //increment J, postfix

The prefix version modifies the variable before the rest of the expression it appears in is evaluated. You place the operator before the variable in question, as in these examples. The postfix version modifies the variable only after the expression it appears in is evaluated. You place the operator after the variable. The difference can be made obvious with this example.

Page 107

Go To INDEX

While j becomes 5 in both these examples, i will become either 4 or 5 depending on whether the postfix or prefix form of increment is used. The "-", or negation operator, negates a numeric expression. So j = -i; just negates the value of i and assigns it to j. The value of i is not modified by the operation. The bitwise negation operator, "~", toggles all the bits of an integer. All binary ones become binary zeros, while all binary zeros become binary ones.

Java's integers are stored in two's complement form, where the leftmost bit indicates sign.

A value of 1 in the leftmost bit denotes negative numbers, while a 0 denotes positive numbers. A bitwise negation operation always changes the sign of an integer value because the leftmost bit will change from 1 to 0, or from 0 to 1. Here are some examples and the resultant values.

Page 108

Go To INDEX

In binary, you change a two's complement number's sign by toggling all the bits and then subtract 1. So 0000 0011 (decimal value 3) becomes 1111 1101 (decimal value -3).

The complement, or bitwise negation, of 3 is -4, since 1 is not added after the bits are inverted. And the bitwise negation of -3 is 2 for the same reason.

Page 109

Go To INDEX

Binary operators take two operands. Addition, subtraction, division and modulus all work in the standard ways on integers and floating-point numbers.

The modulus operator applied to integers returns the remainder of a division operation. The modulus operator applied to two floating-point numbers returns the remainder, as a floating point number, after a division operation.

Bitwise operators work at the binary level of integers. Bitwise operations involve the systematic comparison of the corresponding bits of two integers, one by one. They are useful operators to have when integers are being used as bit fields, or groups of binary flags. In bitwise AND, "&", the result returned is 1 if and only if both bits are 1.

Page 110

Go To INDEX

In bitwise OR, "|", the result returned is 1 when either bit is 1.

In bitwise XOR, "^", 1 is returned only when the bits are different from each other.

The shift operators also work at the binary level.

The three shift operators shift the bits of an integer by a specified number of bits. Because integers in Java are signed, the standard right-shift operation, ">>", preserves sign. The alternative ">>>" operator shifts an integer and fills in the leftmost bits with

Page 111

Go To INDEX

zeros. This makes an important difference for negative integers, since they become positive after a ">>>" operation.

Relational and boolean operators
In Java, there are six relational operators: Equal to "==" Not equal to "!=" Greater than ">" Less than "<" Greater than or equal to ">=" Less than or equal to "<="

The relational operators compare arithmetic operands only, except for "==" and "!=", which can also compare other primitive types. They all return boolean true or boolean false. Boolean types require their own set of operations.

These operators take one or two boolean expressions and return either boolean true or boolean false. Logical AND, "&", returns true if both operands are true, false otherwise.

Page 112

Go To INDEX

true & true = true true & false = false Logical OR, "|", returns true only if either operand is true, false only if both are false. true | true = true true | false = true Logical XOR, "^", returns true only if the operands are different from each other. true ^ true = false true ^ false = true Another way to state this is that logical XOR returns true if, and only if, exactly one operand is true. For logical AND, "&", the result is assured false when the first operand evaluates to false. For logical OR, "|", the result is assured true when the first operand evaluates to true. For logical XOR, "^", both operands must always be evaluated. This is often a more efficient way to evaluate compound conditionals, as well as avoiding unwanted sideeffects. Here, you can clearly see that (i++>1000) evaluates to false, and that the whole expression then evaluates to false.

But using the & operator, the int j is incremented - a side-effect of evaluating the second operand. The && operator would not increment j, since it would cease evaluating the boolean expression after finding the first operand false. There is no need for a shortcircuit logical XOR operation, since both operands must be evaluated in any case, before a boolean result can be returned. The boolean negation operator, "!", toggles the boolean value. !true = false true = !false It returns true for a false operand, and false for a true operand.

Page 113

Go To INDEX

The ternary if-then-else, or conditional operator, "?:", takes a boolean expression and two other expressions.

If the boolean expression evaluates to true, then expression1 is evaluated and its value returned. If the boolean expression evaluates to false, then expression2 is evaluated and its value returned.

Expression construction
You can use multiple operators in a single expression. 10 * 5 + ( 4 / 2 )

These operations are performed in a particular order that is strictly pre-determined by the Java language. The first rule used to determine the order that operations are performed in is operator associativity. When you use two or more of the same operators in an expression, then the associativity rule states that they are evaluated in left to right order. Here are three examples of multiple operators in a single expression. i = j + k + l + m + n; i = j * k * 1; i = j / k / m; In each case, the leftmost operation is performed before the others. In the third line, j is divided by k and then the result is divided by m. Note that this is distinctly different to k being divided by m first, and then j being divided by that result. When different operators are used in a single expression, then the order of evaluation depends on the precedence of the different operators. All the operators have a strictly predetermined level of precedence, from highest to lowest. For example, multiplication and division are higher precedence operations than addition and subtraction. In this example, the multiplication of 8 by 99 precedes the addition of j to 8.

Page 114

Go To INDEX

You use parentheses around a sub-expression to explicitly set it to the highest precedence. This line shows how 8 is added to j first, then the result is multiplied by 99 - a completely different result. This table sets out, from highest to lowest, the precedence of Java operators.

Those appearing at the top are said to have higher precedence, and are evaluated first in expressions. The highest precedence operators are "()", "[]", and ".". Parentheses, as we have seen, explicitly promote the precedence of the expression they surround. Array access expressions are evaluated first, before the element returned can be used in another operation. The dot operator, ".", is used for de-referencing class members. At the next level are the unary operators: Increment, "++"
Page 115 Go To INDEX

Decrement, "--" Bitwise negation, "~" Boolean negation (NOT), "!" Then comes multiplication, division, and the modulus operator. The lowest level of precedence is reserved for assignment, "=", and assignment with operator, "op=". Associativity and precedence determine the order that operators are evaluated. But another issue still requires deciding - the order in which operands are evaluated. In binary operations, the left hand operand is always evaluated before the right hand operand.

Flow control statements

Flow control statements dictate the basic structure of your Java code. The two categories of flow control statement are: Branches Loops Branch statements execute blocks of code or do not execute them depending on the result of evaluating some test condition. Loops constantly repeat a block of code until some condition is met. You are forced to use proper boolean expressions that evaluate to either true or false, in test conditions in flow control statements. Testing for an integer to decrement to zero, using for example if (i--), is invalid in Java. The two basic branch statements are: if/else switch/case The if/else branch statement tests a condition that evaluates either as boolean true or boolean false.

Page 116

Go To INDEX

It executes the "if" block when the condition is true. The optional "else" block is executed when the condition is false. It you omit an "else" and the condition returns false, then nothing is executed and control passes to the next statement after the if. If/else statements can be nested.

This syntax illustrates three nested if/else statements. You may find nested if/else statements quite confusing at first. The rule of thumb is that an "else" belongs to the nearest prior "if" statement without an "else" already. You are advised to use braces in your code, even with single statements, to make the matching of nested if/else statements more explicit. The other branch statement is the switch statement.

Page 117

Go To INDEX

It can be used to replace complicated, nested if/else statements. The switch statement is the same in Java as in C/C++. An integer or character expression is evaluated. The short, int, byte, and char types can be used as values for the case labels. You may not use long integer types as values for case labels. The statement-list of the case clause whose label equals the result is executed. A statement list is any series of valid Java statements separated by semicolons. Unlike statement blocks, statement lists do not require enclosing braces. If no case constant matches the expression, then the default clause is executed. Once a statement list executes, then all subsequent statement lists are also executed, regardless of the case clause constants. Control is said to "fallthrough" the rest of the switch statement. To prevent this, it is common practice to use break statements after each statement list.

A break forces the interpreter to jump to the first statement after the closing brace of the switch statement. Here you see a simple switch statement.

Page 118

Go To INDEX

The loop expressions are: for while do/while The for loop executes a block of statements, or a single statement, a given number of times. There are three sections in the "for" clause: Initialization A test condition for terminating the loop A step expression A for loop generally has one or more loop variables.

Page 119

Go To INDEX

Loop variables are initialized in the initialization section, tested in the test condition section, and altered in the step expression. Let's look at a simple example. for ( int I = 0 ; I < 100 ; I++) This for loop features a loop variable of i, initialized to 0 in the initialization section. The for loop terminates when i reaches 100. And at the end of each step i is incremented by 1. Variables can be declared in the initialization section. Their scope covers just the for statement block alone. Variables with the same name outside of the block are not modified in any way. The initialization section has the facility to have multiple initializations or declarations separated by commas. The step section also has the facility to have multiple statements separated by commas. Here are some more examples.

The second example illustrates how to initialize two separate int loop variables. The third illustrates a slightly different example using a String variable. The while and do/while loops are very simple.

The statement body in a "while" loop executes until the test condition becomes false. The do/while statement body is guaranteed to execute at least once. The while and do/while loops are exactly the same as they are in C and C++. The goto statement is banned in Java, and is replaced by a combination of the break and continue statements, and labeled loops.

Page 120

Go To INDEX

You may notice that goto is a reserved keyword in Java, but is not implemented. A compiler error will result if you try to use goto as an identifier in your code. A break statement inside a while, do, or for loop stops the execution of that loop, as in C/C++. This code, for example, shows two break statements - one inside a for loop and another inside a while loop. The key difference in Java is that you can label loops with identifiers, followed by a colon, as in this example.

Notice the "top_loop" label before the while keyword. An optional label can follow the break keyword. If there is a label following the break, then control transfers out of the enclosing statement that the label refers to. You use "continue" in much the same way. It stops the execution of the current loop cycle, but restarts at the beginning of the loop again.

4.3. Java object orientation • Building a simple class

Classes are the fundamental building blocks of Java programs. A class is a collection of data and related methods that operate on that data. All data and functionality
Page 121 Go To INDEX

in a Java program is organized into classes. And the object-oriented principles of encapsulation, inheritance, and polymorphism are implemented using classes. Classes are the mechanism used to define objects in Java. Objects are instances of classes. Classes define a "template" for objects, while objects are dynamically created to represent instances of those classes. So a running Java program consists of interacting objects, which are all instances of classes. Many instances of a single class can exist side-by-side in a Java program. Methods operate on the data in the class. Data may include primitive types or other classes. Classes are split into three basic components: A class declaration Data Methods Let's have a look at a simple class, called Employee. This will illustrate the main syntax used to define classes in Java. Here, the class name is "Employee".

The convention is to use an uppercase letter to begin a class name. The class access modifier used in this example is "public". This means that the class is accessible, or visible, to all other classes. If a class is public, then the class name must correspond to the source file name, and there can only be one public class per source file. Java filenames and class identifiers are case-sensitive, so they must also correspond in case. There are other access modifiers too, which restrict in various ways the visibility of class members to other classes: The default access Private Protected Then the data for the class is defined. Here you can see four data variables defined. There are three main parts to any data declaration. First we specify the data

Page 122

Go To INDEX

access modifier. In this case we specify public data access. Then we declare the variable's type, String, a class itself. Data in classes can include primitive types or other classes. Lastly, we have the variable's name, "firstName". Convention specifies that the variable's identifier begins in lowercase, with subsequent words within the identifier beginning in uppercase. The Employee class has two Strings for names, an employee number, and a salary amount. Notice the "protected" access modifier for the "salary" variable. Next, the methods that operate on the data in the class are defined. The Employee class has three methods: Employee salaryIncrease getSalary Like classes and data, methods have access modifiers. All three of these methods have public access specified. You should notice that this method has the same name as the class itself. Methods with the same name as their classes are called constructors. Constructors are executed when new instances of the class are being made. Methods must declare a return value. The salaryIncrease method returns a floating-point value. And it receives an integer argument called "percentageIncrease". All classes have this general form. Objects need to be declared to be of a particular class. Then they are actually allocated space, or "instantiated". Let's define a new Employee object and call that Employee "johnny". Precede the object identifier, "johnny", with the class name, "Employee". We have only declared that johnny is an Employee, and have not instantiated it yet. Objects are instantiated using the new operator. Notice how we initialized johnny's first name, last name, employee number, and salary.

We are using a special method defined for this purpose in the class - a constructor method. Constructors are methods, which have the same identifier as the class name and are used to initialize new objects. Java allows us to declare and allocate objects in a single statement. You just combine the declaration and instantiation into a single line, as you see here.

Object data is accessed using the dot operator, ".". So, to access the social security number of an Employee in our example, we would use this syntax.

Page 123

Go To INDEX

currentEmployee is an int (an integer type) variable that takes on the value of johnny's employeeNumber. To invoke a method in the Employee class is also quite simple. Just use the object name, a dot, and the method name with any relevant arguments. This line increases johnny's salary by 7%, for example.

Or we can access his salary by calling the access method getSalary.

Packages and the import statement

Java classes are organized into packages. Packages usually contain groups of related classes. Every class is a member of a package. The package statement appears as the first statement in a Java file. The package's fully qualified name follows the package keyword. In this case, the package is called company.people. package company.people; Package names mirror the directory structure of the class files. Thus, all classes in the company.people package must appear in the path company\people (or company/people). Access to classes from within a package is different than access from external packages. For example, a protected variable is accessible to another class within a package, but inaccessible to packages from outside. Protected variables can be inherited by subclasses in other packages, but cannot be accessed by them. In other words, how you structure your packages can determine class, variable, and method access. If you do not specify a package name in your files, then your classes become part of an unnamed default package. The import statement is not to be confused with the C language directive, #include. It does not read in, or load, the referred-to packages or classes. It simply allows the programmer to refer to classes directly, without having to precede them with the package name. The java.lang package is imported by default into every Java program. So when you refer to Object, which is in the Java.lang package, you can refer to it as Object instead of as java.lang.Object. The import statements appear after the package statement and before anything else. There can be an unlimited number of them. There are only two forms of the import statement. The first form is: import package_name.class_name; Here we can directly refer to the class name in the import statement, without preceding it with any package name. So: import java.awt.peer.ContainerPeer; Means we can refer to ContainerPeer throughout our code. The second form is: import package_name*; It allows us to directly refer to all classes in the relevant package. Thus:
Page 124 Go To INDEX

import java.awt.peer.*; Allows us to access such classes as ContainerPeer and ListPeer without preceding them with the package name. Regardless of the package that a class belongs to, it is stored in a separate file. The name of the file is the class name, followed by a .class extension. A single source file can have multiple class definitions but only one public class. The source file has to have a .java extension corresponding to its only public class. We could, for example, store all the Employee-related classes in a common file, called Employee.java.

Deriving classes using inheritance

You can create a class in Java that inherits the data and methods of another class. In this example, we have declared a new class called PartTimeEmployee, which inherits from Employee. The class you extend is called the superclass of the subclass. In this case, the superclass is Employee and the subclass is PartTimeEmployee. You may, in turn, extend subclasses indefinitely. For example, we can extend PartTimeEmployee to create a new class StudentEmployee.

This means that one subclass may be the superclass of another class. As you can see, the terms subclass and superclass are relative. There are two optional clauses in the class declaration: Extends [extends superclass] Implements [implements interface] The extends keyword, followed by an identifier, indicates the superclass. You cannot extend a final class - a class deliberately defined to be unextendable. A class inherits the data and functionality of its superclass, and the superclasses of its superclass. If you do not explicitly specify a superclass, as in this example, then the default superclass is Object. public class Employee { … } Even if you extend from a class other than Object, that class or one of its ancestors will be a direct extension of the Object class. So, all classes have Object as an ancestor. Java has no multiple inheritance but simulates it instead with the concept of interfaces. A class declares that it is implementing an interface with the "implements" keyword followed by the interface name. Object is the only Java class that does not have a superclass.

Page 125

Go To INDEX

All Java classes belong to a single class hierarchy beginning at Object - the root class - and continuing to the lowest-level classes. That's why all classes inherit the methods of the Object class. The diagram illustrates a sample class hierarchy for the Employee class and its subclasses. There are three subclasses of Employee - PartTimeEmployee, ContractEmployee, and FullTimeEmployee. And there is a single subclass of PartTimeEmployee StudentEmployee. Let's imagine a simple scenario. PartTimeEmployees do not work a full week, so their salary will be specified as an hourly rate. And their weekly salary will be the rate multiplied by a new variable - called hoursWorked. Here you have defined a class, called PartTimeEmployee, and added an extra variable - hoursWorked.

This is the body of the PartTimeEmployee constructor method. Although a subclass inherits all the data and methods of its superclass it can still override the methods of its superclass. This is the salaryIncrease method, as defined in the Employee class.

Page 126

Go To INDEX

Let's see how you can override this class in the ContractEmployee class. Imagine a situation where it was a company policy not to give contract workers salary increases of more than 5%. You must change the salaryIncrease method to reflect this fact. This method overrides the Employee salaryIncrease method.

Notice that their names are identical. And the types and number of their parameters must be identical too, although the parameter names can be different. And notice how you can still call an overridden method, using the super keyword. There are two special modifiers used when declaring classes that change inheritance in Java: Final Abstract public final Employee { … } public abstract Employee { … }

The final modifier means that the class cannot be subclassed. In turn, all methods of a final class are final too. Clearly, final methods cannot be overridden, since the class cannot be subclassed. Final classes and methods allow the compiler to perform certain optimizations to the code. And the interpreter does not need to dynamically look up the class and its methods. The keyword abstract indicates classes that are not fully implemented, or which contain methods that are not fully implemented. The implementation must be provided by a subclass. They must be subclassed, and their abstract methods implemented, before they can be used. Abstract classes cannot be instantiated, since they are missing vital implementation details. And they cannot be declared final since they must be subclassed to be used.

Page 127

Go To INDEX

5 Anatomy of Java Classes
5.1. Overall Structure of a Class • Structure of a Class Definition
Classes are the fundamental building blocks of a Java program. They act as templates, defining the properties of objects within a program. Each instance of a class template represents an object. And each object encapsulates the data and methods defined by the class. Every Java class must be derived from a superclass. Within a Java program, classes form a hierarchical structure of superclasses and subclasses. Subclasses inherit data and methods from their associated superclass. But all Java classes are originally derived from a root superclass called Object. All the classes in your Java program can use the methods defined in the Object class. Object doesn't contain any data. The first step in generating a Java program is to define a class. The general form of a Java class definition is similar to that of a C++ class. Each class declaration typically includes: Optional modifiers such as private and public The class keyword A valid identifier for the class Optional clauses such as extends and implements The function of the class declaration is to define data and methods for objects in a given class. In Java, the data components of an object are called "instance variables". Variables and methods are sometimes referred to collectively as "class members". You can think of a Java class conceptually as having a public external section and a private internal part. The external section is made up of public methods and variables. Public methods and variables represent everything the external users of the class need to know, or are allowed to know. The internal part of a class is typically made up of private instance variables. It may also contain some private methods. Internal class members are invisible to the objects of other classes. Typically, you control access between class members and other objects using one of three access modifier keywords: Private Public Protected If you don't specify an access modifier keyword, the default access modifier is applied. This modifier specifies that only classes in the same package can access a class's variables and methods. A package is a group of related classes and interfaces.
Page 128 Go To INDEX

For more information about packages, see SmartForce’s Java course Introduction to the Java language. Class members declared with the private modifier are accessible to objects within their own class only. For example, an instance variable declared as private cannot be manipulated by methods outside its class. And a private method cannot be called from outside its class. In fact, the only way to gain access to a private variable or method from outside its class is by calling one of the public class methods. A public modifier denotes a class member as being directly accessible to all other classes. If a public modifier precedes the class keyword, that class is visible to all other classes. The protected modifier specifies that class members are accessible only to methods of classes in the same package and to all subclasses of that class, regardless of their package. This modifier is useful because it gives derived classes the ability to directly access protected variables within their parent classes. Methods are usually declared public and instance variables are normally declared private. Public methods represent the public interface of a class, or the services that the class provides. Using public data is uncommon and is a dangerous programming practice. Several access modifiers have been used in this program.

As you can see, these modifiers must explicitly precede an object's instance variable or methods. But within a class definition, they can appear in any order. The ability to hide instance variables and methods means that the exact layout of an encapsulated object is hidden from other parts of the system. In addition, data integrity is enhanced because unauthorized methods cannot access hidden data. Once you have declared a class, you need to create, or instantiate, objects within that class. Using the new operator, you can instantiate a single instance of a named class and return a reference to that object.
Page 129 Go To INDEX

className objectReference = new className () In this example, velazquez is a reference to an instance of Employee. Employee velazquez = new Employee (); The variable velazquez doesn't actually contain the object - the variable is just a reference to it. You are not limited to creating single references to one object. In fact, multiple variables can reference the same object. In this example, you create a single Employee object, referenced by the variables velazquez and vinny. Any changes to the object referenced by vinny will affect the same object referenced by velazquez. Employee velazquez = new Employee (); Employee vinny = velazquez; Using the new operator is the only way to create an object in Java. You can check if a particular object is an instance of a specific class by using the instanceof operator. objectReference instanceof className; The instanceof operator returns a true value if the object preceding the operator is an instance of the class following the operator. velazquez instanceof Employee A true value will also be returned if the class named on the left implements the interface on its right. objectReference instanceof interfaceName If the named object is not an instance of the specified class, or if it does not implement the specified interface, a value of false is returned. The instanceof operator will also return a value of false if the object specified on the left is null.

Using Variables In Java

Data is encapsulated in a class within the braces of the class declaration. This program declares a class named Employee, with four instance variables.

Page 130

Go To INDEX

A Java class can have both primitive data types and other classes as its instance variables. Every time you create a new object, you generate a distinctive set of instance variables. In this way, the instance variables of each object are separate from the variables of other objects. Each object has a copy of the instance variables of its class. Sometimes it may be desirable to make a single copy of a particular variable available to all instances of a class. You do this using the static modifier. Using the static keyword, you can create a variable that can be used outside the context of any instances. Static variables are equivalent to global variables and are accessible to other pieces of code, subject to any access modifiers specified in their declarations. The dot (.) operator is used to access instance variables within an object. This operator can also be used to call methods within an object. Let's look at the general form for accessing instance variables using the dot operator. objectReference.variableName = value; The variableName is the name of the instance variable inside the object that you want to access. You can see that Java uses the dot (.) operator in conjunction with the objectReference to specify the instance variable to be called. Using the dot operator in this way, you can store an employeeNumber value for velazquez. velazquez.employeeNumber = 004081; And this code lets you access the employeeNumber associated with velazquez. Int currentEmployee = velazquez.employeeNumber;

Page 131

Go To INDEX

Using object methods

Methods are functions attached to a class declaration. They enable a class to perform specific actions. In Java, methods are declared inside a class definition at the same level as instance variables. Because Java methods are all defined within a class, the scope resolution operator, which will be familiar to C++ programmers, is unnecessary. Remember; when methods are declared, you can specify a certain type of return value and a set of input parameters.

If no return value is desired, then you replace the returnTypeValue with void. And if no parameters are desired, the method declaration includes a pair of empty parentheses. Using this example, three methods are declared inside the class braces.

These methods are declared within the same scope as the instance variables. As you know, objects communicate with one another through messages. A message sent from one object requests that some method, or action, be carried out on another object's data. So passing messages is often referred to as method calling. Methods are called on an instance of a class using the dot (.) operator. The general form for calling methods within an object is shown here: objectReference.methodName (parameterList); The objectReference is any variable, which refers to an object. The methodName is the name of a method in the class from which objectReference was instantiated. And parameterList is a comma-separated list of values or expressions. The parameterList values exactly match in number and type with one of the methods declared as

Page 132

Go To INDEX

methodName in the class (or inherited from the superclass). This code enables you to increase the salary of velazquez by 7 percent. velazquez.salaryIncrease (7); Here the dot operator enables you to access salary information by calling the getSalary method on the velazquez object.

Using This And Super

Any variables that refer to objects of a class are known as "object references". In Java, there are two special keywords, which act as object references: this super In Java and C++, each object has access to a reference to itself called the "this" reference. The "this" reference represents the current object. The “this” reference is not an instance variable of an object. It is passed to the object as an implicit argument inside every nonstatic method call and references the current object. So when any nonstatic method of object is run, that method has access to the “this” reference. You can use the “this” reference for any reference to an object of the current class. It can be used to refer to both the instance variables and methods of an object. Let's look at an example of where you might use a “this” reference.

Suppose a parameter of a class's method has the same name as an instance variable of that class. When this method is called, the instance variable will be obscured by the identically-named parameter within the method. To access the hidden instance variable when you are using this method, you precede the instance variable's name with the keyword this and the dot operator. This enables the program to differentiate the object's hoursWorked variable from the parameter hoursWorked. Each time an object of a class is created, the variables defined in that class must be initialized. To avoid this tedious process, classes may implement a special method called a "constructor". A constructor is a method that initializes an object immediately upon creation.
Page 133 Go To INDEX

When an object of a subclass is instantiated, the superclass's constructor is called to initialize the superclass instance variables of the subclass object. The "super" reference is used to call the superclass constructor for the current object. Let's look at an example.

Here you have defined a class called PartTimeEmployee. This class is a subclass of the class Employee. Using the super reference you can invoke the constructor method of the Employee superclass. Each subclass inherits the data and methods of its superclass. But sometimes a subclass may redefine a superclass method using the same methodName. This is known as overriding a superclass method. Whenever an overridden method is referenced in the context of a subclass, the method in the subclass is the one selected. But you can access the superclass version of that method by using the super reference followed by the dot operator. For example, this statement lets you call a superclass's getWeeklyWageBill method. super.getWeeklyWageBill If you don't want to allow subclasses to override superclass variables or methods, you declare them as "final". All future references to a final class member will rely on the superclass definition.

5.2. Object Life Cycle • About Constructors

When you declare a class in Java, you can declare an optional type of method that performs initialization when you instantiate objects from that class. A constructor is a special type of method called when an object is instantiated. Constructor methods have unique characteristics and a unique purpose. They are used to set properties and to perform other initialization tasks when instances of a class are created. Constructors use syntax similar to that of methods, but they don't return values. The declaration of a constructor requires just a straightforward addition to the standard class declaration. There are two main rules for declaring constructors: The constructor name and the class name must be the same No return type is specified when declaring the constructor
Page 134 Go To INDEX

Java requires that an ordinary method declare the data type of the value that it returns. If a method does not return a value, it must be declared to return void. A constructor is automatically called when an object is instantiated. Generally, constructors are used to initialize the instance variables. Constructors can perform various tasks related to object creation, such as connecting to a server or performing some initial calculations. Constructors also provide one of the ways to give values to instance variables. Instance variables can: Be initialized in a constructor of the class Be initialized implicitly Have their values set later after the object is created In most cases, it is appropriate to provide a constructor to ensure that every object is properly initialized with meaningful values. For instance, it would be pointless to initialize a variable representing the hour on a 24-hour clock to 25. When you create an object of a class, arguments can be provided in parentheses to the right of the class name. These arguments are passed as parameters to the class's constructor. The use of constructors is closely linked to Java's use of dynamic memory allocation. Just as with other reference variables, you must allocate instances of classes. Instances of classes are dynamically allocated using the new keyword. Any time you allocate memory with new, that memory comes from the heap. The heap is the collection of memory from which class instances are allocated. The new statement tells the compiler to generate code, which will allocate memory for an instance of the class, and points a variable to the new section of memory. In the process of doing this, the compiler also calls the class's constructor method and passes the appropriate parameters to it. If you don’t create your own constructor, one will automatically be created for you that calls the constructor of the class's superclass. This default constructor does not take any parameters and if you could see it, it would look something like this.

This allows you to create new instances of your classes. The default constructor for a class calls the constructor for the superclass. YourClass yc = new YourClass(); It then proceeds to initialize the instance variables in the following way: Primitive or numeric datatype variables are set to 0 Booleans are set to false References are set to null If you define constructors for a class, Java will not create a default constructor for the class. However, you can overload a constructor in the same way that you would overload a normal method. To overload a method of a class, you simply provide a separate method definition with the same name for each version of the method. Overloaded methods must have different parameter lists. An object can now be instantiated with this constructor.

Page 135

Go To INDEX

This call creates an object with var1 set to 100 and var2 set to 2. The keyword "this" in the declaration refers to the current instance of the object. You use it to differentiate the var1 of the object from that of the parameter. Because local variables and parameter variables hide instance variables, they will be used unless the “this” keyword is used. Effectively, this appears in front of every call to an object's variable. So the var2 initialization of the constructor would be: this.var2 = 2; You could also use another constructor to set the initial values for both variables. The call to this constructor is similar to the call to the constructor when only one instance variable is to be instantiated. This call creates an object with var1 set to 100 and var2 set to 200. Note it is also possible for the programmer to provide a constructor without parameters. This is done by placing an empty set of parentheses after the class name when allocating a new object. Often, a class does not have any constructors without parameters provided for it, but it has other constructors. In such a case, a syntax error will occur if an attempt is made to call a constructor without specifying arguments to initialize an object of the class. A constructor may be called with no arguments only if: There are no constructors for the class (the default constructor is called) There actually is a constructor with no parameters in the class definition

Finalization

Constructors can acquire various system resources such as memory. Because Java has automatic memory management, you don't have to explicitly delete objects. Each class in Java can have a finalizer method that helps return resources to the system. When a resource is no longer referenced it is ready to be returned to the system. Finalization is a disciplined way to begin to give resources that are no longer needed back to a system to avoid resource leaks. If you define a finalize() method for your object, then you’ve enabled finalization for your object. Because finalize() belongs to the java.lang.Object class it is present in all classes. It is declared to be protected in java.lang.Object and thus must remain protected. The finalize method may be called some time after the system has determined that your object is a candidate for being returned. In effect, finalization means that before an object is returned to the system, the Java run-time system gives the object a chance to clean up after itself. Before an object is garbage collected, the run-time system calls its finalize() method.

Page 136

Go To INDEX

The finalize() method is the closest thing Java objects have to a destructor method. While the finalize() method is very similar to the destructor method in C++, it has some subtle differences. The intent is for finalize() to release system resources such as open files or open sockets before garbage collection. A class’s finalizer method always has the name finalize(). A finalizer receives no parameters. It does not return a value, so its return type is void. The finalizer method is empty by default. A class can have only one finalize method finalizer overloading is not allowed. Finalizers are rarely used with simple classes. A class can override the finalize method it inherits from a superclass. You can never be sure when the finalize method will be called. Because resources are not returned to the system at predictable times, you need to use the finalize() method carefully. If a resource is in short supply it is better to release it when it is no longer required rather than waiting for the finalize() method to be called. The finalize() method is declared in the java.lang.Object class. This means that when you write a finalize() method for your class you are overriding the one in your superclass. Your class can provide for its finalization simply by defining and implementing a method in the class named finalize(). Your finalize() method must be declared as follows: protected void finalize() For example, in a class that deals with sockets, it is good practice to close all sockets before destroying the object defined by the class. Therefore, you could place the code to close the sockets in the finalize() method. Once the instance of the class is no longer being used in the program and is destroyed, this method would be invoked to close the sockets. An object must shut itself down in an orderly fashion. Let's look at an example.

This finalize method merely closes an I/O file stream that was used by the object, to ensure that the file descriptor for the stream is closed. You can force object finalization to occur by calling the runFinalization() method in System: System.runFinalization(); This method calls the finalize() methods on all objects that are waiting to be garbage collected to free up system resources. You could also invoke: runFinalizersOnExit(boolean) This enables or disables finalization on exit. Doing so specifies that the finalizers of all objects that have finalizers that have not yet been automatically invoked are to be run before the Java runtime exits. While the finalize() method is a legitimate tool, it should not be relied upon. This is because garbage collection is not a predictable process. Garbage

Page 137

Go To INDEX

collection runs in the background as a low-priority thread and is generally performed when a system has no memory left. Consequently, it is good practice to attempt to perform such "clean-up" tasks elsewhere in your code. You should resort to finalize() only as a last resort and when failure to execute such statements will not cause significant problems.

Garbage Collection

C and C++ give the programmer the responsibility for reclaiming dynamically allocated memory. However, in Java the run-time system performs memory management tasks for you. Java provides a facility that automatically reclaims dynamically allocated memory that is no longer needed. In Java there is no need for an equivalent of the delete operator that C++ uses. This facility is known as garbage collection. The Java garbage collector is a mark-sweep garbage collector that scans Java's dynamic memory areas for objects, marking those that are referenced. After all possible paths to objects are investigated, those objects that are not marked (that is, not referenced) are known to be garbage and are collected. Java’s automatic garbage collector eliminates many memory leaks of the sort that occur in C and C++. Most of these memory leaks are due to orphaned objects. Orphaned objects are objects that are no longer referenced. A reference to an object that is held in a variable is dropped when the variable goes out of scope. Java’s garbage collection is not as efficient as the dynamic memory management code that experienced C and C++ programmers write. However, it is relatively efficient and much safer for the programmer to use. Java’s garbage collector runs as a low-priority thread waiting for higher-priority threads to relinquish the processor. When Java determines that there are no longer any references to an object, it marks the object for eventual garbage collection. The garbage collector runs when processor time is available and when there are no higher-priority runnable threads. The garbage collector will run immediately when the system is out of memory. Before an object is garbage collected, the garbage collector gives it an opportunity to clean up after itself through a call to the object's finalize() method. The garbage collector is a daemon thread - a thread that runs for the benefit of other threads. Daemon threads run in the background, usually when processor time that is available would otherwise go to waste. Typically this happens when a user pauses while interacting with an application. Java's garbage collection is an example of multithreading. Multithreading means that applications run several processes concurrently. You can explicitly drop an object reference by setting to null the value of a variable whose data type is a reference type. Setting an object reference to null marks that object for garbage collection provided there are no other references to the object. This can help conserve memory in systems that operate in certain ways. For instance, a system in which a variable referencing an object is not going out of scope because the method it is in executes for a lengthy period. When your program has finished using an object, there are no more references to the object. The object is finalized and is then freed. The finalization and freeing of the object happen asynchronously in the background. However, you can force object finalization and garbage collection using the
Page 138 Go To INDEX

appropriate method in the System class. The garbage collector runs in a low-priority thread, and runs either synchronously and asynchronously depending on the situation and the system on which you are running Java. The garbage collector runs synchronously when the system runs out of memory, or in response to a request from your Java program. You can ask the garbage collector to run at any time by calling System's gc() method: System.gc(); Asking the garbage collector to run does not guarantee that your objects will be garbage collected immediately. This statement invokes the static method gc of the System class to schedule the garbage collector to execute as soon as it can. You might want to run the garbage collector to ensure that it runs at the best time for your program rather than when it is most convenient for the run-time system to run it. For example, your program may wish to run the garbage collector right before it enters a compute or memory intensive section of code or when it knows there will be some idle time. Some systems require about 20 milliseconds for the garbage collector facility to finish its job. So, your program should only run the garbage collector when doing so will have no performance impact on your program. In other words, when your program anticipates that the garbage collector will have enough time to complete its task. Some systems allow the Java runtime system to note when a thread has begun and to interrupt another thread. In this case, the Java garbage collector runs asynchronously when the system is idle. As soon as another thread becomes active, the garbage collector is asked to suspend what it is doing and then terminate.

Serialization

Later versions of Java have added a new mechanism called serialization. The idea of serialization is to allow an object to be converted into a form that can be moved outside of the Java environment. The Java objects are bundled so they can be passed along in streams. Objects which have been serialized can be attached to any stream including FileOutputStream or PipedInputStream. The key to object serialization is to store just enough data about the object to be able to reconstruct it fully. The building of all but the most short-lived applications requires the capability to store and retrieve Java objects. Serialization lets you store objects as easily as you store text or numeric data. If you wanted to save information about objects to a file, you would: Save the type of the object Save the data that defines the current state of the object Previously if you wanted to do this you would have to write code for every class to explicitly load and save its data fields. This in effect is a manual task whereas serialization is automatic. When you read this information back from a file, you must: Read the object type Create a blank object of that particular type Fill it with the data that you stored in the file

Page 139

Go To INDEX

Object serialization provides a much easier way to do all this. When you are saving an object the class of that object must also be saved. The class description contains the name of the class as well as a 20-byte hash code of the data field types and method signatures. Java gets the hash code by: Ordering the field types and method signatures in a systematic way Applying the Secure Hash Algorithm (SHA) to that data Using the SHA means that each class is given a unique identification scheme - a fingerprint. The class fingerprint will change if the data fields or methods are altered in any way. Checking to see if this has occurred is very useful. Let's see why. Suppose a particular object is saved to a file on disk. And later, a change is made to the class such as deleting a data field. Now the data layout in memory no longer matches the data layout on disk. The memory could be corrupted if the data were read back in its old form. Java takes great care to make sure this type of memory corruption does not occur. It checks that the class definition has not changed when restoring an object. It does this by comparing the fingerprint of the current class with the fingerprint on disk. Serialization allows you to keep track of object versions. Java serialization means that all objects that are saved to disk are given a serial number. When Java is saving an object to disk it can find out if the same object has already been saved to disk. If it has, then the new version can be referred to as being the same as the previously saved object with serial number x. When Java is reloading an object it notes its serial number and remembers where it was put in memory. When Java encounters the tag "same as previously saved object with serial number x" it looks up where it put this object with serial number x. Then it sets the object reference to that memory address. Of course this process is carried out automatically by Java. The goals for serializing Java objects are to: Employ a mechanism that is simple yet capable of being extended. Maintain the Java object type and safety properties in the serialized form. Be extensible to support marshaling and unmarshaling as needed for remote objects. Marshaling and unmarshaling refer to the processes of packing and unpacking a method name and its arguments for transmission to a remote system. Be extensible to support persistence of Java objects. Allow the object to define its external format. Objects to be saved in a stream must implement either the "Serializable" or the "Externalizable" Interface. To allow itself to be serialized, a class should implement either the java.io.Serializable or the java.io.Externalizable interface. An example of a class which should not be serialized is a class, which represents inherently transient state, such as a java.io.FileDescriptor. For Java objects, the serialized form must be able to identify and verify the Java class from which the object's contents were saved. It must also be able to restore the contents to a new instance. For Serializable objects, the stream includes sufficient information to restore the fields in the stream to a compatible version of the class. For Externalizable objects, the
Page 140 Go To INDEX

class is solely responsible for the external format of its contents. Objects to be stored and retrieved often refer to other objects. Saving object references gives rise to a number of issues. Often one object is shared by several objects as part of its state. It is easy for several copies of an object to come into existence rather than several references to the one object. If you want to change an object, you then have to track down all copies of it to do so. It is better if you save and restore only one copy. You have to copy and restore the original references to the objects. This is because you want the object layout on disk to be exactly like the object layout in memory. This is known as persistence. Lightweight persistence is that which occurs in a relatively simple application. To maintain the relationships between the objects, those other objects must be stored and retrieved at the same time. When an object is stored all of the objects that are reachable from that object are stored as well. Object Serialization extends the core Java input/output classes with support for objects. It supports the encoding objects and the objects reachable from them, into a stream of bytes, and it supports the complementary reconstruction of the object graph from the stream. Serialization is used for lightweight persistence and for communication via sockets or Remote Method Invocation (RMI). The Java RMI (Remote Method Invocation) API enables method invocation between two remote Java applications. This can be accomplished in a client/server manner or in a peer-to-peer manner. RMI means that applets and applications on different machines can communicate with each other by invoking each other's methods. The default encoding of objects protects private and transient data, and supports the evolution of the classes. Object serialization produces a stream with information about the Java classes for the objects that are being saved. For Serializable objects, sufficient information is kept to restore those objects. This occurs even if a different, although compatible, version of the class's implementation is present. The interface Serializable is defined to identify classes that implement the Serializable protocol: package java.io; public interface Serializable { }; A Serializable object: Must implement the java.ioSerializable interface Must mark the object fields that are not to be persistent with the transient keyword Can implement a writeObjectmethod to control what information is saved or to append additional information to the stream Can implement a readObjectmethod so it can read the information written by the corresponding writeObjectmethod or to update the state of the object after it has been restored ObjectOutputStream and ObjectInputStream are classes designed and implemented to allow the Serializable classes they operate on to evolve. In other words,
Page 141 Go To INDEX

they allow changes to the classes that are compatible with the earlier versions of the classes. For Externalizable objects only the identity of class of the object is saved. It is the responsibility of the class to save and restore the contents. The interface Externalizable is defined as:

An Externalizable Object: Must implement the java.io.Externalizable interface. Must implement a writeExternal method to save the state of the object- it must explicitly coordinate with its supertype to save its state. Must implement a readExternal method to read the data written by the writeExternal method from the stream and restore the state of the object.- It also must explicitly coordinate with the supertype to save its state. It also must explicitly coordinate with the supertype to save its state. Supertypes are those types defined in the class from which an object is instantiated. If writing an externally defined format the writeExternal and readExternal methods are solely responsible for that format. The writeExternal and readExternal methods are public. This raises the risk that a client may be able to write or read information in the object other than by using its methods and fields. These methods must be used only when the information held by the object is not sensitive or when exposing it would not present a security risk. Let's look at a basic serializing example that shows how to read and write objects and primitives to a stream. The example shows you how to write today's date in serialized form to a file.

First an OutputStream, in this case a FileOutputStream, is needed to receive the bytes. Then an ObjectOutputStream is created that writes to the OutputStream. Next, the string "Today" and a Date object are written to the stream. More generally, objects are written with the writeObject method and primitives are written to the stream with the methods of DataOutputStream. You could also read today's date from a file. Let's look at an example:

Page 142

Go To INDEX

For most objects, the default writeObject and readObject methods provided by object serialization are sufficient. They write or read each field individually. A field that is marked transient or static will not be written by the default writeObject method, and will be set to the type-appropriate default value by the default readObject method.

5.3. Interfaces, Casting, And Reflection • About Interfaces

Some programming languages, such as C++, enable a single class to inherit characteristics from several superclasses. This is known as multiple inheritance. While this is a powerful feature, it is complex to use and can give rise to performance problems. Java supports single, rather than multiple, inheritance. Using single inheritance, each Java class can be derived from a single superclass only. If you want to derive a class from multiple superclasses, single inheritance may seem like a limitation. "But Java has a solution to this problem, called an ‘interface’ ". Using an interface, a single Java class can inherit unimplemented methods from many different classes. An interface is a special type of abstract class. As you know, abstract classes are superclasses that act solely as templates for more usable classes. It is not possible to instantiate objects directly from an abstract class. Instead, you define more specific classes, which implement the methods in the abstract class. Each abstract class defines at least one abstract method. Abstract classes are unimplemented classes that typically describe general behavior. Classes describing general behavior are located high in the class hierarchy. They provide unspecific methods that can be utilized by many subclasses. Interfaces and abstract classes are not synonymous. Abstract classes can define abstract and nonabstract methods. But all the methods defined in an interface are implicitly abstract and are therefore unimplemented. The unimplemented methods in an abstract class represent an interface. They determine what the class using the interface can do. An interface may also contain variables with constant values. These values are used by the class implementing the interface. An interface doesn't specify exactly how each of its methods work. A method's implementation contains a description of how a method works. In Java, interface declarations and the implementations of their methods are stored separately. Method implementations are stored in the class that uses, or "implements", that interface. Unimplemented methods are not much use on their own. They only become useful when they are implemented by concrete, or "real", classes. An interface is linked to any class that contains an implementation for the methods defined in that interface. So many different classes in the class hierarchy can use the interface in a similar way to inheriting from a superclass. Interfaces provide many of the same benefits as abstract classes. At their most basic level, they provide a set of methods that can be implemented by many classes.
Page 143 Go To INDEX

Like abstract classes, they can act as a template for deriving new classes in a structured manner. Since interface methods are unimplemented, they allow you to define methods for a class without worrying about the implementation details. This means that if the implementation of an interface method is altered, the change will not disturb the associated interface. And because implementation code changes are isolated, they help prevent the propagation of bugs. Perhaps the most important advantage of using interfaces is that they enable a single class to inherit methods from more than one superclass. This is a new twist on the concept of multiple inheritance. A C++ class can inherit method implementations from more than one superclass. In Java, a class can inherit method implementations from only one superclass. However, Java classes have the ability to inherit additional abstract methods through the use of interfaces. In this case, the class inheriting the interface must provide the implementation for the abstract methods.

Declaring Interfaces
This is the syntax for creating an interface in Java. interface InterfaceName { interfaceBody }

The interface keyword is used to declare a new interface. And the interfaceBody refers to the abstract methods and variables that make up an interface. This code declares a public interface.

All the methods and variables in a public interface are implicitly public, so the public modifier is optional here. Any variables declared in an interface are implicitly static and final. The declaration of these modifiers is optional. Even if you don't explicitly state them, they will be implicitly assumed by the program. In addition, interface variables may not be transient or volatile. This ensures that interface variables have constant values. As you know, the declaration of an interface method and its implementation are stored separately. The interface method declaration is stored in the interface definition. And the implementation is stored in the class that implements the interface. This is the syntax for declaring a method in a concrete class.

Page 144

Go To INDEX

A method declared in a concrete class will include implementation details, in the form of a methodBody. An abstract method has no methodBody. It is simply a method signature followed by a semicolon. returnValue Type methodName (parameterList); Any class that includes an abstract method is automatically abstract itself, and must be declared as an abstract class. You can see that the abstract methods defined as part of the Racket interface have a semicolon after their parentheses. There are no braces, and no methodBody is defined. Because all the methods in an interface are implicitly abstract, the abstract modifier is obsolete. This modifier should not be used in new Java programs. As you can see, declaring an interface in Java is very similar to declaring a class. But there are a few differences. For example, the only modifiers that can be used with the interface keyword are the public and abstract modifiers. Interface methods may not be declared as final, native, static or synchronized. In addition, the interfaceBody may not contain any constructors, static class initializers, or instance variables. Just as you can derive a subclass from a superclass, you can derive a new interface from an existing interface. You do this using the extends clause of an interface declaration. The derived interface implicitly inherits the variables and methods of the parent interface. But you can also define new variables and methods for the new interface. You can even derive a single interface from several existing interfaces. In this case, the interfaces named in the extends clause are separated by commas.

An interface acts as a template for a usable class. In fact, you can't use interface methods until the interface is implemented by a concrete class. In order to generate a class, which can implement interface methods, you use the implements keyword.

The className variable represents the class and interfaceName represents the interface this class will implement. A class implementing an interface method must provide an implementation, or methodBody, for that method.

Casting

The process of converting one datatype to another is called "casting". Java supports casting between data types. Since each class is considered to be a new type, Java also supports casting between classes. Let's look at an example of class casting. Suppose B is a subclass of A. If you cast B to A, then an instance of B can be used as an instance of A. As you can imagine, casting can give rise to some potentially complex manipulations. The Java language restricts casting to help prevent system corruption.

Page 145

Go To INDEX

Let's look at the casting relationship between superclass and subclass objects. In Java programs, objects can be cast: From a subclass to a superclass From a superclass to a subclass Casting between sibling classes isn't allowed in Java. Attempting to do this causes a compile-time error. If a subclass object is cast to a superclass, the object will be treated as if it were an object of its corresponding superclass. This is called "widening". You can cast from a subclass to a superclass implicitly or explicitly. An explicit cast isn't required when you are casting from subclass to superclass. But, for clarity, it's good practice to use one. You use this syntax to cast a class explicitly. (className) ref The “className” is the object being cast to and “ref” is a reference to the object being cast. The cast type must be provided in parentheses. Casting affects only the reference to the object, not the object itself. Because subclasses contain information tying them to their superclasses, casting from subclass to superclass is completely reliable. However, superclass objects cannot automatically be cast as subclass objects. To cast a superclass object as a subclass object, you must use an explicit cast reference. This is known as "narrowing". In this type of casting, the compiler has no way of knowing if the object being cast is an instance of the subclass to which it is being cast. As a result, narrowing isn't considered to be completely reliable. Whenever you cast from a superclass to subclass, the cast is automatically checked at run time. This ensures that the object is actually an instance of the subclass, or is one of its subclasses. If the object is not an instance of the subclass, then a runtime exception is generated.

Reflection

Reflection describes the ability of Java code to retrieve information about Java classes during run time. Using reflection, you can find out the access privileges, fields, and methods associated with a given class. You can also retrieve information about the superclass and interfaces associated with a class. It is even possible to query the constructors used in a class. In order to inspect Java classes, you need to specify a reflection environment. This environment provides the functionality needed to view class information. In Java 1.1, the Java root class Class has been extended to support the concept of reflection. The API defines a number of new classes, including: Constructor Field Method Array Each of these classes defines new methods in addition to those inherited from the root class, Object. These classes are contained in the library java.lang.reflect. The Constructor class is used to create new objects. The Field class provides "get()" and "set()" methods. These enable you to read and modify the fields associated with objects in

Page 146

Go To INDEX

a class. The Method class is used in association with the "invoke()" method. This calls methods associated with the objects in a class. In this example, the call returns an array of Method objects that describe all public methods of the class to which x belongs. x.getClass.getMethods(); The Core Reflection API accommodates two categories of applications. One category is comprised of applications that query and use all of the public class members of a target object, based on its run-time class. These applications require run-time access to all the public fields, methods, and constructors of an object. Examples of applications that query public class members would include service applications, such as Java Beans, and lightweight tools, such as object inspectors. These applications use instances of the classes Field, Method, and Constructor. Instances of these classes are obtained through the methods: getField getMethod getConstructor getFields getMethods getConstructors The second API category consists of sophisticated applications that query and use the members declared by a given class. These applications require run-time access to the implementation of a class at the level provided by a class file. Examples of applications in this API category include development tools, such as debuggers, interpreters, inspectors, and class browsers. Run-time services, such as Java Object Serialization, are also included in this category. Development tool applications use instances of the classes Field, Method, and Constructor. Instances of these classes are obtained through the following Object methods: getDeclaredField getDeclaredMethod getDeclaredConstructor getDeclaredFields getDeclaredMethods getDeclaredConstructors Of course, there are some limitations on the information you can retrieve using reflection. Security provisions ensure that the private properties of a class are revealed to authorized code only.

Page 147

Go To INDEX

5.4. Exception Handling • Handling Exceptions As Objects

Programs are often designed with a view to making them work under ideal conditions. A robust program is one that will function properly even under unusual or exceptional circumstances. Java encourages the development of robust programs by allowing you to anticipate exceptional conditions that might affect your program. Exception handling is the feature that Java uses to help guard against errors and exceptions. An exception is something unexpected or unusual that might occur during the execution of your program. For instance, a user could input a character when your program is expecting an integer. One of the common exceptions that programs produce occurs when an attempt is made to divide a number by zero. When an exception occurs the normal flow of the program is interrupted. Your program could crash as a result. With a traditional programming approach you would build some error correcting code into your program. But this would lead to your code becoming expanded and bulky. The traditional approach does not encourage strong error checking either. Java's implementation of exception handling makes it easier to handle and recover from run-time errors. Using Java exception handling enables you to separate the error handling code from the functional parts of your program. This improves program clarity and enhances modifiability. In this way exception handling separates normal processing from error handling. Exception handling is provided to enable your programs to catch and handle errors rather than let them occur and suffer the consequences. Exception handling catches run-time errors that cannot be caught at compile time. There are two basic approaches to exception handling. These are termination and resumption. In termination (which is what Java and C++ support) a run-time error is assumed to be so acute that it is not possible to immediately re-execute the code that generated the error. A block of code is terminated when an error occurs. An error handler is then executed. Program execution continues at the next block of code. The other approach to error handling is called resumption. When resumption is used the exception handler is expected to do something to rectify the situation, and then the faulting piece of code is reexecuted, presuming success the second time. If you want to implement the equivalent of resumption in your Java program, it means you want to resume execution at the piece of code that generated the exception after the exception is handled. To achieve this you can use the techniques described in this unit to place your code inside a while loop that keeps reexecuting the code until it executes successfully. When an error occurs in your program, the Java run-time system generates an exception object representing the error. The presence of exception handling code imposes very little execution-time overhead on programs unless or until exceptions occur. This act of generating an exception object is called "throwing an exception". A thrown exception will crash your program unless it is dealt with or handled in the correct manner. To be handled, the exception object must first be caught.

Page 148

Go To INDEX

An exception handler is a piece of code written into your program that catches and performs actions on the thrown exception object. When an exception object has been thrown, the Java run-time system begins the search for a handler to manage the exception. It first looks in the method where the error occurred, checking to see whether it has an appropriate exception handler — one that handles the class or a superclass of the thrown exception. Whether you need an exception handler to successfully compile the program depends on the type of exception thrown. The class of the exception determines whether a handler is required. You can derive your own customized exception classes if the ones Java provides do not suit your particular needs. If no appropriate handler is found, Java will continue up the run-time stack and look at the next method. This process repeats until the top of the stack is reached or until an appropriate handler is found. If there is no appropriate handler Java calls the default exception handler which displays a message describing the exception and dumps the stack trace to standard output. Your program will then terminate. Exceptions, like everything else in Java, are objects. Consequently, you can explicitly generate an exception object by instantiating it with the new operator. Exception handling should be used in any scenario where you anticipate that code may generate a run-time error due to factors that are not easily controlled by your program. There are a number of categories of problems you need to consider: User input errors Device errors Physical limitations of disks etc. The exception object must be an object belonging to one of the subclasses of the Throwable class. You can picture the Throwable class as being at the top of the Exception class hierarchy in Java. All exceptions are derived from the Throwable class. Immediately below Throwable are two subclasses that divide exceptions into two distinct branches. One branch is headed by Exception and the other by Error. Usually, the exceptions which will be handled by your programs will be instances of one of the subclasses of Exception. The Error class is for internal errors and resource exhaustion inside the Java run-time system. You should not normally attempt to catch exceptions derived from the Error class because they represent serious errors within the run-time system. Instead the main focus of your exception handling should be on catching exception objects derived from the Exception side of the Throwable class hierarchy. Objects derived from the Exception class can be of two types: Those that are derived from RuntimeException Those that do not. A run-time exception typically results from something erroneous in your programming. Any other exception usually occurs because an otherwise well-written program was used incorrectly. Exceptions that are derived from RuntimeException include: Attempting to access an out-of-bounds array element Bad casting
Page 149 Go To INDEX

Arithmetic exceptions such as divide by zero Exceptions that are not derived from Runtime Exception include: Reading past the EOF flag Trying to open an incorrect URL Trying to access a class or an object using a string that contains the nameof a non-existant class

Handling errors using try, catch, and finally

Once an exception object has been thrown it has to be caught so that it can be dealt with properly. Catching an exception requires a little more effort than throwing one. To catch an exception you must set up an exception handler. Exception handlers are often referred to as try-catch blocks. They consist of a try block and a catch block, each of which performs a particular role in the exception handling mechanism. It is really the catch block part that handles the exception. You place the code that may cause the exception in a try program block. The exception handling code goes in the catch program block.

Java executes the code in the try block first. This is called the try block because you try running your code there. If an exception occurs Java ignores the rest of the code in the try block and jumps to the catch block. The program now handles the exception. If the code in the try block executes without throwing, an exception Java skips the catch block. There is no need in this case for an exception handler because no exception has been thrown. Checking for errors in a programming language that doesn't support exception handling is problematical. You have to surround every method call with setup and error testing code, even if you call the same method several times. But with exception handling, you put everything in a try block and capture all the exceptions in one place. This makes your code much easier to write and read because the goal of the code is not confused with the error checking. You can also catch multiple exception types in a try-catch block and handle each type differently. Each exception object (e1,e2,e3) contains information about the nature of the exception. You can also find out more about the exception object. For instance, you can get a detailed error message. e1.getMessage();

Page 150

Go To INDEX

You can also find the actual type of the exception object. You will sometimes want a particular block of code to execute even if an exception has occurred in a try block. e1.getClass().getName(); To do this you use a finally clause after your exception handling code. Finally is an optional block, which provides code to be executed regardless of whether an exception occurs. Java stops processing all code in the try block within a method when it throws an exception.

However, local methods often acquire resources that only they know about. A problem arises if these resources are not cleaned up by the method. One solution to this type of problem would be to catch and rethrow the exceptions. However, this is cumbersome because you need to clean up in both the normal code and the exception code. The finally statement is Java's solution to this problem. It is used for doing essential cleaning up that under no circumstances may be omitted. Let's look at some possible scenarios. If the code in your method throws no exceptions, Java first executes all the code in the try block. Then it executes the code in the finally block. Your code could also throw an exception, which gets caught by a catch block. In this case Java will execute all code in the try block, up to the point at which the exception is thrown. The remaining code in the try block is then skipped. Next, Java executes the code in the relevant catch block, and then the code in the finally block. If the catch block does not throw an exception, the first line after the try-catch block is executed. If it does throw an exception, it will then throw it back to the enclosing block of code, or to the part of the program that called the method containing the try-catch block. Your code might throw an exception that is not caught by any catch block. Java will execute all the code in the try block up to the point where the exception is thrown. Then Java executes the code in the finally block. Next, the exception is thrown back to the enclosing block of code, or to the part of the program that called the method containing the try-catch block. You can also use nested exceptions. In this case if the int variable y evaluates to 0, an arithmetic exception is thrown. When this exception is caught the code attempts to perform another division operation. However, this is also a divide-by-zero error. Another exception is thrown and is caught by the nested ArithmeticException catch block. Suppose that there was no divideby-zero problem but the array index was illegal. In such a case, the outer ArrayIndexOutOfBoundsException catch clause would catch the exception.

Exception Objects And The Throw Keyword
Go To INDEX

Page 151

Java allows every method an alternate exit path if it is unable to complete its task in the normal way. In such a case the method does not return a value. Instead it throws an exception object that encapsulates the error information. The method exits immediately without returning a value. The code following the method call is not activated. Instead the exception handling mechanism begins a search for a handler that can deal with that particular error condition. There are times when it makes sense for your program to deliberately throw an exception. This is the case when your program discovers some sort of exceptional or error condition, but there is no practical way to handle the error at the point where the problem is discovered. The program can throw an exception so that another part of your program will catch and handle the exception. You use the throw keyword to throw an exception. The syntax of the throw statement is: throw exception-object; In most cases, it will be a newly constructed object created with the new operator. The string parameter in the constructor becomes the error message in the exception object. throw new ArithmaticException(“Division by Zero”); You could also assign an identifier. When an exception is thrown during the execution of a block of code and the exception is not handled in that block, then the block is terminated. EOFException e = new EOFException(); throw e; Remember that try-catch blocks can also be nested. Then the enclosing block of code or the calling method gets a chance to handle that exception. If it doesn't do so, then it too is terminated and the next enclosing block of code or the next calling method makes the next attempt at the exception. The exception will crash the program only if it passes up through the entire chain of method calls without being handled. A method that explicitly throws an exception must indicate this by adding the following to the declaration of the method: throws exception-class-name This method returns a string if it executes as expected. public string readLine() throws IOException However a provision is made in case an error occurs. If an error occurs the method will throw an object of the IOException class. Your program may throw an exception in one of four possible cases: You anticipate an error and write code which throws an exception with the throw statement You call a method that throws an exception You inadvertently make a programming error which causes a run-time error An internal error occurs in the Java run-time system
Page 152 Go To INDEX

In either of the first two cases, you must declare publicly that your method may throw an exception. You do this by including an exception specification in the header of the method. Note you can rethrow exception objects by calling throw in a catch block.

Occasionally, you may need to catch an exception without addressing its root cause. For instance you may need to do some local cleaning up. In this case, calling throw again sends the exception back up the calling chain.

Because the graphics object in the catch clause is a local object of the method, it might not be disposed of for a long time unless you do so explicitly. You must specify in the method declaration every exception that you throw within that method, but is not handled within the method itself. However you do not have to declare that a method may throw internal Java errors that is exceptions derived from Error.

Neither should you declare that a method may throw exceptions derived from RuntimeException such as ArrayIndexOutOfBoundsException. However if you expect these types of exceptions to occur, you should handle them as close as possible to the point in your program where they will occur. An exception which is generated by the Java run-time system is often called an implicit exception. All other exceptions are known as explicit exceptions. Explicit exceptions are those explicitly thrown by code you write. They are normally derived from the Exception class rather than the Error class. Implicit exceptions are typically due either to programming errors (RuntimeException) or are out of your control (Error). It is important to note that a method must declare all the explicit exceptions it throws.

Page 153

Go To INDEX

6 Java User Interface Programming
6.1. User Interface Architecture • Structure of an applet
Java is most often associated with the Internet and the WWW. Web pages can be brought to life by embedding applets written in Java in them. But Java is not used merely for writing applets. It can be used to write and develop full scale applications also. The declaration of an applet is quite straightforward. You should note that the new class is declared as public so that the class can be accessed when the applet is run in a Web browser or in the applet viewer application.

If you fail to declare the applet class as public, the code will still compile but the applet will refuse to run. So all applet classes must be public. The code for an applet does not have a main method like the code in a conventional Java application program does. The Applet class provides the foundation for applets - all applets are derived from the Applet class. Every Java applet you create inherits a set of default behaviors from the Applet class. So you must import the java.applet package to begin with. In most cases, these default behaviors do nothing. In fact, you have to override some of the methods of the Applet class in order for your applet to provide useful functionality. There are five methods in particular which trace the life cycle of an applet. The following four methods inherited from the Applet class let you manage the life cycle of your applet: init() start() stop() destroy() None of these methods are absolutely required to be overridden. However, overriding them is necessary in all but the simplest applets. The fifth method, which relates to the life cycle of an applet, paint(), isn't defined in the Applet class. Instead, Applet inherits paint() from the Component class, a superclass in Applet's chain of inheritance, which goes from Applet to Panel to Container and finally to Component. An applet does not use a conventional constructor to perform initialization. Instead it uses the init method, which works much like a constructor. A default constructor can be provided for applets. However, all initialization will usually be performed in the init method. You can use init() to initialize your applet's instance variables. It's called automatically by the system when Java launches an applet for the first time. The init method takes the following form:

Page 154

Go To INDEX

public void init() { } You typically override this method to set up resources that will be used throughout an applet. Examples of such resources are fonts and buttons. This method is called once and only once during the life cycle of your applet. However, if a new instance of the applet is loaded for some reason or another, the init method will be called again. An example of when you need to override init() is when you are allocating resources for the applet, such as AWT components. The next stage in the applet life cycle is the start section which begins when the start method is called. The start method takes the following form: public void start() { } Start() is called automatically after Java calls the init method. While init() is called only once during an applet's life cycle, start() may be called many times. Let's see why this is so. Suppose you open a web page, which contains an applet. The init method is called to initialize the applet. Next the start method is called. You could then load another web page, which becomes the active page in your browser. To keep the applet from the first web page running would be a waste of certain resources. Each time you revisit the page that contains the applet the start method will be called to reactivate the applet. This means that start() can be called repeatedly. Because start() can be called repeatedly and init() only once, you put the code you need executed only once into init() and not start(). If the start method is to be called repeatedly, there is another method, which must be called in between each call to the start method. This method is the stop method. The form of the stop method is: public void stop() { } The stop method is the third of the four methods in the Applet class that give you a structure on which you can build a useful Java applet. The stop method is automatically called when the user moves off the web page in which the applet is embedded. So, it can be called repeatedly just as start() can. The system can stop the applet by calling its stop method and then later restart it by calling its start method again. The stop method is also called just before an applet is destroyed. An applet that has been stopped will not receive any events until it has been restarted. The purpose of stop() is to give you a chance to prevent a resource-consuming activity from slowing down the system when the applet is no longer the main focus of the display. The stop method is the converse of the start method. Sometimes your applet does not do anything that needs to be suspended when the user leaves the web page containing the applet. In this case, you do not need to implement either the stop or start methods. The init method also has a converse method in the life cycle of an applet. This method is called destroy(). The destroy method takes the form: public void destroy() { } Although Java is not guaranteed to call the finalize method for objects, it will call destroy() when the browser shuts down normally. However, like finalize() the destroy
Page 155 Go To INDEX

method is a good place to do some cleaning up. And just like finalize(), it's unpredictable when destroy() will be called. Therefore it should be used with some discretion. Applets typically reside on a web page so you do not need to bother with destroying them. This happens automatically when the user shuts down the browser. Any code for dereferencing system resources should be put in the destroy method. These resources could include the fonts that you might have used. The destroy stage occurs when the system is about to remove the applet from memory. Like the initialization cycle, the destroy cycle occurs only once. If your applet has resources that need to be cleaned up before the applet exits, destroy() is the place to do it. Although there is a paint stage between the stop and start stages, it is often forgotten that the paint method is part of the life cycle of an applet. This "paint stage" occurs whenever the applet's display has to be drawn on the screen. This happens right after the applet's start stage, as well as whenever the applet is redisplayed or changed. An applet's display is redisplayed or changed whenever it is brought to the forefront of the screen, or when the program changes the applet's display in some way and explicitly repaints the applet. Every applet you write will have a paint method, which is the method you override to display your applet. Note that you must import the java.awt.* libraries, which contain information about the Graphics class, in order to override the paint method. The default implementations of init(), start(), paint(), stop(), and destroy() have no effect. If you want your applet to do something useful at these stages in its life cycle, you have to provide the code yourself by overriding the appropriate method. The form of paint is as follows: public void paint (Graphics g) { }

Overview of the AWT package

Java lets you write programs that use a Graphical User Interface (GUI). The class library for basic GUI programming is the Abstract Window Toolkit (AWT). The AWT package provides an Application Programming Interface (API) for common user interface components such as buttons, menus, list boxes, and scrollbars. AWT provides an underlying foundation for interfacing with the user. One of the main goals of Java is to provide platform-independent GUI development. The GUI area has always been one of the most problematical parts of creating portable code. Java's AWT must interact with the GUI components of the native OS - a task that an applet cannot otherwise accomplish. Applets do not make direct calls to an OS because otherwise they could violate the security of the user’s machine. Components are the building blocks of the AWT. The end-user interacts directly with these components. Among the GUI elements provided by the AWT are: Buttons Radio buttons Lists Labels Checkboxes

Page 156

Go To INDEX

Other AWT GUI elements are Text fields Text areas Canvases Scrollbars Menus The AWT addresses graphical interfaces on two different levels. At the lower level, it handles the raw graphics functions and the different input devices such as the mouse and keyboard. At the higher level, it provides a number of components like scrollbars and buttons that you would otherwise have to code yourself. Lets look at some of the more important classes in java.awt. The Component class is the foundation of basic controls such as buttons and labels. It is also the superclass of more sophisticated controls, such as dialog boxes, and even the Applet class. Container is a class that contains components or other containers. Panel is a visual container that can be used to hold other components, such as buttons, list boxes, and other containers. The AWT Window class is used for popup-style components, such as dialog boxes. The Font class is a class that produces font objects, which can be customized, such as point size and style. The Event class encapsulates user and system initiated events, such as mouse clicks, keyboard strokes, and the shutdown of an applet. The AWT notifies your applet whenever a key is pressed or released, whenever the mouse moves, and whenever you press or release a mouse button. The Graphics class is mainly used when a Component needs to be painted. Graphics encapsulates a wide range of functions, including drawing text, polygons, and images. MenuComponent provides the foundation for creating menus. AWT graphics utilities are low level utilities. While not providing as much functionality as some 2D drawing packages, the Graphics class lets you draw lines, boxes, circles, and polygons in different colors. Using the AWT you can select different fonts and font styles for drawing text. You can draw text in different sizes, proportions, and colors. You can also determine the size of the characters you want to draw. Java has become a popular tool for animating Web pages. Using the AWT you can create different kinds of animation, from drawing a series of static images to creating flicker-free animation applets. AWT lets you load images and draw them. The AWT contains methods for loading GIF and JPEG files from the network and displaying them easily. You don't necessarily have to know anything about the GIF and JPEG formats to use them.

Designing a user interface with AWT

When you are building a GUI using the Java AWT you combine a number of components. These are the essential building blocks of your user interface. Components are things such as buttons, text areas, and scrollbars. You use the AWT to create the code for most parts of your user interface. For instance, you need to write to: Present the components exactly as you want them to look

Page 157

Go To INDEX

Position them correctly within a window. As a rule in the AWT you do not specify absolute positions for interface elements. Have them respond to user input For a GUI-based program, you add the code to create the user interface elements in the constructor of the derived class. Java programs with a GUI typically use a class derived from Frame to represent the top-level window. Within that window you can have divisions derived from the Panel class. Each Panel can group a bunch of components together. This allows you to structure your interface more efficiently. One Panel could be used to group buttons together while another could contain a text area. You might also want to include an area where you can draw objects such as lines or circles on the window. The solution for this is to add a canvas to the window. A canvas is a rectangular area, which you can draw in. You derive a Canvas object as follows: class SomeCanvas extends Canvas By default, a Canvas object has no specific size. At the minimum, you should implement the setSize method in a canvas. It is also worthwhile implementing getMinimumSize() and getPreferredSize(). In order to make your canvas do something worthwhile, you should override the Canvas paint method. Most GUI displays incorporate buttons. Clicking a button causes something to happen. You don’t explicitly paint a button or any other kind of control. You simply place them on the frame and let them automatically take care of painting themselves. You call add() to add the buttons to your frame.

The add method takes as a parameter the particular component you want to add to the frame. The constructor for a Button object lets you specify the string that you want to be displayed on the button. If you want to customize the color of the button, you must: Provide a name for the Button object Invoke the setBackground method Add the button

Sometimes it is better to get user input by offering a choice of options. Java uses the Checkbox component for this. It overcomes problems associated with the user
Page 158 Go To INDEX

inputting plain text in response to a prompt. Error-checking code is also minimized by using a series of checkboxes as opposed to receiving text input from the user. A checkbox has two parts, a label and a state. The label is the text that is displayed next to the checkbox itself. The state is a boolean variable that indicates whether the box is checked. By default, the state of a checkbox is false, or Off. You can create a checkbox using a constructor that takes the label string as an argument.

Whenever a checkbox is selected or cleared an event occurs, which you can capture the same way you do a button event. You can check to see if a checkbox has been selected by using the getState method. public Boolean getState(); Radio buttons are another component that you can build into your GUI. They are used in a different way to other buttons on a user interface. You can use radio buttons to get user input. Using radio buttons allows you to force a single choice among many. They are called radio buttons because they work in the same way as the band selector buttons on a radio. Each button can have one of two states - either Off or On. But only one button in a group of radio buttons can be On at a given time. The AWT does not have a separate class to represent the radio button so it reuses Checkbox instead. To implement a group of radio buttons, you create an object of type CheckboxGroup for every group you require. A CheckboxGroup has no constructor argument. Its purpose is to group together a number of checkboxes to provide the functionality of radio buttons. Radio buttons are not the best component to use if you are dealing with a lot of possible options. For instance, a word-processing application interface would become cluttered if you had to use a radio button for every font you could use. A more suitable alternative is to use drop-down lists. When the user accesses a drop-down list, alternative choices are displayed. As with radio buttons, the user is forced to select only one element from a group of possibilities. Customizing your interface using drop-down lists rather than radio buttons can help you maintain consistency more easily. This is because your interface changes every time you add another option with a new radio button, but no matter how many new options you add to a drop-down list you still need only one list. Drop-down lists are implemented using the Choice class. The addItem method is used to add choice items.

Note java.awt has no combo box facility to combine a text box and a choice list. Combo boxes allow the user to pick one of the choices or type in an option that is not listed.

Page 159

Go To INDEX

The List component is similar to a drop-down list with multiple options visible at once. For this reason it is suitable for cases with only a few options. Otherwise it would occupy too much space on your interface. The advantage of Lists is that the user can quickly and easily see what choices are available. You can allow the user to make multiple selections in a List component but not in a Choice component. The target operating system may have a keyboard shortcut, which allows multiple selections to be made. For instance, under Windows 95 holding the Ctrl key while clicking items will allow more than one to be selected at a time. The constructor for a List has: A parameter that takes then umber of items you want to display at one time A boolean flag to indicate whether or not you want to allow multiple selection

You initialize a List just as you would a Choice component. Java's AWT also allows your program to receive text that the user inputs. The TextField component is a one-line area that allows the user to enter and edit text. TextField is inherited from TextComponent, which lets your program: Set the text to display Select a portion of the text Get the selected text as a String Set whether the TextField is editable or not The easiest way to create a TextField is by calling the constructor with no arguments. public TextField(); The constructor with no arguments will create an empty TextField with a default width. You can add text to a window by putting it in a Panel.

You can initialize a TextField by passing a string argument to the constructor. The second parameter of the constructor determines the width of the text field. Note that this number is not an upper limit on the amount of characters that can be entered. Instead it represents the width of the text field, or the maximum number of characters, that will be shown on the screen at one time. The user will need to scroll to see the rest of the text. Whether or not the text can be edited is controlled by passing a true or false value to the setEditable method. You can also use the setText method from java.awt.TextComponent to change the contents of a text field.

Page 160

Go To INDEX

You can use another component to display more than one line of text at a time. This is the TextArea component. It shares many common methods with TextField because both are derived from a common class called TextComponent. A TextArea object is like a TextField except that it can have multiple lines and has significantly more functionality. You can create an empty TextArea with a default height and width by calling the constructor with no arguments. When specifying the dimensions of a TextArea, you must give both height and width arguments to the constructor. For instance, this code creates a TextArea called "A text area" with 10 lines, each 30 characters wide. UsersText = new TextArea(“A Text Area”, 10, 30); Again the user is not restricted by the size you set. The text will scroll if the user enters more text than the screen text area can display. The TextArea and TextField classes have methods, which allow your program to get the text, which the user has selected. The TextArea class has an associated method called append(). Append(string) It takes a string and appends it to the end of whatever text is in the TextArea. A similar method is insert(). insert(string, int) It takes as parameters a string as well as an integer, and is used to insert the specified text (String) at the specified position (int). The AWT also provides a very useful scrollbar facility. The Scrollbar class provides a basic interface for scrolling that can be used in a variety of situations. You can create a simple vertical scrollbar with an empty constructor. public Scrollbar() You can also specify whether your scrollbar is to be horizontal or vertical. Scrollbar someScrollbar = new Scrollbar(Scrollbar.HORIZONTAL,75,10,0,100); Four other arguments can be passed in the declaration of a new scrollbar: The initial scroll position The size of the slider The minimum value of the scrollbar position The maximum value of the scrollbar position Moving a scrollbar can update the value of the scroll position in three ways: Line by line Page by page

Page 161

Go To INDEX

An absolute value If the user clicks the arrows of the scrollbar Java updates the display value in increments or decrements of a number of lines at a time. Java updates page by page when the user clicks on the space between the arrows and the slider part of the scrollbar. An absolute update occurs when the user drags the slider up or down the scrollbar. You have no control over how the position value changes for an absolute update, except that you are able to control the minimum and maximum values. Many scrollbar features are implemented in the AWT: You can set the line increment value which is added to or subtracted from the scrollbar position public void setLineIncrement(int increment); You can query the current line increment public int getLineIncrement(); You can set the page increment value which is added to or subtracted from the scrollbar position public void setPageIncrement(int increment); You can also query the page increment public int getPageIncrement(); The AWT provides other useful scrollbar features: You can find out the scrollbar's minimum and maximum position values public int getMinimum() --public int getMaximum() You can set the scrollbar's current position public void setValue(int value) You can query the current position Public void getValue() Menus are a fundamental part of any GUI. Java's AWT supports pull-down menus. At the top of a window, you can place a menu bar, which contains the names of the pulldown menus. As with other GUI menus, each Java pull-down menu can contain a number of options that the user can choose from. Creating a menu bar is quite straightforward. MenuBar someMenuBar = new MenuBar(); Once you have created a menu bar, you can add it to a Frame by using the setMenuBar method. someFrame.setMenuBar(someMenuBar); You then create a menu object for each menu. Menu fileMenu = new Menu(“File”);

Page 162

Go To INDEX

You can also put separators between the items in the menu to group related actions together. fileMenu.addSeparator(); Adding an item to the menu is also straightforward. fileMenu.add(new MenuItem(“Open”)); You can now add the menus themselves to the menu bar. someMenuBar.add(fileMenu); You can add an instance of a menuItem class to a menu. MenuItem saveMenuItem = new MenuItem(“Save”); fileMenu.add(saveMenuItem); You can enable and disable menu items by using enable() and disable(). When you disable a menu item, it still appears on the menu, but it usually appears in gray and cannot be selected by the user. You can also create special checkbox menu items. These items can be toggled between checked and unchecked. A string parameter containing the name shown in the menus is used in creating a checkbox menu item, just like a regular menu item. public CheckboxMenuItem(string ItemLabel); The getState method returns true if a checkbox menu item is selected. public boolean getState(); You can set the current state of a checkbox menu item with setState(). public void setState(boolean newState);

6.2. Event Handling • The Delegation Event Model

The delegation event model was introduced in JDK 1.1. It supercedes the JDK 1.0 and JDK 1.0.2 inheritance event model, although backward binary compatibility is maintained. If you use methods from the old event model, the compiler produces warnings telling you that they are not approved. In the inheritance model, you subclass GUI components and override their handleEvent() method. You can also override more specific event handling methods in some components, such as action() or mouseDown(). These methods return a boolean value to represent the status of the event. If boolean true is returned by the method, the event has been declared "handled", while if boolean false is returned, then the event is propagated up the GUI component hierarchy to the containing components.

Page 163

Go To INDEX

You can decide to handle all events in one place, using the handleEvent() method, and use complex if/else or switch statements to process them. Or you can subclass each particular component to handle its own events.

For example, the FocusButton subclass switches its foreground color when it gains and loses the focus, from DEFAULTCOLOR to FOCUSCOLOR and back again.

The gotFocus() and lostFocus() methods are overridden event handling methods of the Component class. The inheritance model suffers from many defects: The requirement to subclass components is unwieldy There is no clear separation between the user interface and the application It is too complex and error-prone Events are delivered to components even if they are not handled, making it inefficient The delegation-based event model aims to solve some of the problems in the original inheritance event model. In this model, all events are objects - instances of predefined event classes. Each type of event is represented by the combination of an event class and an event id.

Page 164

Go To INDEX

You can even create events of your own by subclassing these event classes. Event sources "fire", or originate, events. In the AWT, event sources are typically GUI components. They define the events they fire by registering "listeners" for those event types. The API provides predefined listener interfaces. One listener interface is defined for each set of event types that a source can fire. These listener interfaces declare suitable methods for handling events fired from event sources. Listeners are implementations of one or more listener interfaces. They can include: Classes that directly implement listener interfaces Subclasses of abstract classes that implement listeners (adapters) Extended GUI components Adapters are abstract classes, each of which implements a listener interface. You can subclass adapters to selectively override event handling methods. This means that you need only override the event handling methods for the events you are interested in and can ignore the others. Listeners have to be "registered" to receive method invocations from event sources when events occur. Each component has a set of methods available for registering listeners for each event type, of the form: component.addeventlistener(theListener);

When listeners are registered, the Java run-time system automatically invokes the correct method in the listener in response to events. Event sources can "fire" events to a single listener or to multiple listeners. However, the Java run time system does not deliver the events to the listeners in any predetermined order. To achieve ordered delivery, you must broadcast to a single listener that can then deliver the event to various other listeners in an ordered fashion. There is a special class, called AWTEventMulticaster, that allows you to efficiently chain multiple listeners together. Components can handle their own events. You extend components to override the default methods defined for handling events. Subclassed components can override selected event handling methods and ignore the others. The delegation-based event model allows you to clearly separate the user interface from the application code. You can redevelop your GUI without altering your application code in any way, and vice versa. You will find debugging much easier when the interface and application are separated, which is particularily important in large applications. Events are filtered by the system, meaning that unwanted events can be prevented from reaching your event handling code. This makes the model more efficient than the earlier one, which is particularly important for frequent events, like mouse moves. The delegation event model is equivalent to the event model used in the JavaBeans API. Events can be introspected during design time, meaning that visual builder tools can manipulate the events generated by components during design time. Once you grasp the general principles involved in the AWT event model, you should be able to understand the JavaBeans event handling model.

Event Objects
Go To INDEX

Page 165

All events are instances of event classes in the event class hierarchy. There are no public fields, or variables, for these events, but each event class defines all the methods necessary for accessing information describing the event. All events belong to the class hierarchy starting at java.util.EventObject.

This class has only two methods - getSource() and toString(). The getSource() method returns the Object that originated the event. The toString() method returns a string value representing the event. The root event class for all AWT events is AWTEvent, a subclass of EventObject. The getID() method in AWTEvent returns the event identified as an int. All event types are represented as predefined constants in the different event classes to which they belong. There are five subclasses of AWTEvent: ItemEvent AdjustmentEvent ComponentEvent ActionEvent TextEvent Semantic events are higher-level events, which are common across a series of visual components. ItemEvent, AdjustmentEvent, ActionEvent, and TextEvent are all semantic event types. ItemEvents occur in components that have implemented the ItemSelectable interface, which includes lists, checkboxes, and popup choice menus. Events are generated when items within the component are selected or deselected by the user. There are methods in the ItemEvent class for discovering which items are selected or deselected. AdjustmentEvents occur in components, like scrollbars, that have numeric

Page 166

Go To INDEX

values that can be incremented or decremented by the user. As a scrollbar is moved, its new position is notified to an adjustment listener as an adjustment event. There are methods in the AdjustmentEvent class for finding the type of adjustment event that occurred and the extent of that adjustment. TextEvents occur when text is entered, deleted, or edited inside text entry fields. ActionEvents are semantic events that include such actions as clicking a button or pressing a function key. Several different user inputs may result in the same action event being generated. Allowing for generic action events means that logically related, higher-level events can be grouped together for handling in one place. Low-level events occur as specific input, or as a windowing-event, to a particular visual component on screen. ComponentEvent and all its subclasses are counted as lowlevel events. The ComponentEvent subclasses are: InputEvent WindowEvent PaintEvent FocusEvent ContainerEvent InputEvent is the root event class for all component-level input events. InputEvent has two subclasses: MouseEvent KeyEvent There are times when you may need to consume an event to prevent it being processed in the default manner by the originating component. For example, visual builder tools may want to intercept events generated by components during design time. However, only input events for extended components can be consumed. The consume() method in java.awt.event.InputEvent is called to prevent an input event being processed in the default manner. All registered listeners will still receive the event for processing in the normal manner, even if another listener has consumed the event. The normal, and easiest, way to handle mouse events in a listener is to implement the MouseListener interface. The interface defines one method for each mouse event type, such as mouseReleased() and mouseClicked(). The relevant method matching the mouse event type is then automatically invoked by the system. The MouseEvent represents each of the following mouse events using int constants: Clicked Dragged Entered Exited Moved

Page 167

Go To INDEX

Pressed Released You can obtain the id of the mouse event using the getID() method defined in AWTEvent, and can then compare it to each constant in turn to see which subtype the event belongs to. There are three types of key event defined, listed below, and each has a corresponding method and constant: Key typed Key pressed Key released A key typed event is just a combination of a key press and a key release. Key events include a method called getKeyChar(), which returns the char representing the key pressed. For special characters, like function keys, constants representing each one are defined in the class. The window event types are: Activated Deactivated Closing Opened Closed Iconified Deiconified An opened event occurs once, when a window is first opened. A closing event occurs when a user selects an option to quit, while a closed event occurs after the window has been actually closed. The iconified and deiconified events occur when a window is reduced to, or expanded from, an icon on the desktop. PaintEvents are special system events that aid the serialization of repaint/update events for components. You should not use PaintEvents, or expect to handle them, in the delegation event model. The two focus events are focus gained and focus lost. Permanent focus is gained through a successful requestFocus() method call, a user mouse click, or tab. Temporary focus on a component is gained as a side-effect of another operation, like a window deactivation or scroll-bar drag. Permanent and temporary focus events are distinguished using the isTemporary() method. This hierarchy gives you great flexibility when handling events. It allows you to choose at what level of abstraction you want to handle an event. A key event can be handled as a key event, a component event, or as a semantic action event. ContainerEvents occur when components are added to, or removed from, a container. The registered container listener receives notification and can get the identity of the new component using a getChild() method call. This allows containers to smoothly add or remove input event listeners to their components as each one is added or removed. You are free to add extra event types by extending any of the classes in the EventObject hierarchy. You will, however, not normally need to extend event classes in this way.

Sources, listeners, and adapters
Go To INDEX

Page 168

AWT events almost always originate from GUI components, in response to mouse actions, key strokes, or other user activity. In the standard model, the system filters each event by checking to see whether there is a listener registered to receive events of that type for the component in which the event occurs. If the event is registered, it is sent to the processEvent() method of the component firing the event. The processEvent() method then selects the relevant method for handling that type of event. Then the event is passed to the registered listener method that corresponds to that event type. If you extend a component, you can override the processEvent() method. If no listeners are registered, then the system will filter out all events, so you must explicitly enable the events you wish to handle. You do this by calling the enableEvents() method in Component with a relevant event mask. Event masks for each type of event are long integer constants defined in the AWTEvent class. You only need to explicitly enable events if you are not registering listeners for those event types, since they will be enabled automatically by the system when you register them. Here is an example of an extended button component, called ActButton, with its constructor.

Note how this method call enables mouse events for this new extended component. Listeners are the objects that handle events fired from sources.

They are all implementations of the EventListener interface or its interface subclasses. An easy way to know what listener interfaces there are is to remember that there is one defined for each type of event in the event hierarchy. There are eleven types of listener interface defined. They include: ComponentListener ActionListener
Page 169 Go To INDEX

AdjustmentListener FocusListener KeyListener Other types of listener interface are: MouseListener WindowListener MouseMotionListener ItemListener TextListener ContainerListener To use a listener interface you must: Implement a listener interface Override the relevant methods Code the event type(s) you want to handle

The listeners represent each event type they handle with a separate method. You must provide empty methods for the events you wish to ignore. AWT event sources are usually on-screen GUI components. The types of events an event source supports are defined by the kind of component it is and by its registered listeners. To handle an event you must register a listener for that particular GUI component. The registration method calls are all of the form: component.addEventTypeListener(eventListener) For the Component object the following listener registration methods are defined: addComponentListener

Page 170

Go To INDEX

addFocusListener addKeyListener addMouseListener addMouseMotionListener Each of these methods takes a listener class or subclass as an argument. Each low-level listener interface has a corresponding adapter class that implements it.

These adapter classes can be extended quite readily to shortcut the requirement of having to override all the interface's methods. They are there, in effect, as a convenience to Java programmers. This example shows how to extend the MouseAdapter to handle a subset of the possible mouse events.

Moving the mouse over a component causes the foreground color to switch from black to red and back again when the mouse exits. This handler implements mouseEntered and mouseExited only, and ignores the other mouse event handling methods like mouseClicked.

Page 171

Go To INDEX

Event handling examples

Since an applet is also a component, it is possible to extend an applet to handle its own events. The first example you will see here shows how to write a simple event handling applet. This applet consists of a single button, labelled "Switch colors", and a black background.

You click the button to switch background color from blue to black and back again. The applet itself, as you will see, implements the mouse listener itself. Here is the applet code.

You must first remember to import the java.awt.event package, along with any other relevant packages. The applet, Events0, implements the mouse listener interface itself. These lines define the button and the background color variable. You define and instantiate the button and then add it to the applet's display in the normal way. You then register a mouse listener for the button switchColors. The method addMouseListener is the method used to register mouse listeners to GUI components. And you pass the method a reference to the applet, meaning that the applet is registered as being the mouse listener for this component. The applet will now receive all mouse events from the button. The mouseClicked method is an implementation of part of the mouse listener interface.

Page 172

Go To INDEX

The method simply tests bkgroundColor and switches between black and blue colors. Because you are implementing the MouseListener interface, you have to provide empty methods for all other mouse event methods defined in that interface, even if you are not interested in handling them. This simple example illustrates event handling alongside application code. One advantage of the new delegation event model in JDK 1.1 is that it allows you to easily separate the GUI interface from the application code. The next example illustrates how to create a mouse listener, which separates the event handling from the applet. This applet consists of a text field and three buttons called search, reset, and exit.

The user enters some text in the text field, and can then search a database to produce a listing (not shown). The Reset button will clear the screen and reset the focus to the beginning of the text field. The Exit button stops execution of the applet. Additionally, when you pass the mouse pointer over any button its foreground text turns to red, and when the pointer exits it switches back to black again. HandleMouseEvents is the class, defined later, that handles mouse events on buttons. The buttonHandler instance of HandleMouseEvents is declared and instantiated here.

To set up the Search button, you must: Declare and instantiate the button Give the button a name (optional) Add the button to the applet Register a mouse listener, buttonHandler in this case, for the button

You follow a similar procedure for the Reset and Exit buttons.
Page 173 Go To INDEX

In this example, each button has buttonHandler registered to listen to button events, but you could have chosen to register another listener with different functionality. HandleMouseEvents is an extension of MouseAdapter, making it convenient for you to only override those methods you are interested in.

The source object, a Component, will refer to the source of the mouse events. And parent is the applet on which the event occurred. This method responds to mouse clicks on one of the buttons.

You call the getComponent() method, defined in the ComponentEvent class, to retrieve the source of the event. Then you retrieve the applet in which the source button was placed. Here you check the source's name to discover which button was clicked. This is not the only way of handling multiple buttons on a single applet. You could, for example, have registered a different instance of HandleMouseEvents for each button, and

Page 174

Go To INDEX

instantiated them using a constructor that specified what type of button it was. This would make HandleMouseEvents capable of handling many more different types of buttons. This example illustrates how you can extend a component to handle events occurring on it, in this case buttons.

Here you add three ActButtons, the extended button component, to the applet. Notice that you do not have to register listeners on these extended components, since they handle their own events. You extend a button, and there is no need to implement any of the listener interfaces.

This is the constructor for ActButton, in which you: Call the super classes constructor in the first line Enable mouse events Set the button's name (optional)
Page 175 Go To INDEX

You must override the process MouseEvent method in the extended component. All mouse events have been enabled in the component's constructor, so you distinguish between different types of mouse event by getting the event's id with getID(). Most importantly, you must remember to call the super classes process event method.

6.3. Useful AWT Utilities • Simple Graphics

Java's Abstract Window Toolkit provides capabilities for drawing on the screen. These capabilities are provided by a number of classes in the java.awt class hierarchy. The java.awt classes, derived directly from the Object class, include the following: Color Font Component Polygon Graphics The methods for drawing simple graphical items such as lines, ovals, and rectangles are contained in the Graphics class. The Graphics class is an abstract class that models a graphics context. A graphics context is an abstract representation of the surface that is being drawn on. Using a graphics context makes life easier for the programmer by hiding the physical details of the implementation. This means that the graphics routines used for drawing on screen can also be used for printer output. The abstract nature of the Graphics class also supports one of Java's key features - platform independence. Each platform that supports Java implements graphics routines in different ways. In each case a derived class of Graphics implements the low-level details of the drawing process. These details are hidden from the programmer who simply uses Java's Graphics class. Graphical output can be displayed in a number of ways: In an applet In an application's frame or dialog box On a canvas
Page 176 Go To INDEX

All these objects are derived from the Component class. These objects inherit the Component class methods used to create graphical output. These include the paint method and the repaint method. public void paint(Graphics g); public void repaint(); The paint method takes a Graphics object as an argument, and renders the output on the appropriate component. The repaint method - which normally takes no argument repaints the component. It will result in a call to the update method as soon as it is possible. public void update(Graphics g); Java uses an x-y coordinate system to position objects on the screen. The origin that is the (0, 0) coordinate - is located at the top left corner of the screen. Positive x values are drawn to the right of the origin, and positive y values are drawn below it. The values are measured in pixels, which are the smallest elements that can be displayed on screen. To draw a line in a Java program you use the Graphics object's drawLine method. public void drawLine(int startX, int startY, int endX, int endY); This method takes four int parameters: startX, startY, endX, and endY. The first pair is the coordinates of the line's starting point and the second pair is the coordinates of the finishing point. Let's use the drawLine method to draw some lines with an applet.

This code draws three lines - two are connected because the finishing point of the first line coincides with the starting point of the second. To draw a rectangle in Java you use the Graphics object's drawRect method. public void drawRect(int top, int left, int width, int height); The first two parameters specify the location of the top left corner of the rectangle. The third and fourth parameters specify the width and height respectively. You can use the fillRect method to draw a filled rectangle. public void fillRect(int top, int left, int width, int height);
Page 177 Go To INDEX

It takes the same parameters as drawRect, but it fills the rectangle with the current drawing color. If you want to erase a previously drawn rectangle you can use the clearRect method. public void clearRect(int top, int left, int width, int height); Again this takes the parameters of top and left coordinates, width, and height. However it draws a rectangle in the background color of the current drawing surface. As well as drawing regular rectangles, you can use the drawRoundRect method to draw rounded rectangles. public void drawRoundRect(int top, int left, int width, int height, int arcWidth, int arcHeight); The first four parameters specify the position and size of the bounding rectangle. This is the area inside which the rounded rectangle is drawn. The arcWidth parameter specifies how much of the horizontal component of the rectangle will be rounded. Likewise arcHeight specifies how much of the vertical component is rounded. A fillRoundRect method is also available, with the same parameters as drawRoundRect. public void fillRoundRect(int top, int left, int width, int height, int arcWidth, int arcHeight); Drawing oval shapes in Java is just as straightforward as drawing rectangles. public abstract void drawOval(int x, int y, int width, int height); The drawOval method has four parameters - x, y, width, and height. The first two parameters specify the top left coordinate of the bounding rectangle that encloses the oval. If width and height have the same value, a circle is produced. The fillOval method takes the same parameters as drawOval. public abstract void fillOval(int x, int y, int width, int height); The Graphics class uses the drawArc method for drawing an arc, which is a portion of a circle or oval. public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle); The drawArc method is similar to the drawOval method, with its first four parameters specifying the location and size of a bounding rectangle. The startAngle parameter specifies the point on the oval where the arc begins - zero degrees is the 3 o'clock position. The arcAngle parameter then gives the angular extent of the arc. The arc is drawn counterclockwise if arcAngle is positive, while negative angles are drawn in a clockwise direction. The fillArc method takes the same parameters as the drawArc method, but fills the space swept by the arc with the current drawing color. public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle); A polygon is a shape with three or more straight sides. The Graphics class provides methods for creating polygons. Some of these methods take, as parameters, the coordinates that define a number of connected lines. But others take a polygon object as a

Page 178

Go To INDEX

parameter - such an object is created by instantiating a Polygon class. The drawPolygon method draws lines between a number of points. public abstract void drawPolygon(int xPoints[ ], int yPoints[ ], int numberOfPoints); The x-coordinates of these points are specified in the xPoints array, and the ycoordinates in the yPoints array. The number of points is specified in the numberOfPoints parameter. If the number of points specified in the numberOfPoints parameter doesn't match the number of array elements, an error will be generated. If the first coordinate is different from the last, the polygon is automatically closed by drawing a line connecting those two points. The fillPolygon method takes the same parameters as drawPolygon. public abstract numberOfPoints); void fillPolygon(int xPoints[ ], int yPoints[ ], int

Let's use these methods to create two polygons. First the x and y coordinates of the two polygons are assigned to the relevant arrays. Then the drawPolygon command creates the first polygon. And the fillPolygon command creates the second polygon, which is filled with the current drawing color. The drawPolygon and fillPolygon methods can accept a Polygon object as a parameter.

A Polygon object is created with the Polygon constructor. public Polygon(int xPoints[ ], int yPoints[ ], int numberOfPoints); This takes similar parameters to the drawPolygon method. However you can construct a Polygon object that does not contain any points.
Page 179 Go To INDEX

public Polygon( );

Containers and layout managers

Graphical user interfaces can have a wide variety of components. Text boxes, list boxes, buttons, and labels are just some of the components that can be found in a typical GUI. The Java AWT provides classes for most common GUI elements. It also provides classes for organizing these elements into an effective user interface. The Container class is the superclass for these organizing classes. It is an abstract class, so it cannot itself be instantiated. But two of its derived classes, Window and Panel, play vital roles in constructing a graphical interface. The Window class provides a top-level window without borders or menu bar. Window is not often used directly - usually its subclasses Frame and Dialog are used. The Frame class provides a window with the following features: Borders Resizable corners Menu bar Title bar Icon The Dialog class provides a dialog box window, which is generally used for two purposes: Eliciting information from the user Giving warnings or notices to the user It is often used in a modal fashion, which means the user must dismiss it before interacting with any other part of the interface. The Panel class provides a container that occupies part of an existing window. It is useful for grouping related components - this enables you to subdivide the interface into discrete areas. The Applet class is a subclass of Panel. This is why applets are displayed in a borderless area inside a Web browser's window. Java provides a number of classes to organize the user interface elements in a logical and user-friendly way. When writing GUI programs in other languages, it is common practice to specify the positions of user interface elements in terms of screen coordinates. These coordinates are usually measured in pixels. Because Java is designed to run on multiple computer platforms, such an approach does not always guarantee a good result. This is because different windowing systems will have different fonts, font metrics, and component sizes. Java's AWT provides a way that helps you create a screen layout that will be consistent across different platforms. Every container object implements a LayoutManager interface. This provides a layout manager that rearranges components within a container whenever it is resized. Java provides the following LayoutManager classes: FlowLayout GridLayout GridBagLayout
Page 180 Go To INDEX

BorderLayout CardLayout Each container or panel can have its own layout manager. It is passed as an argument to the setLayout method, usually during a program's initialization.

The FlowLayout class provides the simplest means of laying out components. Components are added to the panel one at a time, building up a row from left to right. When a component won't fit at the end of a row, it's wrapped to the start of a new row. The default alignment of the FlowLayout class is centered. However you can change this to a left or right alignment by passing the constant FlowLayout.LEFT to the FlowLayout constructor. setLayout(new FlowLayout(FlowLayout.LEFT)); FlowLayout creates a default gap of five pixels between components. You can change both the horizontal and vertical gap between components by passing the appropriate arguments to the FlowLayout constructor. The following line of code specifies a horizontal gap of ten pixels and a vertical gap of four pixels: setLayout(new FlowLayout(FlowLayout.CENTER, 10, 4)); The GridLayout class gives you more control than FlowLayout.

Page 181

Go To INDEX

You can specify the number of rows and columns in a rectangular grid layout. In this example, the buttons are added in sequence - the first three buttons fill the first row. Then Button4 and Button5 are added to the second row. By default there is no gap between components arranged with the GridLayout class. However you can specify horizontal and vertical gaps by passing suitable arguments: p1.setLayout(new GridLayout(3, 4, 12, 14)); This code creates a grid that can hold up to 12 components in three rows and four columns. There is a horizontal gap of 12 pixels between components, and a vertical gap of 14 pixels. The GridLayout class is useful when you need to lay out a number of components of similar size. However when the components are of different sizes, GridLayout may not give you sufficient control over the end result. In this case the GridBagLayout class may be more appropriate. GridBagLayout functions are like GridLayout in using a grid of rows and columns. These rows and columns do not need to have the same width and height. GridBagLayout places the components into the grid's cells without any overlap. Each component has a GridBagConstraints object that specifies how it should be displayed. The GridBagConstraints class has a number of instance variables such as anchor, fill, gridheight, gridwidth, and insets. It also has a number of static variables such as NORTH, EAST, CENTER, and VERTICAL. GridBagLayout uses this GridBagConstraints information to lay out each component with great precision. The BorderLayout layout manager can arrange up to five components in a container.

Page 182

Go To INDEX

The position of the components is specified by one of the following strings passed to the add method: North South East West Center You must start these strings with an upper-case letter. Within the confines of the container, the North, South, East, and West components get laid out according to their preferred sizes. The Center component then gets any space left over. The CardLayout class manages a set of components in such a fashion that only one is displayed at any given time.

These components are held in a parent container. You can use methods such as first, last, next, and previous to select the component to be displayed.

These components can, in turn, be containers that hold other components arranged with other layout managers. This scheme would be ideal for creating a tabbed dialog box or set of property sheets.

Page 183

Go To INDEX

Fonts and metrics

The ability to use different fonts to display text is an important part of a graphical user interface. Java provides a number of methods and constants for controlling the display of fonts. Most of these methods and constants belong to the Font class. Java maintains platform-independence by defining the following set of standard fonts: Helvetica TimesRoman Courier Dialog DialogInput In practice these fonts are mapped to the available platform-specific fonts. For example, on the Windows platform the Helvetica font is displayed as Arial. The constructor for the Font class has three parameters: Font name Font style Font point size public font (string name, int style, int size); The font name is one of the supported fonts, for example TimesRoman. The font style is one of the static constants of the Font class, which are Font.PLAIN, Font.ITALIC, and Font.BOLD. If a style is not specified, the PLAIN style is displayed. The font size is specified in points - a point is 1/72 of an inch. If a font size is not specified, the default size is 12 points. The setFont method is used to set the current font. public void setFont(Font f); This method takes a Font object as its argument, and is a member of the Graphics class. Once this method is invoked, all the following text will be displayed in the new font. The Font class has a number of methods that can be used to retrieve information about the current font. The getName method returns the font's name as a string. public string getName() The getFamily method returns the font's family name as a string. The name of the font family is platform-specific. public string getFamily() The getStyle method returns an integer value indicating whether the font is plain, bold, or bold italic. public int getStyle() You can also use a method to determine if a particular element of a font's style is being used. For example, the isBold method returns a boolean value indicating whether the text is bold.
Page 184 Go To INDEX

public boolean isBold() public boolean isItalic() public boolean isPlain() The Font class's getSize method returns the font's size in points. public int getSize() However it is sometimes necessary to have more detailed information on the size characteristics of a font. For example, if you were writing a page layout program in Java it would be necessary to have such detailed information. Java has a class called FontMetrics, which provides many methods for handling such information. Let's look at the detailed characteristics of a font. All letters stand on the baseline. The starting position of a string's baseline is specified by the x and y parameters of the drawString method. The space taken up by the descenders of lower-case characters is called the descent. The distance from the baseline to the top of the highest upper-case character is called the ascent. A further space, called the leading, provides the separation between successive lines. The height of a font is the sum of the descent, the ascent, and the leading. This is the same as the distance between the baselines of adjacent lines of text. The following FontMetrics methods all return their respective values in points: getAscent getDescent getLeading getHeight public int getAscent() public int getDescent() public int getLeading() public int getHeight() These methods can be used in conjunction with the getFontMetrics method of the Graphics class. For example, to get the ascent of the current font of Graphics object g, you would use the following code: int gAscent = g.getFontMetrics().getAscent(); Two FontMetrics methods that are useful for aligning text are stringWidth and charsWidth. The stringWidth method takes a string parameter and returns the advance width required to display that string in the current font. public int stringWidth(string str) The advance width of a character is the amount by which the current point is moved from one character to the next in a line of text. In the context of this function, it means the width of the string when displayed using the current font. The charsWidth method also returns the advance width required to display an array of characters.

Page 185

Go To INDEX

public int charsWidth(char data[ ], int off, int len) The first parameter is an array of characters. The second parameter is an offset to the first character in the array to be displayed. And the third parameter is the number of characters to be displayed.

Customizing colors

Color plays an important role in a good graphical user interface. The ability to display text, controls, and graphics in color enhances the user interface. When used effectively, color can make programs more intuitive and easier to use. Most computer systems use the RGB model to handle the on-screen display of colors. RGB stands for red, green, and blue, which are the three primary colors, which together produce white light. Any color can be modeled by specifying its RGB components - this is often called an RGB triplet. In many computer graphics systems, each RGB component is specified by a value between 0 and 255. So, pure red is specified by the RGB triplet (255, 0, 0), while pure blue is specified by (0, 0, 255). In the additive color model, white is represented by the presence of all primary colors - so its value is (255, 255, 255). And black is represented by the absence of all color - (0, 0, 0). This RGB color model gives a possible maximum of over 16.7 million colors - 256 x 256 x 256. Most computer graphics systems can't display this many colors, but they will display the nearest available color instead. The process of displaying an approximate color on a computer monitor is called dithering. Java supports the RGB color model with the Color class, and its associated methods and constants. A Color object represents an RGB color. It is created with the Color class constructor, which has three variants. You can specify the color as a set of three integers, each with a value between 0 and 255. public color(int r, int g, int b) Alternatively you can specify a set of three floating point values, each between 0.0 and 1.0. public color(float r, float g, float b) You can also specify the color with a single 32-bit integer value. public color(int rgb) A pure green Color object is created by calling the constructor Color (0, 255, 0). The same color is created by calling the constructor Color (0.0, 1.0, 0.0). Although Java uses the RGB color model, it provides support for the alternative HSB model. HSB stands for Hue, Saturation, and Brightness. Hue indicates the color shade - red, yellow, blue, and so on. Saturation indicates the intensity of the color. The Color class provides methods for converting between RGB values and HSB values. public static int HSBtoRGB(float, float, float) public static float[ ] RGBtoHSB(int, int, int, float[ ])

Page 186

Go To INDEX

A programmer can treat an RGB color as a set of three integer values, each between 0 and 255. However Java's internal representation of a color is a single integer of the form 0xAARRGGBB RR, GG, and BB stand for the red, green, and blue components, as you would expect. The AA stands for the alpha component, which represents the transparency of an image. When you create a Color object, AA is always set to 0xFF, which means fully opaque. The transparency component can be used in image manipulation. However it is not used in any of Java's drawing operations, and it can't be manipulated by any of the Color class methods. The Color class has a number of color constants.

These have the following form: public final static Color color The Color class has a number of methods for retrieving the characteristics of a Color object. For example, the getRed method returns an integer between 0 and 255 representing the red content of the color. public int getRed() public int getGreen() public int getBlue() And the getRGB method returns a 32-bit integer representing the color in the default RGB color model. public int getRGB() With the getRGB method, bits 24-31 - the alpha component - will contain 0xFF. Two important color-related methods belong to the Graphics class. The setColor method sets the current drawing color to the specified color. public void setColor(Color c) And the getColor method returns a Color object representing the current drawing color. public Color getColor() Let's use the setColor method to draw text in different colors in an applet. First you import the relevant classes - Applet, Graphics, and Color.

Page 187

Go To INDEX

The new class will be called Show3Colors. The work of the applet is done in the paint method, which receives the Graphics object g as a parameter. We want to change the current color from black - the default - to red. So we create a new Color object, passing it an RGB triplet of (255, 0, 0) to create a pure red color. The Color object is then passed to the setColor method, which changes the current drawing color of the Graphics object g. The line of text is then written with the drawString method, starting at x coordinate 50 and y coordinate 20. The next line of text will be drawn in green. Pure green is very vivid and difficult to read on a monitor, so we reduce the green component to 0.6. In this case floating point parameters are being used, rather than integers. The line of text is again drawn with drawString, this time with a y coordinate of 40. For the final line the current color is changed to blue. This time we pass the Color object's blue constant to setColor.

Using the clipboard for data transfer

Most graphical user interfaces allow you to copy objects to a system clipboard, and paste them into an application. Previous versions of Java did not have this copy and paste functionality. Java 1.1, however, has a clipboard API that enables these operations. The relevant classes are contained in the java.awt.datatransfer package. The java.awt.datatransfer package contains three new classes - Clipboard, DataFlavor, and StringSelection. It provides two interfaces called ClipBoardOwner and Transferable. And it has an exception called UnsupportedFlavorException. Let's create an applet that will demonstrate clipboard functionality. This program allows the user to type text into a text field. A Copy button causes the text to be copied to the clipboard. A Paste button transfers the text from the clipboard to an internal string and then paints it to the applet surface. First the necessary classes must be imported, including all those in the java.awt.datatransfer package. import java.applet.*; import java.awt.*; import java.awt.event.*; import java.awt.datatransfer.*; Then the applet declaration begins - it is called ClipboardApplet. public class ClipboardApplet extends Applet
Page 188 Go To INDEX

implements ClipboardOwner, ActionListner { TextField Button Button Clipboard String public void init () { clipBoard = getToolkit().getSystemClipboard(); sourceText = new TextField(20); add(sourceText); paintText = new String(“No Text yet !”); copyButton = new Button(“Copy”); copyButton.SetActionCommand(“Copy”); copyButton.addActionListener(this); copyButton = new Button(“Copy”); add(copyButton); It implements the ClipboardOwner interface, which defines the interface for classes that will provide data to the clipboard. Now we declare variables for the five objects that the applet will use, including the Clipboard object. The applet initialization is started by getting a handle for the system clipboard. The text field is created and a variable is initialized with the message "No text yet!". The next section of code creates the Copy button, assigning it the name copyButton. It sets the applet as an action listener. Then it adds the button to the applet. The code for the Paste button is similar, although it is initially disabled. sourceText; copyButton; pasteButton; clipboard; paintText;

The next section of code creates a method called actionPerformed.

This method handles events from the Copy and Paste buttons. If the button clicked was the Copy button, the text is transferred from the edit control to the clipboard.

Page 189

Go To INDEX

To achieve this we create a StringSelection object. StringSelection is a class which implements the capability required to transfer a simple Java string in plain text format. Because there is now data in the clipboard, the Paste button is enabled. If the Paste button was clicked we use the getContents method.

This method returns a transferable object representing the current contents of the clipboard. The StringSelection class has a method called getTransferData. This method is used to transfer the data into a string called clipData. The DataFlavor object represents the format of the data involved.

In Java 1.1 it has two static variables, stringFlavor and plainTextFlavor. In this example we are expecting stringFlavor, which represents a Java Unicode String class. There is a possibility that another program may have written to the clipboard. The clipboard can accept data in many different formats - it can store graphics files just as easily as text, for example. If the data is in any format other than stringFlavor an exception is thrown and an error message is printed. Finally the paint method draws the paint string on the applet's surface at the specified location.

6.4. Java applications • Comparing applets and applications

Java's initial impact has been in the development of applets. These are programs, which are downloaded from either the Internet or a corporate intranet, and execute within a browser. However Java can also be used to develop stand-alone applications. These applications reside on the local hard disk, and can execute without a browser. Applets and applications have many similarities. This is because both are developed using the Java API. So both applets and applications can benefit from Java's strengths, such as

Page 190

Go To INDEX

multithreading and platform-independence. The differences between applets and applications arise from the fact that they execute in different environments. All Java programs execute within a Java Virtual Machine (JVM). In the case of an applet, the JVM is implemented within the browser. With Java applications, however, the JVM is provided by the Java interpreter, which must be invoked each time the application runs. The fact that applets run within a browser has important implications for the user interface. The browser provides a ready-made interface that the user will normally be quite familiar with. For example, its File menu contains the Cut, Copy, and Paste menu items for clipboard operations. The features of the applet's interface should complement those provided by the browser itself. Because the browser provides ready-made interface features, the applet developer has less work to do. However the developer suffers the disadvantage of being more restricted in terms of interface design. Applets do not usually have menus, for example, because the browser already has them. So the developer must provide other means of user input, such as buttons and list boxes. The developer of a Java application has full control over the user interface. Menus and dialog boxes - features not normally found in applets - can be used in applications. This does mean more work for the developer, however. Even the simplest application has to be displayed inside a Frame object, which has to be explicitly created by the programmer. Some of the biggest differences between applets and applications are found in the area of security. Applets, like web pages, are usually downloaded from a remote computer. But unlike web pages, which are static documents, applets are programs. This means that if their source is not trusted, it is possible that they may contain malicious code. Java implements a multilayered security system to prevent malicious applets causing damage: Features of the Java language, such as its memory management, make it difficult to write malicious code The bytecode is verified when loaded The browser's security manager places severe restrictions on what the applet can do An applet is not normally allowed to perform certain actions that applications routinely perform. Applets are not allowed to write to or read from the local hard disk. They can't create directories on the local disk, either. And they are not allowed to check the details of existing files, such as file size and type. Another major restriction placed on applets is that they are not allowed to execute native methods. Dynamic Link Libraries (DLLs) are an example of a library containing native methods. Native methods are sections of code that are written in another programming language and compiled for a particular platform. For example, code could be written in C++ and compiled for the Solaris platform. The most common reason for calling a native method is the increase in performance. Using native code circumvents many of the security restrictions that are inherent in Java's design. For example, it is possible to write to specific memory locations. For this reason the applet's security manager does not permit the execution of native methods.

Page 191

Go To INDEX

However these methods can be called by Java applications. Another restriction normally placed on an applet is that it can only make a network connection to the computer from which it was downloaded. The restrictions on applets are imposed by the browser's security manager. They only apply to applets that have been downloaded from a remote server and loaded by the applet class loader. Certain browsers allow the user to configure the security restrictions on such applets. If an applet exists on the local disk in a directory specified in the CLASSPATH environment variable, it is loaded by the file system loader. These applets are not subject to the security restrictions imposed on applets downloaded from a remote server. Another area where applets can differ from applications is performance. When an applet is executing it requires a certain amount of memory. The requirement varies with the complexity of the applet, but is usually relatively small. An applet can only run in an external viewer such as a browser. The browser also requires memory - several megabytes in the case of the most recent browsers. So the applet inherits the memory overhead of the browser in order to execute. A Java application runs in conjunction with a Java interpreter. The interpreter's memory overhead is much smaller than a browser's. This means that an application's performance is often better than that of a corresponding applet. When deciding whether to develop a program as an application or as an applet, another important issue is distribution. There are several advantages to distributing applets over the Internet or a corporate intranet. The most important is that the most recent version of the applet is made available to the user. However not all users have access to the Internet or to an intranet. If a section of the target audience does not have such access, it may be necessary to develop the program as an application. A program that runs as an application always contains a method called main. This method acts as a starting point for the execution of the program by the Java interpreter. The main method is contained in a class that defines the application and is named after it. The format of the main method is rigidly defined, and it always contains certain elements.

The public and static keywords must be present. The return type is always void, unlike C and C++ programs, which usually return an int. The only parameter is an array of String objects. These strings correspond to command line arguments being passed to the application. Unlike C or C++ programs, the first element of the array args[0] does not contain the command name. If the application took three arguments, you would use the following syntax at the command prompt to start it: java MyApplication arg1 arg2 arg3 There is an important difference between applets and applications in how they handle initialization and starting. In the case of an applet, the browser automatically calls both the init method and the start method.

Page 192

Go To INDEX

These methods are not called by the Java interpreter in the case of an application, so they must be explicitly called in the main method. Another fundamental difference between applets and applications is their derivation.

Applets are always subclassed from java.applet.Applet. Applications are not derived from this class - if no superclass is explicitly extended, they are derived from Object.

Frames and dialogs

Creating a user interface for a Java application requires more effort than does an equivalent applet. This is because an applet runs within a browser, which provides a framework to hold its interface components. The browser also provides interface components of its own, such as menus, which can be used by the applet. Java applications need a framework to hold the user interface components. The java.awt package provides a Container class for holding components. Container is an abstract class that can't be instantiated, however. Container has a subclass called Window, but this class does not provide borders or a menu bar. These features are provided by the Frame class, which is a subclass of Window. In practice an application's main window is often provided by subclassing java.awt.Frame. The main method is then included in this subclass.

The Frame class provides many of the interface features that users expect to see in an application. It has a title bar, which can display a title specified by the setTitle method. public synchronized void setTitle(string title) A Frame object can have an associated icon to represent the frame when it's minimized. The icon is specified by the setIconImage method. public synchronized void setIconImage(Image image) Menus play an important role in most applications. The setMenuBar method is used to associate a particular MenuBar with a Frame. And menu components can be removed with Frame's remove method. public synchronized void remove(MenuComponent m) The MenuComponent class is the superclass of all menu-related components. The Frame object provides a wide variety of cursors, including the following: A default cursor A text cursor

Page 193

Go To INDEX

A wait cursor A crosshair cursor A hand cursor Eight resize cursors

The cursor for a Frame is specified with the setCursor method, and can be determined with the getCursor method.

These methods are from the Component class, and they use a Cursor object. Most applications make extensive use of dialog boxes. These are windows, which are generally borderless and smaller than the main window of the application. Dialog boxes have two main purposes: Giving information to the user, especially warnings Eliciting information from the user A dialog box can be either modal or modeless. A modal dialog box does not allow the user to interact with any of the application's other windows until it is dismissed. The user can, however, interact with other applications. Modeless dialog boxes do not impose such a restriction, but they are not as widely used. Java provides the Dialog class to implement dialog boxes. Dialog is a subclass of Window, so it is also a container. Components such as buttons and labels can be added to the Dialog object with the add method. The default LayoutManager used by Dialog is BorderLayout.
Page 194 Go To INDEX

Let's look at one of Dialog's constructors.

The first parameter refers to the parent window of the dialog box. The second parameter is a string that appears in the box's title bar. The third parameter determines whether the dialog should be modal or modeless. A true value indicates modal operation. Let's create a dialog box to warn the user that a file could not be found. We start by subclassing the Dialog class to create a class called FileWarning.

The current frame will be the parent window, so it is passed to FileWarning's constructor. This is then passed to the constructor of the superclass, which is Dialog. The second parameter - "Warning!" - appears in the box's title bar. The third parameter indicates that the dialog box is modal. Now we can create the components for the dialog box. These are simply a label containing the warning message, and an OK button to close the window. We will also use a panel to hold the button. This is because we don't want BorderLayout to stretch the button across the dialog box. Now we add the components, using the default layout manager, which is BorderLayout. Dialog boxes don't have a default size so we specify the dimensions with the resize method. Finally we use the show method to display the dialog box. Java provides a useful class called FileDialog for handling files. FileDialog is derived from Dialog, and can be used to create both "Open File" and "Save As" dialog boxes. The constructor takes an argument called mode, which specifies which type of dialog to create.

The value of mode is an integer specified by the constants FileDialog.LOAD and FileDialog.SAVE.

Page 195

Go To INDEX

You can specify a FilenameFilter object to control which files are displayed by FileDialog. FilenameFilter is an interface that is part of the java.io package. The appearance of the dialog box created by FileDialog depends on the windowing system being used. So an "Open File" dialog box on a Windows NT machine will look different from one on a machine using Solaris. However their functionality will be the same in both cases.

Menus

Menus play a vital role in most GUI applications. They provide a lot of functionality without taking up large amounts of screen space in the user interface. And, when well organized, menus are easier to use than a large number of components such as buttons and checkboxes. A menu consists of one or more menu items. When a menu is clicked, it expands to show a listing of its menu items. When a menu item is clicked, an event is generated that causes the application to perform some action. Menus are usually grouped together in a menu bar, often located at the top of an application's window. However another type of menu is sometimes used which can appear anywhere within the window. This is called a popup menu - it usually appears at the same location as the cursor. Java provides a number of related classes for implementing menus. In the class hierarchy MenuComponent is the superclass of all menu-related classes. MenuItem, which is derived from MenuComponent, has the methods required to manage menu items. In the Java 1.1 event delegation system, a MenuItem object acts as an event source. MenuItem has a method called addActionListener. This method registers a specified listener to receive action events from the menu item. MenuBar, which is also derived from MenuComponent, is the class that acts as a container for menus. The Menu class is derived from MenuItem - it defines a pulldown menu. This means that a menu can itself be a menu item, enabling the use of submenus. A popup menu is defined by the PopupMenu class, which is a subclass of Menu. PopupMenu is a feature of Java 1.1, and was not available in earlier releases. Another menu-related class is CheckboxMenuItem, which is derived from MenuItem. This class defines a particular type of menu item that can be toggled between two states - checked and unchecked. Let's create a menu system for a simple paint program. This system will have the following menus: A Color menu for selecting the paint color A Brush menu for selecting the brush type You first create the menu bar to hold the menus with the MenuBar constructor. Then you create the Color menu with the Menu constructor.

The String passed to the constructor is the menu name. Now you create menu items and add them to the menu.
Page 196 Go To INDEX

In this case, you use the MenuItem constructor to create menu item objects. Then they are added to the menu with the Menu method add. Alternatively you can add an item name as follows: colorMenu.add("Black"); In this case, the method accepts a String rather than a MenuItem object. The Brush menu is created in a similar fashion.

Now you can add the menus to the menu bar.

This is done with the MenuBar object's add method. The menu bar is now attached to the application's Frame with the setMenuBar method. The appearance of menus is often improved by adding a separator bar between groups of menu items. This is done by passing a hyphen to the MenuItem constructor.

Alternatively you can use the Menu class's addSeparator method. AddSeparator(); Separator bars do not generate any events when they are clicked. Some menu items can be toggled between two states. In your paint application, you may want to add an airbrush effect, which can be switched on or off. You use the CheckboxMenuItem class for this purpose. The state of the checkbox menu item can be either true or false. When it is true, the menu item will have a checkmark beside it. The state can be determined with CheckboxMenuItem's getState method. Submenus are a very useful way to enhance menu structure. Let's change the Brush menu to incorporate a submenu for brush width. First you create a new menu called Width. Then you add the three menu items - Thin, Medium, and Thick.

Page 197

Go To INDEX

Now when you create the Brush menu, you simply add the Width menu to it.

This makes Width the first menu item on the menu. When it's clicked, the Width menu opens.

Running an applet as an application

The principal difference between an applet and an application is the environment in which the program runs. A Java applet runs inside a Java-enabled browser or the applet viewer provided in the JDK. A Java application is run by the Java interpreter. Let's take a Java applet and enable it to work as a standalone application. It will still be able to function as an applet if loaded by a browser. The applet you will use is a very simple one.

All it does is print the following message to the screen: Runs as an applet! Java programs start by importing all necessary classes. You will need more AWT classes than simply Graphics, so you import all of its classes. The next line starts the declaration of the new class, which you call Application. Because you still want the program to function as an applet, it is derived from Applet. If you did not have this requirement you would extend it from another class - Frame would be the obvious choice here.

Now you declare a variable called StandAlone. StandAlone will be set to true when the program is running as an application. Now you come to a crucial part of this program. The main method acts as the applet's entry point when it is running as a standalone application. All applications must have a main method with this format. This method is ignored if the applet is running within a browser.

A graphical application has to be displayed inside some kind of framework. So you create a Frame object and give it the title Application.

Page 198

Go To INDEX

You want the display area of the program to be a certain size - 320 pixels horizontally and 240 pixels vertically.

Java programs can run on computers with different windowing systems. So you can not be certain of the size of borders and title bars at design time. You use the getInsets method to determine these values at run time. The frame is briefly made visible so that getInsets will return valid values. The next section of code starts the applet running within the frame window.

The StandAlone flag is set to true as the program is now running as an application. Immediately after the application is initialized the frame is made visible. The next section of code is the Application class constructor.

This is followed by code that initializes the size of the applet.

The final code section in our program is the paint handler. This writes a string to the screen with the drawString method.

Page 199

Go To INDEX

7 Features of Java
7.1. Java packages • String handling utilities
Java does not have a built-in string type. However, the java.lang standard library contains a predefined String class. In C, operations on strings occur on character arrays. But in Java they occur through class methods. In Java, String objects are constant so their values cannot be changed after they are created. Instead you apply methods to them in order to create new String objects. The String class is best used for string constants strings that are not going to change after they are created. Fixed, immutable strings can be implemented more efficiently than changeable strings. However, you are not wholly restricted to using fixed strings. The StringBuffer class is used for strings that need to be modified after they have been created. To create a string you can explicitly instantiate an object of the String class.

You can also declare a variable, which references a String object and then set its value later in another part of your program.

The declaration for an empty string is also straightforward. string a = “”; There are in fact nine constructors that you can use to explicitly create a String object. string () string (byte [ ]) string (byte [ ], ByteToCharConverter) string (byte [ ], int, int) string (byte [ ], int, int, ByteToCharConverter) string (char [ ]) string (char [ ], int, int) string (string) string (StringBuffer) In Java you can use the plus (+) sign to join two strings together. This operation is known as concatenation. In this example, the string variable fullname will contain "Cheryl
Page 200 Go To INDEX

Wong" but note that you need to have a space in one of the strings if you want the name stored in the correct format.

The + sign joins the two strings together exactly as they are given. The string fullname would then contain "Cheryl Wong". You can use the concatenation operator many times in a single line: String somestring = "This " + "is " + "a string"; You could also use the concat method. It concatenates the specified string to the end of another string and returns a new String object representing the concatenation. Another method that can be used to manipulate String objects is replace(). This method takes two characters and replaces all occurrences in a string of the first character with the second character. In this example, secondstring contains "PICK A OTRING, ANY OTRING", because the call to replace() requests that every occurrence of an "S" be replaced with an "O".

Note that firststring remains unchanged and that secondstring is a new String object. Another useful string handling utility is the ability to extract a substring from another string. To do this you make a call to the substring method of the String class. This let's you create a new string object out of a larger string.

In this example, the new string object 'a' equals "Chery". Java assigns position 0 to the first character in a string. The first argument of substring is the position in the string of the first letter that you want to be part of the new substring. The second argument is the stopping position. The substring returned will contain all the characters from the starting position up to but not including the stopping position. So in this example the characters starting with position 0 ("C") will be copied but the copying will stop when the character at position 5 ("l") is reached. You can also find out the length of a given string. You do this by calling the length method of the String class.

The method that Java uses to count the position of characters in a string makes it easier for you to find out how long a substring is. The string a.substring(x,y) always has yx characters. You can also find out whether a string starts with a certain prefix. Just call the startsWith method. Here, the boolean variable result is equal to true.

Page 201

Go To INDEX

This is because the string somestring does start with "This". A similar method is endsWith(). This method determines whether the string object ends with a given set of characters. In this example, result is equal to true.

If you want to find the location of the first occurrence of a character within a string, use the indexOf method. In this example, index is equal to 3, which is the index of the first "s" in the string.

To find the location of subsequent characters you can use a slightly different version of indexOf(). In the example you're looking for the next occurrence of "s". By including the index+1 as the method's second argument, you're telling Java to start searching at index 4 in the string (the old value of index, plus 1).

This results in index becoming equal to 6, which is the location of the second occurrence of "s" in the string. One of the most important aspects of dealing with strings is the ability to compare them. In Java you can use the equals method to test strings for equality. You should never use the == operator to test for equality between strings in Java. This is because Java does not always arrange for equal strings to be shared. In this case, equals will return true if the string called somestring is the same as the string called anotherstring.

You can use string variables or string constants as the arguments to equals.

The equalsIgnoreCase method compares two strings without regard for uppercase or lowercase letters. If you want to know more than just whether or not the strings are equal, you can call upon the compareTo method. The compareTo method returns a value: Less than zero when the String object on which the method is invoked is lessthan the string argument Zero when the strings are equal

Page 202

Go To INDEX

Greater than zero if the String object on which the method is invoked isgreater than the String argument The regionMatches method enables you to compare part of one string with part of another. The regionMatches method's four arguments are: Where to start looking in the String object on which the method is invoked The string to compare with The location in the comparison string at which to start looking The number of characters to compare In the example, the boolean result will be true.

The first letter to be compared in the main string is the character in position 10 which is S. The location in the second string is the letter in the second position which is S. The next six characters in each are compared and are equal - "S, T, R, I, N, G". Remember that String objects are immutable. That is, you cannot change the individual characters in the string. The string "Cheryl" will always contain the sequence of characters "C", "h", "e", "r", "y", "l" and they cannot be changed.

You could however change the contents of the string variable firstname by making it refer to a different string. You could modify the firstname string by taking the substring you want to keep and concatenating the characters you want to insert. This changes the value of the firstname variable to "Cherub". Note that the new string had the same number of characters as the original string had. You could however have changed the firstname variable to hold a string larger or smaller than the original one.

The firstname variable can just as easily be changed to contain the strings "Chernobyl" and "Cher". Because String objects are immutable, Java allows the compiler to arrange the sharing of strings. Strings sit on the memory heap and variables of type String reference locations on the heap. So in reality the substring firstname.substring (0, 4) is just a reference to the existing "Cheryl" string. The range of characters used in the substring are stored with the reference. You can use a method called trim() to remove both leading and trailing whitespace characters.

Page 203

Go To INDEX

Examples of whitespace characters are spaces, tabs, carriage returns, and linefeeds.

Math Methods

The java.lang standard class library also has a class, which provides math operations. This class is called java.lang.math. It provides many useful methods, from simple minimum and maximum to logarithm and trigonometry. Two of the most familiar math methods are those that return the minimum and maximum values of a group of numbers. There are four versions of min() and max() depending on the type of numbers you are using. A math method that operates on a number in Java might have to deal with each of Java's basic types - int, long, float, and double. Many of the methods in the Math class are overloaded to work on different types of numbers. The declarations for both the min and max methods are straightforward.

They take two parameters, which represent the numbers that you want to compare. In this case the method will find which of two integers, a and b, is the largest. The absolute value method abs() returns the absolute value of a number. The absolute value of a negative number is the corresponding positive number. The absolute value of a positive number is itself. So the absolute value of -7 is 7 and the absolute value of 7 is also 7. abs() -x = x x=x The declarations for abs() are similar for the four different types of numbers. Often you will want to generate random numbers in some part of your program. Java provides a facility for doing this. The random method of the Math class generates a random number in the range 0.0 to 1.0. You can generate a random number between 0 and 10 quite easily.

Page 204

Go To INDEX

Or you could generate a random number between 1 and 100.

Math also contains a number of methods for rounding numbers. For instance, the round method takes a float number and rounds it to an integer value. public static int round(float a) This code rounds to the closest whole number, which means that 9.4 gets rounded to 9, but 9.6 gets rounded to 10. You can also round a double to a long value. public static long round(double a) The rint method rounds to the closest whole number. public static double rint(double a) The ceil and floor methods are also particularly useful. If you want to find the next whole number greater than or equal to a double you use ceil(). public static double ceil(double a) The ceil method takes its name from ceiling. The floor method gives you the "floor" or largest whole number less than or equal to a double. Trigonometric methods are provided for by the methods sin(), cos(), and tan(). The logarithm of a double can be found by calling the log method. It takes a double argument and returns the natural logarithm (base e) of it. The pow method takes two doubles and returns the first number raised to the power of the second. The ability to get the square root of a number is very useful in programming. The sqrt method in the Math class provides this function. public static double sqrt(double a) Version 1.1 of the JDK contains the java.math package, which contains the classes BigIntegers and BigDecimals. BigIntegers are immutable arbitrary-precision integers, which provide analogs to all of Java's primitive integer operators, and all relevant static methods from "java.lang.math". BigIntegers provide other operations for modular arithmetic, primality testing, prime generation, and single-bit manipulation. BigDecimals are immutable, arbitrary-precision signed decimal numbers, suitable for monetary calculations. BigDecimals provide operations for basic arithmetic, comparison, scale manipulation, format conversion, and hashing.

Streams and I/O

At the most basic level all computer programs receive input and transform it to generate output. Streams are little more than the flows of data in this process. An input stream could contain data entered by a user through a keyboard and an output stream could be the flow of data to a printer. The Java class libraries provide you with a vast number of classes that give you the ability to read and write data in your programs.

Page 205

Go To INDEX

The System class of the java.lang package contains the standard input and output stream objects. You can use these in your programs without having to create your own stream objects. The system.in class variable enables your programs to read data from the keyboard and is a reference to an InputStream class. The system.out class variable is a reference to the PrintStream class and routes output to the computer's screen. You can use these stream objects directly in order to handle standard input and output in your Java programs. In Java, all streams are represented by classes. The simplest of these classes represent basic input and output streams that provide general streaming abilities. From the basic classes, Java derives other classes that are more specifically oriented toward a particular type of input or output. The java.io package provides classes for basic input and output, as well as file handling and inter-thread communications. All of Java's I/O is based on the classes InputStream or OutputStream. Because InputStream and OutputStream are abstract classes, you cannot use them directly. InputStream is an abstract class representing an input stream of bytes. OutputStream is an abstract class representing an output stream of bytes. InputStream defines nine methods that provide the basic functionality common to all input stream classes. These are: available() close() mark(int) markSupported() read() read(byte) read(byte,int,int) reset() skip(long) The available method returns the number of bytes that can be read without blocking. It also throws an IOException. public abstract int available () throws IOException The close method closes the input stream. public void close () throws IOException It must be called to release any resources associated with the stream. It throws an IOException. The mark method marks the current position in the input stream. public synchronized void mark (int readlimit) If a subsequent call is made to reset(), the stream will be repositioned at the last marked position so that subsequent reads will re-read the same bytes. The stream allows readlimit bytes to be read before the mark position becomes invalidated. The markSupported method returns a boolean value indicating whether or not the stream type supports mark/reset.
Page 206 Go To INDEX

public boolean markSupported () The boolean value will be true if the stream type supports mark/reset and false otherwise. The read method is overloaded and is an abstract method. public abstract int read () throws IOException There are three different read methods for reading from a stream.

The first version of read() reads one byte and returns the byte read. If it encounters the end of the input source it returns -1. The second version of the read method reads multiple bytes into a byte array. It returns the number of bytes actually read. The third version also reads data into a byte array, but enables you to specify an offset position (offset) in the array at which to start storing characters. It also indicates the maximum number of bytes to read (length). The concrete subclasses of InputStream implement the read method. For instance, the FileInputStream class defines the read method to read a byte from a file. The reset method repositions the stream to the last marked position. public synchronized void reset () throws IOException If the stream has not been marked, or if the mark has been invalidated, it throws an IOException. The skip method skips x bytes of input. It throws an IOException. public long skip (long x) throws IOException The OutputStream class is the counterpart of InputStream. It provides the basic functionality for all output streams. The methods associated with the class are as follows: close() flush() write(byte[]) write(byte[], int, int) write(int) The close method closes the stream. public void close () throws IOException In order to release any resources associated with the stream, this method must be called. It throws an IOException. The flush method flushes the stream. public void flush () throws IOException It will write any buffered output bytes. It throws an IOException. OutputStream's write method is also overloaded.

Page 207

Go To INDEX

The first version of write() is defined as abstract and it writes one byte to an output file. public abstract void write (int buffer) throws IOException The second version writes all the bytes contained in the given byte array. public void write (byte buffer[ ]) throws IOException The third version allows your program to write data from a byte array, specifying a starting offset position (offset) for the write and the number of bytes to write (length). public void write (byte buffer[ ], int offset, int length) throws IOException Both read() and write() can block a thread while waiting for the byte to be read or written. For example, if a network connection is busy there could be a problem reading or writing the byte. In that case the thread in which the call happens is suspended. This allows other threads to operate effectively while the method waits for the stream to become operable. Streams utilize operating system resources so it is wise to monitor their usage. Both InputStream and OutputStream have a close method that can be used to shut off a stream when you are finished reading or writing to it. When you close an output stream you also flush the output stream. This clears the stream of any data that has been temporarily placed in a buffer before transmission. Buffered data may not be transmitted unless you close the file or explicitly call the flush method. A call to the flush method flushes the stream explicitly. The Java libraries give you a choice of dozens of stream types. Streams are often used for retrieving data from the keyboard and displaying it on screen. File I/O is another fundamental part of any computer system. Java provides several classes for reading from and writing to files. When an applet is downloaded it is unable to access the file system of the computer. This is an important security measure. However, a standalone application usually needs to access the file handling resources of the system it is being run on. If your file-reading needs are relatively simple, you can use the FileInputStream class in the java.io package. This is a simple input-stream class derived from InputStream. It implements many of the abstract methods defined in the InputStream class. However, it does not implement mark() or reset(). There are three constructors for initializing an object from the FileInputStream class.

The first constructor, FileInputStream(File), creates an input file from the specified File object. The second constructor, FileInputStream(FileDescriptor), creates the object from a FileDescriptor object. The third constructor, FileInputStream(String), creates an input file with the specified system-dependent filename. The counterpart to the
Page 208 Go To INDEX

FileInputStream class is FileOutputStream. This class provides basic file-writing capabilities. There are four constructors for the FileOutputStream class.

The first constructor, FileOutputStream(File), creates an output file with the specified File object. The second constructor, FileOutputStream(FileDescriptor), creates the object from a FileDescriptor object. The third constructor, FileOutputStream(String), creates an output file with the specified system-dependent file name. The fourth constructor, FileOutputStream(String, boolean), creates an output file with the specified system-dependent file name. Practically everything you want to do with a file can be done using the RandomAccessFile stream class. It is one of the few stream classes not derived from InputStream or OutputStream. A random access file is one, which allows information stored in it to be accessed at random positions, rather than in the sequential order of the data in the file. There are two constructors for initializing objects from this class. The first constructor, RandomAccessFile(File, String), creates a RandomAccessFile from a specified File object. The second parameter specifies the mode - "r" for read and "rw" for read/write. Calling the second constructor, RandomAccessFile(String, String), will create a RandomAccessFile with the specified system dependent filename and the specified mode. There are over 30 methods with which you can manipulate the file once your RandomAccessFile object has been created. A random access file has a file pointer setting also. This indicator always points at the position of the next byte that will be read or written. The getFilePointer method returns the current position of the file pointer. The seek method sets the file pointer to a specified byte position within the file.

Collections

The java.util package provides several utility classes that give improved functionality to the Java run-time environment. The java.util package defines a number of container objects, that is, objects that contain or hold other objects. Some classes provide you with the ability to store a collection of objects without knowing ahead of time how many objects you want to store. Some also allow you to associate one object with another. The Vector class is a powerful alternative to conventional arrays. Vectors are array-like objects that can grow or shrink to meet storage requirements. This is more flexible than an array, which has a fixed length. Vectors can only hold references to objects. When you create a vector, you can specify how big it should be initially, and how fast it should grow. The Vector class has three constructors.

Page 209

Go To INDEX

The first constructor, public Vector(), creates an empty vector with an initial capacity of the default size 10. The second one, public Vector(int initialCapacity), creates a vector with space for initialCapacity elements. The third one, public Vector(int initialCapacity, int capacityIncrement), creates a vector with space for initialCapacity elements and a specified capacity by which the vector will increment when it is full and needs to store another element. Suppose you create a vector that has the capacity for four items and you fill it with four items. Then you want to add a fifth item. The vector will relocate and resize itself to do this. By default the vector allocation will double each time it relocates. There are two ways to add new objects to a vector. You can add an object as the last element in the vector, or you can insert an object in between two existing objects. Accessing elements in a vector is similar to accessing array elements. You use the elementAt method to access vector elements. someVector.elementAt(4) You might want to use a vector to build up a container of objects initially, but then convert the vector into an array for maximum efficiency. You usually do this only after you have all the objects you need. For instance, if you are reading objects from a file that can contain any number of objects, you store the objects in a vector. When you have finished reading the file, you create an array of objects and copy them out of the vector. Java also has a BitSet class from which you can create bitsets. A bitset is really a vector of bits. It is used if you want to efficiently store a whole lot of twostate (i.e. binary) information. Because a bitset collects the bits into bytes, it is far more economical in terms of memory than using a vector of boolean objects. The java.util package also contains a Stack class. The Stack class is derived from Vector. A stack contains objects and is manipulated in a "last in, first out" (LIFO) manner. So when you take an element from a stack it is always the most recently added element. To add an object to the top of the stack, you push it onto the stack. public Object push(Object newItem) The pop method removes the top object from the stack. public synchronized Object pop() The Dictionary class is an abstract class that provides methods for associating one object with another. Dictionaries are often used to associate a name with an object and retrieve the object based on that name. In a dictionary, the name object is called a key, and it can be any kind of object. The object associated with the key is called the value. A key can be associated with only one value, but a value can have more than one key. You use the put method to store an object in a dictionary with a specific key. public abstract Object put(Object key, Object Value)
Page 210 Go To INDEX

The get method lets you retrieve objects from a dictionary. public abstract Object get(Object key) The Hashtable class is an implementation of the Dictionary class that uses the hash codes of the key objects to perform the lookup. It collects keys into "buckets" based on their hash code. Searching a hash table is efficient because only elements that hash into the same bucket are searched. Firstly, the key's hash code is computed. Next, the hash code is used to get the correct bucket. Finally, the bucket is searched for the correct key.

7.2. Threads and Synchronization • Introduction to multithreading

When they're running on your computer, programs sometimes execute using separate "threads". Each of these threads can be viewed as a piece of work that the CPU must carry out. Threads are sometimes called "lightweight processes". Like processes, they can be executed independently. But threads don't generate the same level of overheads as processes. In a single-threaded system, programs are restricted to carrying out one task at a time. Each thread must finish its activity before another thread can begin. But very often, important operations within a program are designed to occur simultaneously. For example, your computer may want to download a file, update a screen, and respond to user input at the same time. For this reason, single threading can be restrictive. The ability to run more than one program thread at a time is called multithreading. Java has a built-in multithreading capability, unlike most other programming languages. This means that Java threads can execute concurrently. Writing multithreaded programs can be tricky. But as you can see, they give Java programs a powerful advantage over single-threaded languages, such as C and C++. Both C and C++ are capable of multithreading on certain platforms. However, their multithreading capabilities are much less sophisticated than those of Java. Each thread has a distinct life cycle, during which it passes through a number of states. Most of these states don't require complete control of the CPU. A newly created thread is said to be in a "born" state. Threads remain in this state until their start method is called. Once the start method is called, the thread enters the "ready" state. When a system assigns a CPU to a thread and that thread begins executing, it is described as being in a "running" state. If a running thread issues an input/output request, it enters a "blocked" state. A blocked thread must wait for some input or output to complete before it can continue. In a single-threaded system, the entire program must pause until the I/O process completes. In Java, the blocked thread is the only one that pauses. All other threads can continue running. A running thread enters a "sleeping" state when a sleep method is called. This thread automatically returns to a ready state as soon as the designated sleep time expires. A thread's activity can be temporarily "suspended" by calling the suspend method. A suspended thread becomes a ready thread when its resume method is called by another thread.

Page 211

Go To INDEX

Blocked, sleeping, and suspended threads cannot use a CPU, even if one becomes available. When a running thread calls the wait method, that thread enters a "waiting" state. A waiting thread will resume running when the notify method is called. A thread enters the "dead" state when its run method completes or when its stop method is called. The stop message sends a ThreadDeath object to the thread. ThreadDeath is a subclass of the class Error. Once stopped, a dead thread cannot be restarted. This thread will eventually be disposed of by the system. A good example of Java's multithreading capability is the process of garbage collection. Remember, garbage collection describes the process of reclaiming dynamically allocated memory when it is no longer being used. In C and C++, programmers must explicitly dispose of dynamically allocated memory. However, in Java, this process runs as an implicit, "low priority" thread in the background. This means that this thread runs when the CPU is idle. This means that it doesn't use valuable processing time, or cause higher priority threads to wait.

Priorities and daemons

A CPU can execute instructions from only a single thread at a time. But Java programs often contain several ready threads. To coordinate thread execution, you assign a priority to each thread. Thread priorities range between Thread.MIN_PRIORITY and Thread.MAX_PRIORITY. In current Java implementations, Thread.MIN_PRIORITY is defined as a constant with value 1 and Thread.MAX_PRIORITY is defined as a constant with value 10. As an absolute value, a thread priority is meaningless. A thread of priority 9 doesn't run any faster than a thread of priority 1, if it is the only running thread. By default, each thread is given a priority of Thread.NORM_PRIORITY, which is currently defined as a constant with value 5. Java provides an internal scheduler to ensure that higher priority threads receive more CPU time than lower priority threads. A running thread is automatically pre-empted by a higher priority thread. Alternately, a running thread can voluntarily relinquish control to another thread by yielding, sleeping, or blocking. In some cases, several threads of equal priority may compete for CPU time. The way these threads are treated depends on whether your Java run-time system implements timeslicing. In timeslicing systems, each thread receives a limited amount of time - called a quantum - to execute on a CPU. When a quantum expires, the CPU is taken away from that thread - even if the thread hasn't finished executing. The Java scheduler makes this thread wait while all other threads of equal priority get their chances to use their quantum in rotation. When all the other threads have had a chance to run, the scheduler lets the original thread resume its execution. Depending on the way timeslicing is implemented by your system, threads of a given priority may have to run to completion before that thread's peers get a chance to execute. A running thread will be interrupted only if a higher priority thread becomes ready to run. You can't be sure how the system on which your program is running will implement timeslicing. So it's good policy to periodically call the yield method. This gives each thread of equal priority the chance to run. This program illustrates some basic threading techniques.

Page 212

Go To INDEX

You can see that two classes have been defined - ThreadApp2 and SimpleThread. Class ThreadApp2's main method instantiates four instances of SimpleThread. The program launches the execution of four threads by calling the Thread class start method. This places the threads in a ready state. Class SimpleThread is derived from the class Thread.

Page 213

Go To INDEX

It consists of: The instance variable MyID A constructor A run method The run method is automatically called by the start method. It contains the main body of code to be executed by a thread. You can set or change a thread's priority using the setPriority method. This method takes an int argument. If the argument is not in the range Thread.MIN_PRIORITY (1) to Thread.MAX_PRIORITY (10), then this method throws an IllegalArgumentException. In this program, the priority of threads t1 and t2 is set to 4. And the priority of threads t3 and t4 is set to 2. If you want to find out the priority value assigned to a given thread, you use the getPriority method. You can see that the priority value for each running thread is displayed in the program output.

Page 214

Go To INDEX

The output of this program may vary from one system to another. Two important methods have been used in this program: Join Yield Using the join method, you force the main method to wait for threads t1, t2, t3, and t4 to finish before it can proceed. If the main thread finished first, the child threads would immediately be terminated, even though they may not have completed their task. The yield method is a static method that forces a running thread to yield a CPU to its peers after a preset time. As you can see, threads of equal priority, like t1 and t2, yield to one another until one or both complete. Remember, this output may vary slightly from one system to another. When the last running thread completes, the program generates this statement. The yield method is unnecessary in a fully timeslicing system because threads of equal priority will automatically execute for their quantum in rotation. A daemon thread is a low priority thread that runs in the background for the benefit of other threads. A garbage collector thread is an example of a daemon thread because a program will terminate if the garbage collector thread (and/or other daemon threads) are the only remaining threads. You designate a daemon thread with this method call. SetDaemom(true) An argument of false means that the thread is not a daemon thread. If a daemon thread is not set to daemon before its start method is called, an IllegalThreadStateException is thrown. Similarly, the method isDaemon returns true if a thread is a daemon thread, and false if it is not. final boolean isDaemon()

Page 215

Go To INDEX

Programs typically include a mixture of daemon and non-daemon threads. Nondaemon threads are conventional, user threads. When only daemon threads remain in a program, the Java program quits.

Synchronization, wait, and notify

Sometimes two or more program threads need concurrent access to a shared data resource. For example, in this program, the transaction threads t1 and t2 are executing concurrently.

Both threads use the incBalance method to increment the instance variable balance.

In order for this program to work, the balance variable must be incremented twice. But as you can see from the program output, balance has been incremented only once.

These threads must be coordinated so that only one thread can increment balance at a time. To do this, you use synchronized methods, and you coordinate these methods by using "monitors". A monitor is an object that acts as a mutually exclusive lock, or mutex. Every object that contains a synchronized method acts as a monitor. As soon as a synchronized method is called, the thread that executes that method is said to "own" the monitor. Since only one thread can own a monitor, no other threads will be able to call a

Page 216

Go To INDEX

synchronized method on that object. Here the incBalance method has been altered to include the keyword synchronized.

This ensures that only one synchronized thread can execute the synchronized incBalance method at any given time. When the synchronized method finishes executing, the lock on the object is released, and the next thread is allowed to proceed. If several threads are waiting to act on the same object, the highest priority ready thread is allowed to proceed. You can see from this output that by synchronizing the incBalance method, t1 was able to increment balance before t2.

Sometimes a thread executing in a synchronized method will determine that it cannot immediately proceed. In this piece of code, the print method can print a character only when the register buffer has been filled. Suppose the print thread controls the monitor. The register must contain a character in order for the print thread to complete. If the register buffer is not full, the print thread voluntarily calls the wait method. The wait method instructs the running thread to give up the monitor. The print thread will wait in a queue while other threads try to run. This allows the fill thread to execute its synchronized method and read a character into the buffer. When the fill thread completes, it releases its lock on the monitor and uses the notify method to wake up the waiting print thread. The notify method acts as a signal to the waiting thread that the condition the waiting thread was waiting for has been fulfilled. The print thread will now attempt to obtain a lock on the monitor object and execute again. This output illustrates the synchronized behavior between the threads executing the print and fill methods.

Page 217

Go To INDEX

If several threads have called the wait method on the same object, you can wake them all by using the notifyAll method. All the threads waiting for the object become eligible to re-enter the monitor. Since only one of these threads can obtain a lock on the object at a time, the highest priority thread that wakes up will be the first to run. Any threads waiting for a monitor object must be wakened explicitly with the notify keyword, or they will wait indefinitely. So every call to wait, must have a corresponding call to notify that will eventually end the waiting. The wait, notify and notifyAll methods are implemented as final methods in the root class Object, so all classes inherit them.

The Runnable Interface
Suppose you want to create a class that can make use of threads.

Page 218

Go To INDEX

One way to do this is by extending the class Thread.

All classes derived from Thread can support multithreading. The Thread class defines four default methods: Init
Page 219 Go To INDEX

Run Start Stop These methods are automatically inherited by any classes derived from Thread. The only problem with extending classes from Thread is that it creates some unusual relationships. In Java, each class can only be derived from a single superclass. By extending the Thread class, you make your multithreaded class a subclass of the Thread superclass. You can avoid the problem of deriving classes from Thread by implementing a special interface called Runnable. Remember, an interface is a type of abstract class. The Runnable interface is a simple interface that defines an abstract run method. Java classes can implement one or more interfaces without disrupting their superclass. So by implementing Runnable, existing classes can support multithreading without having to change their superclass to Thread. In fact, the Thread class itself implements the Runnable interface. As you can see, this applet implements the Runnable interface.

The code that controls the threads is placed in the run method. Unlike the Thread class, the methods init, start, run, and stop are not implicitly defined in the Runnable interface. If a class implements Runnable, these methods must be defined explicitly.

Page 220

Go To INDEX

7.3. Internationalization • Internationalizing programs

The Internet is a global resource, incorporating every major world language. So Java software designed for the Internet must be global in scope. To create global software, programs must be developed independently of the countries or languages of their users. But these global programs must also be localized in order to be understood in specific geographical regions. The process of writing global software that can be easily localized is known as "internationalization". In the past, internationalization tended to be a separate process that was performed after the original product development. But Java has been designed to make it easy to create internationalized programs from the very start. Included in JDK 1.1 is a diverse set of Internationalization APIs. Using these APIs, Java programmers can adapt text, numbers, dates, currency, and user-defined objects to any country's conventions. And this facilitates the development of global applications and applets. Several Java packages contain classes that assist the internationalization process. Two examples of these packages are java.text java.util These are not Java's only internationalization packages. The java.io package contains classes capable of converting character sets. And the java.awt package provides support for the internationalization of a program's GUI. The java.text package contains classes and interfaces designed for handling text in an appropriate way for a given country or language. This is important because cultural and language conventions can become significant when data is formatted for output. The java.text class Format is an abstract base class for formatting and parsing locale-sensitive information. Three main subclasses are derived from Format: NumberFormat DateFormat MessageFormat The abstract class NumberFormat contains static methods for supporting localespecific number formats. In this example, a currency format is being set for the German locale.

The DateFormat abstract class is used to format and parse date or time values according to the customs of a country. And the MessageFormat class enables programmers to write code that handles messages in a language neutral way.

Page 221

Go To INDEX

The java.util package defines a number of useful localization classes, including the Locale class and other support classes. It also contains new classes for date and time handling. For example, the Calendar class is used to convert between the internal Java representation of dates and times and the different calendar systems used around the world. Subclasses of the calendar class can represent specific calendar systems. Calendar and its subclasses don't handle the formatted representation of dates. This is the job of the java.text class DateFormat and its subclasses. However Calendar is used by DateFormat.

Using locales with Java

In many programming environments, locales are groups of characteristics defining the language and cultural conventions of a particular region. Internationalized programs in such environments are usually assigned a single "global" locale. This global locale contains information that implicitly determines the behavior of the whole program. The use of a global locale is inflexible because it groups unrelated pieces of information into a locale, rather than associating the information with the classes to which it applies. In this example, the global locale file en_US is being passed to two objects. date M/D/Y time HH:MM:SS currency $ numeric 1,000.27 As you can see, both objects have access to all the information contained in the locale. This means that each object receives information it doesn't need. Another disadvantage of single global locales is that multilingual applications often require several locales to be active at the same time. And where programs handle input from various sources, multiple locales are needed to handle multiple character encodings. Unlike traditional internationalized programs, Java programs are not assigned a single global locale. Instead, operations which are sensitive to region or language are explicitly given a locale as an argument. This greatly simplifies multilingual programs because it means that each operation can be assigned a locale. In this way, a Java locale acts as an identifier for a specific combination of language and country. Every locale-sensitive class in a Java program maintains its own locale-specific resources. It's good policy to group these resources by locale, and separate them from the locale-neutral parts of the program. To help programmers do this, Java
Page 222 Go To INDEX

supports an abstract class called ResourceBundle. Using ResourceBundle, programmers can create subclasses that contain all the resources for a specific locale. This means that new resources can be added to this class, or new ResourceBundle subclasses can be added to your program, without disrupting existing code. In addition, packaging resources as classes allows developers to take advantage of Java's class loading mechanism to load resources. In cases where programs are not designed to manage locales explicitly, a system-wide default locale is available. Using a system-wide locale, it is possible, with a single modification, to alter the look of an entire application. A number of standard localizations are provided by Java's Internationalization APIs. These are listed in your JDK 1.1 documentation. You can view a locale as a request for certain behavior from a given object. Let's look at an example. Suppose a French Canadian locale is passed to a Calendar object. As you know, classes derived from the java.util Calendar class are used to represent specific calendar systems. In effect, this locale is instructing the Calendar object to behave correctly for the customs of French-speaking Canada. If an object has not been localized for a specific locale, it will try to find a "close match" with the locale for which it has been localized. So if the Calendar object isn't localized for French-speaking Canada, but is localized for the French language, then it will use the French localization instead. As you know, the java.util package defines the Locale class. Locale objects are generally created from a language name and a country name.

All Java's International classes use the same convention for naming languages and countries. Language names are two-letter ISO-639 language codes and country names are two letter ISO-3166 country codes. In this case, an empty country string is passed to the Locale constructor.

This defines a locale for the entire English language. The Locale class contains a number of handy constants for creating Locale objects for commonly used languages and countries. For example, this specifies the Locale object for Great Britain. Locale.UK Using this constant, you can avoid creating a new locale. The Locale class contains two static methods - get and set - for accessing the system's default locale. At start-up time, the default locale is automatically set by the Java run time to match the locale of the computer environment in which the program is running. If this is not possible, the en_US locale is used.

Java Support for Unicode

Page 223

Go To INDEX

Internet users often use a combination of languages to communicate with one another. So not only must Java programs be capable of supporting many individual languages, but they must also provide multilingual support. The set of all characters used to write programs and represent data items on a particular computer is called a character set. Because Java designers wanted Java to be both portable and support multiple languages, they chose to adopt Unicode as Java's built-in character set. The Unicode Standard is an internationally recognized (ISO) character encoding system. In its current version (Version 2.0), the Unicode character set contains 34,168 distinct coded characters, derived from 24 language scripts. These include many nonLatin characters used by East European and Asian languages. Unicode was designed to support the interchange, processing, and display of diverse, written texts. Since it is capable of encoding the characters used in the majority of world languages, its use facilitates the development of multilingual programs. And it is well suited to supporting the many languages used on the Internet. Each character in a Java program is represented by a two-byte Unicode character. Each byte is composed of eight bits. So the implementation of a Unicode character is a 16-bit unsigned value. Using this character encoding, the lowercase letter 'a' is represented in Unicode by the integer 97. JDK 1.1 programs are able to display any Unicode character that can be rendered with a host computer's font. Java provides a small number of predefined "virtual" font names that can be mapped to a number of host fonts. In JDK 1.0, each Java font name can be mapped to exactly one host font. But in JDK 1.1, a Java font name can map to a series of host fonts. The series of host fonts can be chosen to cover as much of the Unicode character set as is desired. The font mapping is specified in a font properties file. One of the difficulties in using Unicode is the poor availability of fonts that can display all of the Unicode characters. In addition, many Java programs run in language environments where characters are usually encoded in 8-bits. This means that storing each character in 16-bits as supported by Unicode is not always the most efficient way to transmit text. To get around the problems associated with Unicode, Java designers developed support for other encodings called "transformation formats". For example, Java supports a UTF-8 character encoding with the DataInputStream.readUTF() and DataOutputStream.writeUTF() methods. The UTF-8 is a variable-width, or "multibyte" encoding format, that supports all the characters in the Unicode character set. This means that different characters occupy varying numbers of bytes, with characters from Western European languages each occupying only a single byte. Sometimes Java programs need to handle text data in non-Unicode character sets. To do this, Java provides a set of classes that convert many standard character sets to and from Unicode. These classes are grouped together in the java.io package. Java programs typically convert non-Unicode text data into Unicode, process this data as Unicode, and then convert the result back to the external character encoding.

Page 224

Go To INDEX

8 Java Security, Networking, & Internet
8.1. Java Security • Security features of the Java language
Security becomes a critical issue when downloading applets and using programs that interact with the Internet. You worry about hackers targeting your system with viruses, Trojan horses, and in rare cases, "web-spoofing". Web-spoofing involves an attacker slipping between a Web user and the web site being accessed. The attacker can read sensitive data such as credit card details, while still allowing "through" access to the web site in question. By pretending to be from a trusted source, a hacker's malicious program might persuade your system to reveal valuable data. Complete security cannot be guaranteed when interacting with a dynamic medium such as the Internet. With Java, however, you can provide a significant level of security for your computers and data, even in a networked environment. Java provides you with a series of interlocking defenses that together form a barrier to most, if not all, attacks. Java's powerful security mechanisms operate at four different levels of the system architecture. Much of the security is inherent in the Java language itself. Java was designed from the ground up to be a safe object-oriented language that would operate effectively in a networked environment. The Java compiler - javac - ensures that the source code doesn't break the language's strict safety rules. At run time, additional security comes into play. All bytecodes are checked by a verifier to ensure they comply with the safety rules. In the JDK, the Java interpreter is simply called java. The bytecode verifier guards against an altered or rogue compiler producing code that violates these rules. The third level of Java's security involves the class loader. It ensures classes don't violate name space or access restrictions when they are loaded into your system. Finally, Java provides protection from attack through the use of the SecurityManager. SecurityManager is an application-wide object that determines whether or not to allow potentially threatening actions. The classes in the Java packages cooperate with the SecurityManager. Basically they ask the SecurityManager's permission to perform certain tasks. Java started off with an extremely conservative security model, which will become more flexible as time goes on. Unfortunately, flexibility and security cannot both be maximized. There is always a trade-off between absolute security and practical functionality. So far on the Internet, many users have chosen maximum flexibility at the cost of compromising security. Java provides considerable security while sacrificing only a minimal amount of the flexibility that has drawn so many users to the Internet. The Java language and its compiler are the first line of defense in its elaborate security system. Java was designed to be a safe, object-oriented language and to avoid some of the problems associated with the C and C++ languages. Java's object-oriented features protect data structures and limit the likelihood of unintentionally flawed programs arising.
Page 225 Go To INDEX

Object-oriented code also facilitates maintenance and so enhances system security. Java enforces strict adherence to the object-oriented model. Its private data structures and methods are encapsulated within Java classes. Access to these resources is granted only through a public interface provided by the class. Moreover, many classes and methods within the Java API are declared final, preventing programs from further subclassing or overriding specific code. Java's language definitions are strict. All its primitive types, for example integers and floats, are guaranteed to be a specific size. All operations are performed in a predefined sequence. This is very different from C and C++, where the size of the primitive types are machine and compiler dependent. With these languages, the order of execution is undefined also, except in certain specific cases. Java has learnt much from the shortcomings of the C and C++ languages. Most Clike languages have facilities to control access to objects, but they also have ways to "forge" access to objects, usually by mis-using pointers. This introduces serious security flaws. In the first place, it means no object can protect itself from outside modification, duplication, or "spoofing". In addition, a language with powerful pointers is more likely to have serious bugs that compromise security. Java eliminates these dangers in one stroke by excluding pointers and pointer arithmetic from the language altogether. Java still has pointers of a kind, called object handles, but these are carefully controlled. Java's object handles are unforgeable, and all casts are checked before being allowed. With Java, you cannot create code that thrashes system variables or accesses private information. Java offsets the loss of pointers by providing powerful new array facilities. Array bounds are strictly enforced, avoiding the bugs that in other languages might lead to unexpected problems. With Java, any attempt to index an element before the beginning or after the end of an array will throw an exception. Unlike C or C++, Java is a strongly typed language, which further enhances security. Its strict typecasting ensures that an object cannot be arbitrarily cast to another type. This means that programs cannot access values of uninitialized local variables, reference class variables, or private methods. Java also provides support for thread-safe programming. Multi-threaded programming is an intrinsic part of the Java language. Special semantics ensure that different threads of execution modify critical data structures in a sequential, controlled fashion. With C and C++, programmers have to allocate and deallocate memory, as common bugs arise from careless memory management. Sometimes programmers forget to free memory once it's no longer needed. In other cases, they accidentally free the same piece of memory twice. Failing to free memory can cause a program to use increasing amounts of it. Accidentally freeing the same piece of memory often causes subtle corruption bugs that are difficult to locate. The Java language eliminates the need for programmers to be concerned with memory issues provided that they dereference unused objects. With Java, memory management is dynamic and automatic. When you create a new object, for example, Java automatically allocates the right amount of memory for it. When you finish with an object, and carefully dereference it, Java's automatic garbage collector cleans up.
Page 226 Go To INDEX

The garbage collector searches for unused objects and reclaims the memory that those objects were using. Besides plugging "memory leaks", automatic memory management also prevents the "dangling pointers" problem - where valid storage is freed prematurely. The Java language is in the public domain and so has been subjected to close scrutiny for some time. The Java compiler's stringent compile-time checking of source code ensures errors can be detected as early as possible. The source code for both the Java compiler and interpreter are available to all and they have undergone stringent security reviews. Java has by no means an infallible security system, but it's probably more secure than most current software, precisely because it's been subjected to such intense public scrutiny. Any bugs or security loopholes detected are given maximum exposure and consequently fixed quickly.

The verifier

Java's security would be easy to undermine if it were merely reliant on the protection afforded by the Java language and compiler. The compiler converts Java source code into bytecodes. Java bytecode, the machine code for the Java Virtual Machine, is the essence of what is transmitted over the network. When the Java run time gets bytecodes from the Internet, it has no way of knowing whether those bytecodes were generated by a trustworthy compiler or not. A hostile compiler could easily create bytecodes that would perform dangerous operations on your system. You cannot assume that bytecodes have been generated by a benevolent compiler such as javac. From the start, the verifier adopts a conservative approach. All class files are presumed to be hostile unless proven otherwise. To guarantee a safe execution environment, the verifier runs a comprehensive battery of tests on the bytecode to ensure that they are observing the rules. These tests range from simple format checks all the way to running a theorem prover. The verifier is able to detect whether bytecodes forge pointers or violate access restrictions. It can also ensure that bytecodes don't call methods with inappropriate argument values or types, or try to overflow the stack. Another Java run-time requirement is that when a set of bytecodes takes more than one path to reach the same point in a program, all must arrive there with exactly the same type state. This is a strict requirement, and means, for example, that compilers cannot generate bytecodes that load all the elements of an array onto the stack. This is not allowed because each time through such a loop the stack's type state changes. The start of the loop would then have more than one type state. In the interests of security, Java bytecodes actually carry more type information than is strictly necessary for the interpreter. For example, iload and dload will both load a local variable onto the stack. However, iload is always used to load an integer, and dload is used to load a double. This additional type information allows the run-time system to guarantee that Java objects and data aren't illegally manipulated. The verifier checks all bytecodes for compliance with the extra type requirements of Java. It examines each bytecode in turn and constructs the full type state as it goes. It checks that all the types of parameters, arguments, and results are correct. The verifier
Page 227 Go To INDEX

acts as your system's security guard, allowing access only to those bytecodes with the right credentials. You can only be certain of a safe execution environment when the verifier has proved that the bytecodes are trustworthy. Once the verifier has done its job, the interpreter can run much faster than before. In addition, object references can now be treated as capabilities because they are unforgeable. Capabilities allow advanced security models for file I/O and authentication to be safely built on top of Java.

The class loader

Once bytecodes have been approved by the verifier, they can then enter the class loader. Working closely with Java's security manager, the class loader acts as a protector of your system. The class loader's main function is to enforce Java's name space hierarchy. It guarantees that separate namespaces exist for classes that come from your local file system, and different network sources. When a new class is loaded into your system, it is placed in one of several different realms. For example, it may be placed on your local computer, on your firewall-guarded local network, or on the Internet. Each of these realms is treated differently by the class loader. In particular, the class loader never allows a class from a "less protected" area replace a class from a more protected space. For example, the class loader wouldn't allow a class downloaded from the Internet replace a class on the local file system. You may, for example, have particular security concerns about your file system's I/O primitives. These are all defined in a local Java class, which means that they live in the local computer area. The class loader ensures that no class from outside your computer can take the place of these classes or "spoof" Java code into using "rogue" versions of these primitives. Furthermore, classes from one source cannot call on class methods from other sources, unless those classes have explicitly declared those methods public. This means that classes outside of your local computer cannot "see" your file system I/O methods, much less call them. They can only be called if you, or your system, wants them to be called. In addition, every applet loaded from the network is placed into a separate package-like namespace. This means that applets are even protected from each other. No applet can access another's methods or variables without its cooperation. Applets from inside the firewall can even be treated differently from those outside the firewall. The class loader essentially divides the world of Java classes into small, protected groups, about which you can safely make assumptions that will always be true. This type of predictability is the key to secure, well-behaved programs.

The security manager

The security manager and class loaders are at the heart of a Java application's security policy. Together, they make the decisions about whether to grant requests for access to your system's resources. The SecurityManager class collects in one place all the security policy decisions that the Java system must make when bytecodes are run. Browsers, such as HotJava and Netscape Navigator, enforce security policies by implementing the SecurityManager class.

Page 228

Go To INDEX

In the case of browsers, an instance of some subclass of SecurityManager is always installed as the current security monitor. The security monitor has complete control over a carefully specified set of "dangerous methods" that are allowed to be called by any given class. These methods usually concern file or network I/O. In order to enforce the policy, the security manager takes into account the source of the downloaded class. It also considers whether the class is from an applet or an application. Security policy decisions for applets, including file I/O and network access, can be configured by users within particular browsers. They can change the security manager settings with the HotJava browser, either allowing or preventing file reads, file writes, and network I/O by applets. You can enforce the settings on an area by area basis, or you can give applets unrestricted access to your system. Needless to say, giving applets unrestricted access is not recommended. The Netscape browser currently does not allow you to change the default settings of the security manager. Unfortunately, the JDK doesn't come with a working security policy mechanism that's ready for an application to use. By default Java applications, as distinct from applets, start without a security manager. This means all the resources that SecurityManager could restrict are readily available. However, by implementing a SecurityManager, you can add a significant measure of protection to your system. Existing browsers and applet viewers create their own security manager when starting up. So an applet is subject to whatever access restrictions are imposed on it by the security manager for the application in which the applet is running. The Java API provides the java.lang.SecurityManager class as a way of creating a clearly defined set of tasks an application can or cannot perform, such as accessing files or network resources. The SecurityManager class allows you to establish a specific security policy appropriate to the level of trust you want to give to a particular program. Let's see how the security manager makes a decision about granting access to a system's resources. Suppose an applet called Snoop has been loaded onto your system from the Internet and wants to read one of your files, my.doc. To read the my.doc file, the Snoop applet must use one of Java's core classes in the java.io package, for example, FileInputStream. Because this class is a part of the overall security model, it must ask the security manager for permission to read the file. How does a security manager decide whether to allow my.doc to be read? The SecurityManager class contains several native methods that can be used to inspect the current state of the Java virtual machine. In particular, the methods being executed while the security manager is being queried - the execution stack - can be vetted. By checking the execution stack, the security manager can tell which classes are involved in the current request. It can then decide whether those classes can be trusted with the resources being requested. The security manager needs the help of its class loader partners to do its job. For each class on the execution stack, the security manager can determine which class loader is responsible. For example, if it discovers that a class was loaded from the network, it can then decide to reject the request to read the my.doc file. Until recently, Java applications didn't support multiple levels of trust - a class was either trusted or not.

Page 229

Go To INDEX

Now that digital signature technology is available for Java classes, it will be possible to verify the network source of Java classes. Security restrictions can then be relaxed appropriately to provide greater flexibility and functionality.

Applet security

It's important to note the differences between Java applications and Java applets as they have definite implications for security. Java applications are stand-alone programs that can be run by using the Java interpreter from the command line. Most Java applets, however, run inside a WWW browser, with a reference to the applet embedded in a Web page using a special HTML tag. JDK is intended to enable browsers to run untrusted applets in a trusted environment. When Java applets run inside a Java browser, they have the advantage of the structure that the browser provides - an existing window, an event-handling and graphics context, and a user interface. The convenience that applets have over applications in terms of ready-made UI capabilities, however, is hampered by restrictions on what applets can do. Because Java applets can be downloaded from anywhere and run on a client's system, restrictions are necessary to prevent an applet from causing system damage or security breaches. Prior to JDK 1.1, there was no mechanism for establishing proof of ownership. As a result, all applets were assumed to be untrustworthy and in general that still is the case. Untrusted applets downloaded from the Internet are basically treated as suspicious by the local system and restricted in several ways. They are not allowed to read or write files, or execute programs on the local computer. In addition, these applets are not allowed to create, modify, or delete local files. Untrusted applets cannot make a network connection to a site other than the one from which the applet was loaded. They cannot act as network servers either, or listen for or accept socket connections from remote systems. Windows opened by an untrusted applet are always identified clearly as Java windows. This prevents a Java window from masquerading as something else, such as a window's dialog box requesting your name and password. Unlike Java applications, untrusted applets are also prevented from using dynamic or shared libraries from any other programming language. Applets cannot make use of this feature because there's no way to adequately verify the security of the non-Java code being executed. The restricted applet execution environment is often referred to as the "applet sandbox". The idea is that an applet has to play inside the sandbox, and any attempt to leave it is prevented by the applet security manager. As you can see, the applet sandbox model isn't meant to be a complete Internet security solution in and of itself. However, it is an important safety net protecting your system against bug-ridden or malicious code downloaded from the Internet. Java applets are more limited in functionality than stand-alone Java applications. This loss is a trade-off for the security necessary for applets to run remotely on your computer. When an applet is loaded from the local file system rather than over the Internet, Web browsers and applet viewers may relax some of their security restrictions. The reason for this is that local applets are assumed to be more trustworthy than anonymous applets from the network.

Page 230

Go To INDEX

Web browsers, such as HotJava 1.0, may also allow the user the option of relaxing security restrictions for trusted applets which have been digitally signed and whose certificates have been verified by the user. The way an applet enters the system affects what it is allowed to do. If an applet is loaded over the Internet, then it is loaded by the applet class loader, and is subject to the restrictions enforced by the applet security manager. If an applet resides on a client's local system, and is in a directory on the client's classpath, then it is loaded by the file system loader. A local applet is subject to a more relaxed security regime. Applets loaded via the file system are not passed through the bytecode verifier. They can read and write files, and load libraries on the client. They are also allowed to execute local processes and exit the virtual machine. Intermediate applet security policies are also possible. For example, an applet viewer could be written that would place fewer restrictions on applets loaded from an internal corporate intranet than on those loaded from the Internet. JDK 1.1 now provides the basic technology for loading and authenticating signed classes. This enables browsers to run trusted applets in a trusted environment. Strict security is still needed to run untrusted applets. In the release following JDK 1.1, JavaSoft promise to provide more tools for finer-grained control of flexible security policies. Java security doesn't try to solve every security problem. At present, there are some potential attacks that Java doesn't prevent. These problems involve the abuse of resources such as CPU cycles, memory, and windows. The current Java security model makes no provision for letting an untrusted class use only a certain amount of a system's resource. Java doesn't keep track of resource usage, so a Java application cannot enforce resource quotas. Rogue applets can exploit this fact to mount denial-of-service attacks that render your computer useless by allocating all your memory or some other finite resource. For example, a rogue applet could be set to monopolize your system resources using numbercrunching. This would cause your system to grind to a halt.

Extending Java security

One of the key security capabilities missing from the initial Java implementations was the ability to establish trust relationships. With Java 1.1 and the formation of the Java Security API, you can now create trust relationships. Using digital signatures, you can verify that code from specific sources has not been altered by a third party. The latest features of the Java Security API are based on computer cryptography designs and algorithms. The oldest and most familiar cryptographic system is private key encryption. Under this system, both the sender and the receiver use the same secret key to encrypt and decrypt a message. The private key encryption scheme has serious limitations. Since both the sender and the receiver use the same key, either of them can encrypt a message and claim the other person sent it. This underlying ambiguity about who created a message makes it impossible to establish a message's origin. A number of cryptographic systems use private key encryption. Data Encryption Standard (DES) is a widely used system. However, cracking DES is quite possible with today's technology. Some of the problems associated with private key encryption can be
Page 231 Go To INDEX

addressed by public key cryptography. This system is based on the idea of a pair of private and public keys. One key can encrypt a message while the other decrypts it. The private key is known only by the owner, while the public key is advertised widely. It doesn't really matter whether you use the private or public key to encrypt a message. The receiver just uses the other key to decrypt it. The combined use of private and public keys can be used to guarantee message confidentiality and confirm the identity of the sender. To create a confidential message to John, for example, you would use his public key to encrypt the message. Since the message was encrypted with John's public key, only John can decrypt it with his own private key. Another advantage of public key cryptography is that it allows you to confirm who sent a message. John is the only person who can encrypt a message with his private key. If John's public key succeeds in decrypting that message, then the message must have come from John. Using public key cryptographic algorithms to encrypt entire messages is very timeconsuming. So cryptographers have devised a way to generate a short, unique representation of a message called a message digest. To create a digital signature, you encrypt the message digest with your private key. You can then transmit the unencoded message along with the digital signature. If the message is altered in any way en route, the receiver will not be able to decrypt the signature. One of the limitations in the public key system is verifying that a public key truly belongs to a trustworthy individual. It is quite possible that a hostile individual could claim to be someone else and send you a message signed with a secret key. This attacker then advertises the public key as belonging to another person. When you obtain the public key and decrypt the signature, you may believe you have verified the author. You can then end up trusting information that was actually written by a hostile source. To overcome this limitation, secure transmission systems on the Web have turned to a system known as Certification Authorities (CA). Basically, a CA is a highly respected organization that goes to great lengths to ensure that its public key is properly advertised. These Certification Authorities include GTE Cybertrust, Nortel Entrust, and VeriSign. The CA signs the key of other agencies that conclusively proves their identity. When you receive the public key of an agency, you can use the CA's public key to verify it. If successful, you know that the CA believes this agency is what it claims to be. Digital signatures, based on public key cryptography, can extend the capabilities of applets. In general, applets downloaded from the Net are assumed to be potentially hostile to your system. However, if an applet is digitally signed using public key cryptography, you can then identify the source. This increases your confidence that a cracker has not somehow altered what was written. You can then move on to establish trust relationships, assigning specific roles to applets from known sources. If you have a relationship with a company and trust the information it provides, you can then feel comfortable in allowing its applets greater access to your system. It's important to remember that other parts of the Java security system still remain in place, even after a trust relationship has been established. So it isn't an all-or-nothing proposition.

Page 232

Go To INDEX

Applets from trusted sources may be given incrementally greater access to your system. In JDK 1.1 Beta 1, signature checking always failed, and so all applets were untrusted, with minimal permissions enabled. This made the code signing feature unusable. This problem was fixed on December 13, 1996. Unsigned applets are still untrusted and are subject to the same limitations that were in place prior to the release of the Java Security API. As of February 18 1997, JavaSoft has included the digital signature capability with JDK 1.1. JavaSoft promises to provide more tools for finely-grained control of security policies in the release following JDK 1.1.

The Javakey Tool

Java supports a special type of file called an archive file. Using Java archive (JAR) files, you can package class files, images, sounds, and other digital data in a single file. This simplifies and speeds up file distribution. To secure the distribution of JAR files across the Internet, a digital signature is assigned to each file. Java provides a commandline security tool called javakey to apply and maintain these signatures. Both JAR files and javakey are new features for JDK 1.1. By assigning digital signatures to JAR files, it becomes possible to verify that a given file came from a specified entity. Two types of entity are managed by javakey: Identities Signers Identities are people or organizations that have public keys associated with them. Signers are people or organizations that have private keys in addition to public keys. In order to sign a file, an entity must have both public and private keys. A public key used for signing must be authenticated by one or more digitally signed statements called "certificates". Information about private and public keys, certificates, and their associated entities is grouped together in an entity database. And this database is maintained by javakey. To generate a signature for JAR files, you must first create a signer with an associated key pair and at least one certificate. Every signer (or identity) that you add to the javakey database requires a username. To create a signer called rVelasquez, you use the option -cs. javakey –cs rVelasquez true You can create an identity using the option -c. From now on, any javakey commands acting on this entity must use the defined username. The presence of the true clause in this statement indicates that rVelasquez is a trusted signer. If you don't explicitly declare a trust level of true or false, then by default, entities are untrusted. JAR file applets that are signed by a trusted entity have many more rights than untrusted applets. In fact, they are able to run with the same rights as local applications. Once you've created a signer, you need to assign its public and private keys. In this example, a Digital Signature Algorithm (DSA) public and private key pair has been generated for rVelasquez. javakey –gk rVelasquez DSA 512 If a signer's public and private keys are DSA keys, then javakey will sign the JAR file using DSA.

Page 233

Go To INDEX

If the signer's keys are RSA keys, then javakey will sign the JAR file using the MD5/RSA algorithm. As you can see, the DSA has a key size of 512 bits. Now you need to generate a certificate. Remember, every public key requires an associated certificate for authentication. To do this, you must first specify a certificate directive file. The certificate directive file must specify: The entity whose publickey is authenticated byte certificate Information about the certificate itself The name of the signer signing and issuing the certificate

The person or organization signing the certificate should be publicly recognized as being trustworthy. You can optionally include information about the signature algorithm to be used.

This is only required where the algorithm is not DSA. The name of a file that stores the certificate is also optional. Once the directive file is specified, you can generate the certificate. The -gc option instructs javakey to create a certificate using the information specified in the directive file rVelasquezDirectiveFile. javakey –gc rVelasquezDirectiveFile You've seen how to create a signer in the javakey database, generate public and private keys for this signer, and generate a certificate using this signer. Now you need to
Page 234 Go To INDEX

create a digital signature for that signer. Generating a digital signature, like certificate generation, is directive-based. Each directive file contains a signer profile. So in order to create a digital signature you must first create a signer profile. The signer referenced in the signer profile must exist in the javakey database.

And the signer must have a certificate number. This specifies which of the signer's certificates is used to sign the certificate file, and authenticates the subject's public key. The chain parameter indicates the chain depth of the chain of certificates to be included. This parameter is not currently supported. The signature filename can be eight characters or less. You can also specify the name of the signed JAR file in the profile, but this information isn't mandatory. Once the JAR file and the directive file have been created, you can use the -gs javakey option to sign the JAR file. javakey –gs directiveFile jarFile The directiveFile variable represents the name and path of the directive file. And jarFile represents the name and path of the JAR file. The output of this command is a signed and secure JAR file.

8.2. The applet execution environment • Implementing the security manager

If you wish to tighten or otherwise change the security of your Java application, you must implement your own security manager. To do this, your first step is to create a subclass of the SecurityManager class. The Security Manager subclass will then have to override various methods from the SecurityManager class in order to customize your security policy. Let's assume you want a class CustomSecurityMgr to implement a security manager that places further restrictions on reading your files. To get permission from CustomSecurityMgr, you need to invoke one of SecurityManager's checkRead methods. If CustomSecurityMgr approves access, then the checkRead method returns. Otherwise, it throws a SecurityException. Our sample program prompts the user for a password. If the password is correct, then permission to read local files is allowed. In this example, the CustomSecurityMgr class extends SecurityManager.

Page 235

Go To INDEX

The checkPassword method is invoked before a file is read. It prompts the user for a password and then verifies it. If a user enters a valid password, then checkPassword() returns true. If the password is invalid, it returns false. Let's now refine the applet's security by first overriding the SecurityManager constructor to initialize the private instance variable "password".

This will hold the password that the user must enter in order to read files. This method calls the super function to initialize the base class. Let's now override the three checkRead methods, to ensure that files can't be read without entering the correct password.

Page 236

Go To INDEX

Each of these methods calls checkPassword() to prompt the user for a password. If the password is invalid, checkRead() throws a SecurityException and the message "Access Denied" is displayed. SecurityException is a run-time exception, and as such does not need to be declared in the throws clause of the checkRead method. Write access to the local files is still forbidden since no checkWrite methods have been overridden in this program. Once you've completed your SecurityManager subclass, you can then install it as the current security manager.

You do this with the setSecurityManager method. The main method starts installing the new custom security manager. The highlighted code creates a new instance of the CustomSecurityMgr class with the password "jumbo". This instance is passed to System's setSecurityManager method. The setSecurityManager method installs the object as the current security manager. This security policy will remain in effect while the application is being executed. To verify that your customized security manager is working properly, you open a "test.txt" file for reading. If the file's contents are displayed, then your CustomSecurityMgr has been installed correctly. When you run the CustomSecurityMgrTest application, you are prompted for a password. If you type in the correct password, access is granted. If you type an incorrect password, a SecurityException is thrown. The application terminates and the "Failed Test" error message is displayed.

Applet context

Applets are often viewed as incomplete applications. Yet applets have considerable capabilities because they are supported by the code of the application they run in. Applets have access to this support through the java.applet package. All of an applet's basic functionality can be found in the java.applet package. This package contains the Applet class, and the AppletContext, AudioClip, and AppletStub interfaces. The AppletContext is a public interface that lets you get at information in the applet's execution environment. Applets run inside browsers such as HotJava and Netscape or the applet viewer. An applet can ask the browser to:
Page 237 Go To INDEX

Show a message in the status line Fetch an audio clip Show a different web page The browser can carry out an applet's requests or ignore them. For example, if an applet asks an applet viewer to show a web page, nothing happens because the applet viewer isn't able to do it. When an applet wants to communicate with a browser, it calls the getAppletContext method. This method returns an object of type AppletContext. An applet can access only two areas of the browser: The status line The web page display area Both use methods of the AppletContext class. Using the getAppletContext method in conjunction with showStatus() lets you display a string in the status bar of a browser.

You can use this for error, link, or help messages. In practice, showStatus() is of limited use as the browser uses the status line at the same time. More often than not, the browser will overwrite your applet's message with its own, such as "Applet running ... ". For that reason, you should avoid using the status line for important information. The showStatus method may not be supported in all browsers. It should only be used to provide optional information to the user. The showDocument method tells the browser to show a different web page. The simplest way to do this is to call showDocument() with one argument or string - the URL you wish to display.

The one-argument form of showDocument() means that the new web page opens in the same window as your current page and so displaces your applet. To return to your applet, the user must select Back. You can get the browser to show the document in another window by using a second parameter in the call to showDocument(). showDocument(URL, string) The two-argument form of showDocument() lets you specify the window or frame where you want the document displayed. The term frame refers to an HTML frame within a browser window and not to AWT's Frame class. The second argument of showDocument() can have a number of optional values. Using "_self" is the same as using one argument - the document is displayed in the current frame. showDocument(myURL, “_self”) If you wish to display a document in a new unnamed top-level window, append "_blank". showDocument(myURL, “_blank”) Using "_top" displays the document in the top-level frame of the applet's window.

Page 238

Go To INDEX

showDocument(myURL, “_top”) The "_parent" argument uses the parent frame of the applet's window for display purposes. showDocument(myURL, “_parent”) When you use any other string as your second argument, the document will be displayed in the frame of that name.

Handling Parameters

With Java applications, you can pass parameters to your main() routine by using arguments on the command line. Applets, however, don't use the command line. Instead, they use parameters from a HTML file containing the <APPLET> tag. To set up and handle parameters in an applet, you need: A special parameter tag in the HTML file Code in your applet to handle parameters Applet parameters come in two parts, a name and a value. For example, you can set the color of text in an applet by using a parameter with the name color, and the value blue. In the case of animation speed, for example, you can use a parameter with the name speed and the value 5. Once you've decided on the names and values you want, you can then mark each parameter with the <PARAM> tag in the HTML file containing the embedded applet. The <PARAM> tag goes inside the opening and closing <APPLET> tags. Let's look at some HTML code for handling parameters.

This example defines two parameters for the MyApplet applet. Here, the name of the first parameter is title and its value is the text string "Cool Site". The next parameter specifies the font as Times Roman. Parameters are passed to an applet when it is first loaded. Parameters are generally accessed by using getParameter() in the init method of the applet. The init method is used for whatever initializations are needed for your applet.

It is called by the system when the applet is first launched. The getParameter method takes only one argument - a string containing the name of the parameter you're looking for. This method will return a string containing the corresponding value of the parameter. Suppose you want to get a font parameter's value from your HTML file. To access this value, you need to include the following code in your init method:

Page 239

Go To INDEX

string thisFontName=getParameter("font"); Parameter names specified in the HTML file and parameter names used in the getParameter method must match exactly. If, for example, a HTML file contains <PARAM NAME="font"> but your applet code contains getParameter("FONT"), then the font parameter won't be passed on. If an applet's parameter has not been specified in the corresponding HTML file, the getParameter method returns null. To handle such cases, you should test for a null parameter. If a parameter is missing, you can then add a reasonable default: string thisFontName=getParameter("font"); if (thisFontName==null)thisFontName = "Courier"; The getParameter method always returns strings. So if you want a parameter to be some other object, you have to convert it yourself. For example, if you specify an integer parameter such as size in your HTML file, you will need to parse it, and assign it to an integer variable in your applet. Here you assign the size parameter to an integer variable called rightSize.

If no size is specified in the HTML file, then a default size of 10-point type is set. In this line you parse the parameter size, converting it from a string to an integer. Supplying applet parameters makes it easy for users to modify your applet's functionality and appearance to use it in their own home pages. Adding a few lines to a HTML document is much simpler than having to change and recompile source code.

System properties

Java stores information about your system in a Properties object that is part of the System class. Applications would normally have complete access to this information. Applets have only restricted access to it. The level of access to the System properties is controlled by the checkPropertyAccess method in the SecurityManager class. There are fifteen standard properties in the system properties list. Certain sensitive values are hidden from "untrusted" applets. Using Netscape Navigator or the applet viewer, you can read ten system properties from within an applet. You simply call System.getProperty(key) on the property you are interested in.

This code, for example, would allow an applet to discover the operating system's architecture. Using os.name and os.version keys enable applets to read the name and version of the operating system. os.arch x-86 os.name – Windows 98
Page 240 Go To INDEX

os.version – 4.0 Other system properties that an applet can access include java.version, java.class.version, java.vendor, and java.vendor.url. java.class.version – 45.3 Less sensitive properties such as platform-dependent file, path, and line separators can also be read by applets. file separators: / or \ path separators: : or ; line separators: \n or \r\n With most browsers, applets cannot access sensitive information such as a username, a user's home directory, or current working directory.

Applets are also denied access to java.home, the directory Java is installed in, and to java.class.path.

You cannot hide the ten system properties from applets loaded into Netscape Navigator. For security reasons, Netscape Navigator doesn't read or write to any files, including the ~/.hotjava/properties file. The tilde ~ symbol is used on UNIX systems to refer to your home directory. If you install a web browser on your E: drive and create a top-level directory named .hotjava, then your properties file is found in E:/.hotjava/properties. You can prevent applets loaded into the applet viewer accessing your system, by redefining properties in your ~/.hotjava/properties file. If you add os.name=null to this properties file, for example, the name of the operating system will be hidden from applets. Applets loaded into Netscape Navigator can't read system properties other than those that can be read by default. However, if you add user.name.applet=true to your ~/.hotjava/properties file, applets loaded into the applet viewer will be able to read the username. Applets loaded into Netscape Navigator cannot read or write files. However, Sun's applet viewer allows applets to read and write files that are named on the access control list for reading or writing. The access control list is empty by default. You can allow applets to read or write to a file by setting the relevant properties in your ~/.hotjava/properties file. For example, if you wish to allow an applet to write to myfile in mydir, you name it explicitly:

Page 241

Go To INDEX

acl.write=/mydir/myfile

Applet-to-applet communication

If multiple applets are contained in a web page, applets can communicate with each other. Inter-applet communication is possible using static variables of a shared class. There are a number of possible solutions for inter-applet communication, including JavaScript-assisted solutions and server-based solutions. However, you can invoke inter-applet communication completely within Java using the AppletContext and static variables. If you want to have several different applets on a web page, all you have to do is include several applet tags in the HTML file. The browser will then create an instance of each applet for each corresponding applet tag. Some browsers may not allow multiple applets on the same web page. When a security hole was discovered, Netscape Navigator 3.0 deactivated this feature. Netscape Navigator 4.0 will restore this feature. How do you communicate between applets appearing on the same web page? What if you want a change in one applet to affect another applet in some way? Using the applet context is the best way to access different applets on the same page. Let's say you want to get information about all the applets on your web page. The getApplets method returns an Enumeration object with a list of the applets on the page.

Iterating over the Enumeration object enables you to access each element or applet in turn. Calling a method in a specific applet is slightly more involved. To do this, you give all your applets a name, and then refer to them by that name inside the body of code for that applet. To give an applet a name, you simply use the NAME parameter in your HTML file.

To get a reference to another applet on the same page, you use the getApplet method from the applet context with the name of the applet. You can then refer to that applet as if it were just another object - set its instance variables, call methods, and so on. In this code sample, you use the getApplet method to get a reference to the recipient applet.

Page 242

Go To INDEX

Once you have that reference, you can then call methods in that applet as if it were just another object in your environment. Assuming the MyApplet class has an updateData method, you can, for example, tell the recipient applet to update itself using information available to the current applet. Naming applets in a HTML file and then referring to them by using getAppletContext() and getApplet(), facilitates applet-to-applet communication. This helps provide uniform behavior for all the applets on your web page.

8.3. Networking • URL Objects

A Uniform Resource Locator (URL) is a reference to, or the address of, a resource on the Internet. An example of a URL is as follows: http://java.sun.com/ This particular URL addresses the home page hosted by Sun Microsystems. All URLs have two main components: The protocol identifier The resource name In the example quoted, http is the protocol identifier (Hyper Text Transfer Protocol), and //java.sun.com/ is the resource name. The colon (:) separates the two components and the trailing slash (/) is a shorthand for a default file depending on the host (it may be /index.html). In the context of our discussion of the Java language, the term URL may refer to one of two concepts. A URL address is the location where a web resource resides. A URL object is an instance of the URL class in a Java program. HTTP is not the only protocol used to access resources on the Internet. Other protocols include FTP (File Transfer Protocol), Gopher, and News. The URL address provides the complete location of the resource on the Web. While the format of the resource name varies with the protocol used, generally the resource name contains one or more parts. The URL parts are Host name (name of host machine) Information field Port number (to connect to) Anchor indicated by a hash# character (reference to a specific location within a file)

Page 243

Go To INDEX

For some protocols, the host name and the file information are required, but the port number and anchor reference are usually optional. The exact content of the information field is both protocol- and host-dependent. For example, the information field may indicate the path name to a file. The default port number for the protocol is supplied if none is specified (HTTP is 80). The anchor reference is not technically part of the URL. Resources other than hypertext documents can be accessed with URLs. A resource can be something as simple as a file or directory. It can also be a reference to a more complicated object such as a query to a database or search engine. The class named URL is contained in the java.net package. Your Java program can construct a URL object, open a connection to it, and read to and write from it. Let's look at how to create a URL object. The easiest way to do this is to create it from the string that forms the URL address. Here is an example of this in a Java program.

This is creating an absolute address which gives you all the information necessary to reach a resource (in this case, to reach the Java web site hosted by Sun Microsystems). While a URL object always refers to an absolute URL, it can be constructed from an absolute URL, a relative URL, or from URL components. A relative URL contains only enough information to reach the resource relative to (or in the context of) another URL. Suppose that you have already created a URL for http://www.gamelan.com and you know the name of a file at that site, namely network.html. You can create a URL for that file by specifying the filename in the context of the original Gamelan URL. The code for this is now shown in the graphical area.

Additional constructors allow you to specify a URL from its components, which is useful if you do not have a complete string. An example of this would be when you are letting users use the mouse to select the protocol, hostname, port number, and filename. This is now shown in the graphical area.

The URL class provides several accessor methods that parse the URL for you. The getProtocol method returns the protocol identifier component of the URL. The getHost method returns the hostname. The getPort method returns the integer port number. The getFile method returns the information field of the URL. The getRef method returns the anchor or reference field of the URL. Not all URL addresses contain all these components. This is particularly true of HTTP addresses, which are probably the most commonly used URLs. The accessor

Page 244

Go To INDEX

methods can be used to get information about the URL regardless of the constructor used to create it. Having successfully created a URL, you can call the URL's openStream method to get a stream. This enables you to read from that connection. The openStream method returns a java.io.InputStream object. If you need to do more than just read from a URL, you can connect to it by calling openConnection() on the URL. Connecting to a URL means you are opening a connection to the remote object referred to by the URL. In the following example, shown in the graphical area, a connection is opened to the search engine site named site.

The openConnection method tries to create a new URLConnection, initializes it, explicitly connects to the URL, and returns the URLConnection object. A MalformedURLException occurs if the new URL fails. If the site search engine is down, then the openConnection method throws an IOException error. The URLConnection class contains a number of methods that let you communicate with the URL over the network. It is most useful with HTTP addresses. Most URL protocols allow you to read from and write to a URL through a URLConnection object. You may find that reading from a URLConnection, rather than reading directly from a URL, is more useful to you. This is because you can use the URLConnection object for other tasks at the same time, such as writing to the URL. The URLConnection class also provides methods to get information such as the content type of the resource, or HTTP header information sent with the resource. Let's now look at writing to a URL, often known as posting to a URL. Many HTML pages contain forms. These are buttons, fields, listboxes, and other GUI objects that allow you to enter data and use your web browser to write the data to the URL over the network. At the server end a Common Gateway Interface (CGI) script reads the data, processes it, and sends you back a response, usually in the form of a new HTML page. Many CGI scripts use the POST method for reading data from the client, often reading from their standard input. Some server-side CGI scripts use the GET method to read your data. However, the POST method is rapidly making the GET method obsolete because it's more versatile and has no limitations on the amount of data that can be sent through the connection. Both POST and GET are HTML form methods, not Java methods. Your Java programs can be designed to interact with CGI scripts on the server side. They just need to be able to write to a URL, to provide data to the server. Writing involves several steps: Creating a URL Opening a connection to the URL
Page 245 Go To INDEX

Getting an output stream from theconnection - this is connected to the standard input stream of the server's CGI script Writing to the output stream Closing the output stream One of the most important uses for writing to a URL is searching. A URL may require detailed query data to be supplied. Your Java program can interact with the server CGI script to carry out a long series of searches.

Opening streams to the server

Input and output are an integral part of computer programming. Every computer language must have a way of dealing with I/O. Data can be described as flowing from the input through the computer to the output. A stream is nothing more than a flow of data. Input streams direct data from the outside world (for instance, the keyboard) to the computer. Output streams direct data towards output devices, such as a computer screen, file, or printer. The java.io package defines over twenty stream classes that you can use to control the flow of data in your programs. Apart from basic I/O, these classes provide a whole range of functionality. Bytes are not the only data types you can send with Java. You can send whole objects across a stream. Java also allows you to parse data from a stream. You can create and manipulate disk files and directories. You can communicate between threads using pipe streams. The java.io classes that provide basic stream input are: InputStream BufferedInputStream DataInputStream FileInputStream InputStream is the class that provides the basic functionality for all input streams. The basic method for getting data from any InputStream object is the read method. public abstract int read () throws IOException The code shown reads a single byte from the input stream and returns it. A blocking read is executed using this method, which means that the program waits for the next byte of data to arrive. When the stream reaches the end of stream, the method returns -1. The advantage of buffered streams is that they help speed up your programs by reducing the number of system reads and writes. The BufferedInputStream class reads data in blocks into a buffer. Subsequent reads can access the data directly from the buffer. You provide the buffer size when you create the class. The DataInputStream class is a stream filter for the DataInput interface. It reads primitive Java data types from an input stream in a machine-independent manner. The constructor is now shown. public DataInputStream (InputStream in) The last basic input class currently available is FileInputStream. This class allows you to read files. You can create file streams from a filename string, a file instance, or a special file descriptor. The java.io classes that provide basic stream output are

Page 246

Go To INDEX

OutputStream BufferedOutputStream DataOutputStream FileOutputStream PrintStream An output stream is a recipient of data. OutputStream is the class that provides the basic functionality for all output streams. The most basic method of an OutputStream object is the write method. The code shown writes a single byte of data to an output stream. public abstract void write (int b) throws IOException The BufferedOutputStream class writes data into a memory buffer. The contents of the buffer are written to disk when the buffer is full, the buffered output stream is closed, or when flush is called. You provide the buffer size when you create the class. The DataOutputStream class is a stream filter for the DataOutput interface. It writes simple data types. The constructor is now shown. public DataOutputStream (OutputStream out) The fourth basic output class is FileOutputStream, which allows you to write data to files. As with FileInputStream, you can create file streams from a filename string, a file instance, or a special file descriptor. The fifth basic output class is PrintStream. PrintStream provides for printing values and objects as text output. PrintStream uses: Write() to write data tothe stream Flush() to flush data from the stream CheckError() to flush the stream and flag errors Print() to print data in text form Println() to print a line of data (and a line separator)in text form Close() to close the stream Let's briefly look at some of the more advanced features available in the Java stream classes. One major aspect of streams is that you can chain one stream to the end of another. You can achieve this using the FilterInputStream and FilterOutputStream classes. You can read from and write to arrays of bytes using the ByteArrayInputStream and ByteArrayOutputStream classes. A ByteArrayOutputStream is an array of bytes that continually grows to fit the data that is stored in it. The ability to stream arbitrary objects became available with Java's Remote Method Invocation. The ObjectInput and ObjectOutput interfaces define methods for reading and writing any object. The ObjectInputStream class implements a stream filter for the ObjectInput interface. Similarly, the ObjectOutputStream class sets up a stream filter that allows you to write any object to a stream, as well as any primitive type. The StreamTokenizer class allows you to treat a stream like a group of words and parse the data. It works in a similar
Page 247 Go To INDEX

way to the StringTokenizer class for strings. StreamTokenizer implements a simple dictionary scanner that breaks up a stream of characters into a stream of tokens. The File class lets you manipulate files and directories on the local system. With the PipedInputStream and the PipedOutputStream classes, you can link an input stream to an output stream directly. A piped input stream is the receiving end of a communications pipe. Two threads can communicate by having one thread send data through a piped output stream, and having the other thread read the data through a piped input stream. Let's examine a Java application (as opposed to an applet) that reads a file from a remote server. It first connects to the server using the server's URL. It then opens an input stream to read the contents of the file and display them.

A number of points are worth making about this ReadFile program. A URL object called url is created and this is set to an absolute address. The absolute address refers to file test.txt on the remote host javaworld.com on the World Wide Web, running protocol HTTP. On the next line in the example program, a BufferedReader named is defined. This is setting up the input stream. BufferedReader is a class in the java.io package. It reads text from a character-input stream, buffering characters so as to provide for the efficient reading of characters, arrays, and lines. You will notice the use of the openStream method on the same line of code. This gets an InputStream to the object referenced by the URL. The reader is InputStreamReader. InputStreamReader is a class in java.io. It acts as a bridge from byte streams to character streams, reading bytes and translating them into characters according to a specified character encoding.
Page 248 Go To INDEX

Without buffering, each read request made of the reader would cause a corresponding read request to be made of the underlying byte stream. To avoid this inefficiency, BufferedReader is wrapped around the InputStreamReader so that the input is buffered.

Socket Programming with Java

You can communicate over the network at a relatively high level using URLs and URLConnections to access resources. However at times you may require a lower level network communication, especially when you want to develop a client/server application. In client/server applications, the server does back-end processing such as stock control or processing database queries. The client displays the database query results or stock prices to the user. Communication between client and server normally needs to be very reliable. No data can be dropped in transit. Data must arrive on the client side in the same order that it was sent by the server. A protocol must provide a reliable point-to-point communication channel for client/server communications. A socket is one end of a two-way communication link between two programs running on a network. A socket uniquely identifies a communications link. No two applications can bind to the same port on the same machine simultaneously. Many clients can connect to a single server through separate sockets. Two protocols are used in socket programming - UDP (User Datagram Protocol) and TCP (Transmission Control Protocol). Technical information is available on the UDP protocol in RFC 768 and on the TCP protocol in RFC 793. You can download these RFCs from ftp://ds.internic.net. A server application usually listens to a specific port, waiting for connection requests from a client. When such a request arrives, the client and the server make a dedicated connection over which they can talk. During the connection process, the client is assigned a local port number, and binds a socket to it. The client can now write to the server and get information from the server by reading from it. The server needs to get a new local port number so that it can continue to listen for connection requests on the original port. A socket is bound to the server's new local port. The client and the server must be agreed on the protocol for transferring information. Let's look at socket programming using the UDP protocol. UDP is sometimes compared to communicating with postcards, because messages are usually restricted in size and replies are not guaranteed. UDP is generally branded as unreliable. Making allowances for unreliability is the responsibility of the programmer. This can mean programming clients to retransmit requests and to display error messages. UDP is connectionless and message-oriented. UDP does not have the overheads associated with reliability, such as datagram acknowledgment. So UDP is a good choice for applications in which discrete messages (datagrams) are sent. A discrete message could mean a single query from a client invoking a single response from a server. Time-dependent data is particularly suited to UDP. UDP programming can be broken into a number of tasks: Creating a properly addressed datagram to send
Page 249 Go To INDEX

Setting up a socket to send and receive datagrams Inserting datagrams into a socket for transmission Waiting to receive datagrams from a socket Decoding information from the datagram Creation and decoding of a datagram is achieved by means of the DatagramPacket class in the java.net package. You use the following constructor (shown graphically) to create a datagram to send to a remote system.

In the code, you need to replace ibuff with the name of the buffer for the array of data. You replace length with the exact number of bytes you want to send. In the code, iadd takes the IP address of the intended recipient. You fill the destination port number where iport is indicated. Next, a UDP socket is created making use of the DatagramSocket class. Two constructors are available. One allows the system to assign an unused port dynamically. The other allows you to specify a known port to be the socket. The two constructors are shown in the graphical area.

For both UDP and TCP protocols, superuser privileges are required to bind ports below 1024. Well-known ports such as FTP, Telnet, and HTTP are assigned to ports below 1024. A named datagram can now be inserted into the socket port for transmission using a DatagramSocket method.

You receive a datagram packet using the function shown in the graphical area.

Because UDP datagram delivery is unreliable, the receive method may never return anything. The receive method of the class DatagramSocket blocks until a datagram is received, so the program can hang. This dilemma can be addressed using threads in the program. The TimeCompare class uses different threads of execution, and the application can make it interrupt the program after a certain length of time. Once communication through the UDP socket is finished, that socket should be closed. public synchronized void close () After a datagram has been received, you can read the data. Other information regarding the message itself is also available through DatagramPacket accessor functions.

Page 250

Go To INDEX

The getLength method obtains the number of bytes occupied by the data portion of the datagram. The getData method obtains a byte array containing the data received. The InetAddress object identifying the sender is provided by the getAddress method. The getPort method indicates the UDP port used. Let's now look at socket programming with the TCP protocol. TCP sockets are a powerful programming facility and enable you to develop quickly your own custom client/server applications. As a protocol, TCP is very reliable and is much more often used than UDP for socket programming. In client/server applications, a centralized service waits for various remote machines to request specific resources. This service handles each request as it arrives. For many application protocols, you can use the Telnet application to connect to the service port and then manually emulate a client. As with UDP, client applications must obtain or bind to a port to establish a socket connection. Four items of data are needed to set up a TCP connection: The local system's IP address The local application’s TCP port number The remote system's IP address The remote application's TCP port number The java.net package contains two main classes used for TCP socket programming. These are Socket for the client side and ServerSocket for the server side. The Socket class is used for ordinary two-way communications. It has four constructors, which are now illustrated graphically.

Page 251

Go To INDEX

In the constructors, host or address is replaced with the address of the remote host. Port refers to the remote port number. The boolean parameter is used to negate the default protocol, which is streambased like TCP. In other words, stream set to false means that a protocol such as UDP is used instead. A socket connection (named conn) can be opened as follows.

The hostname and port number are specified. The Socket class has methods that allow you to read and write through the socket connection. These are the getInputStream and getOutputStream methods. Examples of creating buffered input and output streams for reading from and writing to the socket are now shown in the graphical area.

Once you have finished with the socket connection, it is necessary to close it. Continuing with the example variable names, the code for closing the input and output streams and the socket is now shown.

The ServerSocket class listens for connections incoming to the server. It creates a Socket object for each new connection. The server socket must have a port number to listen on. Here are the two constructors, shown graphically.

If you pass a port number of zero, the system will assign the port number for you. The second constructor shows a connection backlog implementation. If many clients connect to a server at once, the number of connections yet to be accepted is the backlog. A limit can be set for this backlog. The accept method is used to accept incoming socket connections. Here is a simple way of creating a server socket called sconn bound to port 5555, listening for connections, and accepting any connections.

If no connections are pending, the accept method blocks until there is a connection. Performing accept() in a separate thread is a way of preventing your program from
Page 252 Go To INDEX

blocking completely while waiting. The methods for finding out the address and port number for your server socket are now shown.

This is the method for closing a connection. Public void close() throws IOException; This is how it is achieved in the case of connection sconn. sconn.close();

A simple client/server application

A Java client/server application has a program running on the client side, which communicates with a program on the server side. Let's look at an example of TCP socket programming, beginning with Client.java, which implements a client application. Then we deal with Server.java, which implements a server application. The code for Client.java is shown in the graphical area.

A number of points are worth noting about this code. A socket connection is established using the Socket class and it is called conn.

Page 253

Go To INDEX

The constructor arguments describe the machine on which the server is located and the port number on which the service is provided. In this case, we are connecting to a server on the local machine using InetAddress.getLocalHost(). After connecting to the server, an input stream is opened using the getInputStream method. InputStreamReader is a class that reads bytes and translates them into characters. BufferedReader is wrapped around the InputStreamReader so that the input is buffered. The constructor for the InputStreamReader object takes the feeder InputStream object as an argument and establishes the connection automatically. In the same way, the constructor for the BufferedReader object takes the InputStreamReader object as an argument. Input is read and buffered until the end of stream is reached. The output of the server is read using the is.readLine method of the BufferedReader class.

Finally, the conn connection is closed by conn.close(). Now, let's present the code for Server.java. The most relevant parts of Server.java are now described.

Page 254

Go To INDEX

Using the ServerSocket class, a Socket object called server is set up to listen for incoming connections on port number 4000. The server then waits for a connection from clients by issuing the accept method call. This method blocks the current thread until a client has made a connection. When the connection has been made on port 4000, accept() returns a new Socket object. Then an output stream is opened, using the getOutputStream method, for sending a message to the client. This in turn is wrapped in the OutputStreamWriter, and the writer is given the name os. "Server says 'Hello'." is sent to the client using the write method. To make sure that the message has been written out of internal buffers to the connected stream, the flush method is invoked. Finally, the conn connection is closed by conn.close().

8.4. Server Issues • Java server product family

The Java Server product family is an emerging suite of Sun Java products specially designed for the server side of client-server applications. The applications are designed to reside on the Internet, corporate intranets and extranets, and new Network Computing environments. Four products have been announced: Java Server Toolkit Java Web Server Java Servlet Developers Kit (JSDK) Java NC Server These products are all based on the Java Server application program interface (API). The Java Server API defines a cross-platform, extensible framework for the creation of network-centric server solutions, such as web servers and proxy servers. The Java Server product suite also contains the Java Servlet API. The code that services a request

Page 255

Go To INDEX

is known as a servlet. A servlet is the server side equivalent of an applet, but it does not have a graphical interface. Java Server has been shipped in an alpha, beta, and now JDK 1.1 version. To use Java Server, you need to have a server environment as well as a client environment from which to run administrative tools. You can download a version of the Java run-time environment from Sun's web site to suit most platforms. On UNIX, it is possible that you will want some special support to run Java Server automatically as the default web server. Java Server is not currently supported on the Macintosh, but this will be rectified with JDK 1.1. The client environment for Java Server needs the administrative applet. The currently released versions of Netscape Navigator (3.0), Microsoft Internet Explorer (3.0) and AppletViewer, all of which are JDK 1.0.2 compatible, support the administrative tools. Older web browsers do not. The Java Server Toolkit consists of the Java Server API, a core set of servers, tutorials, and examples. The Java Server Toolkit can be used to create web servers, mail servers, print servers, proxy servers, and boot servers. JavaSoft has developed a web server written completely in the Java programming language, using the Java Server Toolkit. This is Java Web Server. Web site developers can use the Java Web Server to bring dynamic content to their pages. Java Web Server supports the Java Servlet API. With the help of the Java Servlet Developers Kit (JSDK), you can create Java servlets to enhance web sites. JSDK includes a servlet engine for running and testing servlets. It also contains the java.servlet.* sources and all the API documentation for java.servlet.* and sun.servlet.*. The Java NC Server will be used for developing network computing environments. Java NC Server will support JavaOS and the recently introduced HotJava Views user productivity environment to provide solutions for deploying network computers across the enterprise. Let's take a closer look at the Java Server generic framework, and how a HTTP server is built using it. The architectural framework provides a generic set of classes for implementing connection-oriented servers. These servers manage multiple threads for handling client connections. A HTTP server acceptor thread uses the framework to listen to the incoming HTTP requests. In this way, a HTTP request is dispatched by the acceptor to the HTTP server handler thread. The handler thread then carries out authorization and name translation, based on server administration settings. The handler finds out which servlet, if any, will be able to handle the request. Servlets are object bytecodes that can be dynamically loaded off the Internet. However, servlets do not have a graphical interface of their own, and are termed faceless objects. Servlets have to conform to a specific interface. Like applets, they can be identified by a URL address. Sun is still developing Java Server. It is planned to integrate Java Server with existing operating system facilities as much as possible. Integration with standard network management and security tools is foreseen. Network management tools such as JMAPI, SNMP, and CMIP are candidates for integration. It is not anticipated that administrators will need to modify administration files, which are properties format name-value pairs. Servers and servlets make use of an EventLog log file for messages of arbitrary format. Access to files is controlled using access control lists (ACLs) and these are in common log file format.
Page 256 Go To INDEX

Java server security

Web site services shared by many people need to be defended against a variety of security problems. Java Server allows you to defend your web site against two general forms of threat or attack: Alteration or theft of data Malicious code Theft can occur when data is in transit to or from the server. This is generally the work of eavesdroppers. Another aspect is theft within the organization, and the principal guard against this is access controls. Code received from outside is potentially malicious in intent. The servlet "sandbox" provides a safe environment in which servlets can be checked out. Every web site has a security policy that defines, however vaguely, "how secure this site needs to be". Some risks are inevitable and acceptable in every enterprise, but other risks are not. A "risk versus reward" tradeoff needs to be made. Web site security policy is administered by your web site administrator. You can never trust software security mechanisms on their own, since they can be overwritten. The bottom line is that your staff also need to be trustworthy. They need to be trusted not to violate your security or that of your customers. Java Server supports a variety of security mechanisms, and these can be grouped as: HTTP protocol features UNIX-specific server features Java-specific server features Let's view the various HTTP-oriented features. In these features, the concept of authentication occurs. This generally refers to verification, but it has a particular meaning in Secure Sockets Layer. Secure Sockets Layer (SSL) server authentication means that a server authenticates (validates) itself. It can also mean a client authenticating itself on request from a server. The browser users are guaranteed that the server they are addressing is, in fact, the one they think they are addressing. This is achieved through a third party Certificate Authority. SSL privacy protection is a protocol feature that allows for encryption of data sent to or received from the server. Encryption has to be approved by both the web site administrator and the user. It is most useful for protecting financial information and credit card numbers. Basic authentication refers to the basic verification of a username and password after they are entered into a login prompt or dialog box. Access control lists (ACLs) are part of the access control. Passwords in transit are susceptible to eavesdropping. Passwords stored in plain text on a server are also vulnerable. Digest authentication resolves the problem of passwords in transit. However, the server still needs to store the passwords in plain text. SSL Client authentication protects against attacks on the server's stored passwords but it is not yet supported. Realms consist of groups of users using a virtual host, application, or part of a given web site. Different realms can be given different sets of access privileges. So, two or more users named "Marie" can be different people with different sets of privileges,

Page 257

Go To INDEX

depending on the realm. Groups within a realm can be a way of efficiently distributing privileges. For instance, you might want to define a group "accounts", all of whose members can access accounting web pages. ACLs allow your web site administrator to limit user access to individual web pages or trees of web pages. ACLs assign permissions to users and groups. Users, groups, and ACLs are all within the scope of a realm. Now, let's describe the UNIX-specific server features. These are provided through an optional native code module. Java Server can be set up so that the "root" account is needed only when initially binding to the default HTTP server TCP port (80). All normal administrative tasks after that can be done without needing "root" privileges. Java Server supports a "UNIX" realm. This allows the existing UNIX user accounts to be used for access control, removing the need for double administration. Web pages belonging to UNIX users can be automatically exported through the Java Server. The remaining security features are those, which are Java-specific. provides administrative tools, which help avoid manual error-prone editing Administration pages are controlled by digest authentication, by default. HTTP over SSL (HTTPS) is available to protect administrative operations "active wiretapper" attacks. Java Server of text files. Additionally, from certain

The servlet sandbox protects the system from the potential danger posed by new servlets, whether supplied by third parties or developed on-site. By default, all servlets are untrusted. The Security Manager does not allow servlets to access network services or local files. A servlet which has been delivered in a signed Java Archive (JAR) file may be trusted and granted more privileges by the Security Manager. A digital signature on executable code means that the source organization "vouches for it" to some extent. It is no longer expected that servlets will be delivered unsigned, and if unsigned servlets are installed the system may grant them arbitrary privileges. Extension APIs in scripting languages or other languages like C can't support low level access controls even if they do allow digital signatures. Extensions built with 100 percent Pure Java can be prevented from maliciously altering data. So Java has a competitive advantage. An administrator needs to manage the security features for: Realms Access control lists Servlet sandbox Secure Sockets Layer The Users web page gives the administrator a choice of realms, each with different security privileges. The page allows users to be added, edited, or deleted from the realms. The users in a particular realm, for example adminRealm, can be listed. Once you have created a list of users, you can control their access using the Access Control web page. Access control lists allow control on access to particular web pages. ACLs may also be used by servlets. Users may be granted permission to GET a web page, or to POST from data to a servlet. The servlet sandbox is analogous to the applet sandbox used by web browsers.

Page 258

Go To INDEX

Server administrative tools are set up to recognize digitally signed JAR files. The Secure Sockets Layer provides three services: Integrity protection Authentication Confidentiality Integrity protection is the prevention of undetected changes to data as it is exchanged between clients and servers. The normal situation in SSL is that servers authenticate themselves, and clients do not need to do so. When a peer at the other end of a socket connection authenticates, its chain of X.509 public key certificates is presented to an object implementing the TrustDecider API. The object is placed in the AuthenticationContext. Some classes support secure storage of private keys, using encryption to ensure confidentiality. The most basic class is KeyStore, which defines the format of the storage.

Java servlets

JavaSoft has made available for immediate download the Java Servlet Developers Kit (JSDK). The Servlet API provides a simple flexible platform for writing the server side of a web-based application. This API is consistent across web servers. The Java Servlet API is officially a Standard Java Extension API. This means that while it is not currently part of the core Java framework, it will be made available as an add-on package. Implementations are available for other popular web servers, such as Apache, Stronghold, Netscape servers and Microsoft's IIS. One of the major performance features of servlets is that they do not require the creation of a new process for each request. Many servlets run in parallel within the same process as the server. Java servlets offer the best performance, better than C, regular CGI (Common Gateway Interface), and Fast-CGI. Servlets require only light-weight thread context switches. In other words, servlets can handle many client requests each time they are initialized, and spread the costs of that initialization over many methods. The client requests to that service can share data and communications resources, benefiting more from system caches. Servlets used with HTTP have a significant performance advantage over the C language and over both the regular Common Gateway Interface (CGI) and Fast-CGI approaches. Regular CGI requires heavy weight process startup and initialization on each request. Fast-CGI still involves heavy duty process context switching on each request. Taking advantage of the flexible Java Server architecture, the server divides up its work among several specialized core servlets: File Servlet Invoker Servlet Server Side Include Servlet Admin Servlet CGI Servlet Imagemap Servlet

Page 259

Go To INDEX

The File servlet makes available the standard document serving capabilities of Java Server. It includes a caching mechanism to speed up accessing of frequently-used files. It also recognizes files destined for the Server Side Include servlet and passes them on. The Invoker servlet invokes other servlets that are specifically requested in a URL. The Server Side Include (SSInclude) servlet handles the embedding of servlets within HTML documents. A file with an .shtml extension is parsed for a servlet tag when the server writes it out to the client. The servlet tag identifies the servlet and supplies name value pairs. The server loads the servlet, invokes it, and sends the output to the client at the point where the servlet tag was embedded. The Admin servlet allows administration of the Java Server through a GUI front end. The CGI servlet permits any program that utilizes the CGI 1.1 standard to operate under Java Server. Hence it is a gateway for the CGI 1.1 interface. The Imagemap servlet facilitates server-side imagemaps, using an extension of standard NCSA mapfiles. Text-only browsers are presented with a menu of links when an imagemap is presented. An optional double-quoted string can be added to the end of each line in the imagemap file. This string is used as a descriptive string in the menu generated for text-only browsers. Let's briefly look at the life cycle of a servlet, from loading to shutdown. Servlets may be loaded from remote directories as easily as from the local file system. While servlets are always dynamically loaded, an administrative option is usually provided by the server to force sharing or particular mappings. The server administrator could decide always to map certain kinds of client request to one servlet, for instance one which talks to a particular database. The administrator could specify that part of the client request must be the name of the servlet's as found in a servlets directory shared between servers. Servlets can be configured to filter the output of other servlets. Properly authorized clients can just specify which servlet is to be invoked, without administrative intervention. Servlets are activated by the server through an init call. Servlet writers may wish to implement their own init calls, to remove initialization from later requests, and ensure access to files and network services. After initialization, servlets can handle many requests if asked. Each client request generates one service call to the servlet, using service(). The service method has two arguments, the request to the servlet and the response from the servlet. A ServletException and IOException can be trapped. Servlet requests can be concurrent, allowing servlets to coordinate activities among many clients. Finally, the servlet is explicitly shut down by the web server, by calling the destroy method. The servlet's class may then become eligible for garbage collection. Now let's review typical applications of servlets. Two important groupings are: HTML-aware servlets HTTP-specific servlets Many servlets will directly generate HTML-formatted text. A scripting language is not needed for servlets to generate HTML. It is easy to do with standard internationalized Java formatted classes such as java.io.PrintWriter. Web pages can be preprocessed using SSInclude core servlet functionality and .shtml files. The servlet tag in an shtml file can be used to insert formatted data. Examples of this could include survey data, customer responses to an online magazine, or the output
Page 260 Go To INDEX

of a web or database search. HTTP-specific servlets may support any HTTP method, such as GET, POST, and HEAD. These servlets can redirect requests to other locations and pass on HTTP error messages. They can get access to parameters passed through standard HTML forms, including the HTTP method to be performed and the URL. Applications can include processing purchase orders with credit card data as part of an order entry and inventory database system. It is possible to exploit the power of servlets to handle multiple requests concurrently in collaborative applications such as online conferencing. By defining a community of active agents, it is feasible to share work by passing data. Servlets can forward requests to other servers, to partition a service by type of task or customer. Java servlets are a vital part of web-based applications. Many organizations use multi-tier applications. Three-tier applications include many servers (the standard client/server model is two tier), where the servers exchange data between each other. The first tier could include any number of Java-enabled browsers. The second tier of the model consists of servlets, which incorporate the particular business rules and logic of the application. The third tier is comprised of data repositories and is accessed using relational database interfaces. A final example of a servlet application could be a web publishing system. An enterprise could possess a large collection of historical and current data, perhaps sales figures covering a wide geographical area. The data needs to be presented in easily understood formats, in response to current applications for the data.

Mainframe issues

Java offers solutions that fit into an environment where mainframes and machines from different vendors still perform vital functions. The features of Java that offer significant advantages are: Thin-client functionality Network-centric focus Central server upgrade Platform independence Security and reliability The thin-client approach is the opposite of full desktop functionality. In place of a platform-specific client requiring many megabytes of hard disk memory, a thin client requires connection to a server for its functionality. The network-centric concept means that Java applications, which are developed in modules, can be delivered over the network as functions are required. This is economical and efficient for the user. With networkcentric computing, you can simply upgrade the software on the server. This saves having to upgrade every single user client. In a sense, this represents a return to the centralized model of the mainframe era. As you know, Java is inherently a multi-platform programming language. The Java Virtual Machine (JVM) converts byte code, on-the-fly, to the correct binary for whatever machine it is running on. Java's security features and reliability are important advantages. Software protection mechanisms, including encryption and authentication, build confidence for

Page 261

Go To INDEX

future applications. The reliability of Java in maintaining connections is a great improvement over static HTML, which requires constant reconnection. To permit Java to connect with the range of different vendors' naming services, it was necessary to create a special Java interface. Let's now look at JNDI, the Java Naming and Directory Interface. JNDI enables seamless connectivity to heterogeneous company naming and directory services. With JNDI, Java applications can store and retrieve named Java objects of any kind. JNDI provides methods for performing standard directory operations, such as searching for objects using their attributes, and associating attributes with objects. Different naming and directory service providers can be plugged in seamlessly behind the common API of JNDI. Java applications can thus take advantage of information in a variety of legacy applications and systems. Examples of other directory and naming services that can coexist with JNDI include LDAP, NIS (YP), NDS, and DNS. One application of Java is to make available real-time business data such as stock prices and company results. Raw data can be gathered from many data sources, sent across the Internet, and viewed using appropriate graphics. An example is being able to locate numerical rows and columns, manipulate them in a familiar spreadsheet format, and then model various business scenarios. Applix, Inc. has produced the Anyware product, which does just that. Many banking and insurance companies are actively considering Java to implement future online customer transactions. Corel Corporation has launched its Corel Office for Java product. This offers a word processor, spreadsheet, and extensive business graphics. Corel developers have exploited Java's cross-platform capabilities and portability. Users of Corel Office for Java can pick and choose the features they wish to download. The accessing of databases via multi-megabyte, platform-specific clients has now been recognized as problematic in terms of ease and flexibility, and costly for the organization in terms of software maintenance. There is a need to integrate database access into the expanding Internet and corporate intranets. One company addressing the database access issue with Java is Infospace, Inc. Infospace came up with a 3D business graphics package based on Javanamed WebCharts. Their SpaceSQL product became the first thin-client database access tool for the Web, written entirely in Java. Once downloaded, the SpaceSQL client runs inside the browser, independently of the server. The idea of being able to make travel reservations from any desktop or laptop computer is no longer a dream. Via World Network is launching a Java-based product offering desktop travel planning and booking. Via cites Java's overall reliability, platform-independence, and object-oriented design as major factors in choosing Java. Mainframes, particularly those reliant on IBM's Systems Network Architecture (SNA), have played an important role in corporate computing for many years. Core business data, including a wide range of data warehousing, database, and related data-intensive applications, often reside on mainframe and midrange SNA systems. The issue is how to incorporate these critical legacy systems into web-based client/servers.

Page 262

Go To INDEX

OpenConnect Systems, Inc. is leading the way in giving legacy systems a whole new lease of life. Their product OC://WebConnect became the first Java-based SNA host access solution on the market. The product gives web-connected users access to the wealth of mission-critical data generally handled by mainframes. An advanced product OC://WebConnect Gold was also introduced to tackle security issues. The Gold product ensures a secure mainframe-to-desktop connection at the key levels in the protocol architecture. The competitive advantages of secure mainframe access are obvious. The focus is shifting away from both the mainframe and the desktop. Transparent information access via the network is the way forward. This is expected to yield large savings in software distribution and help-desk costs. The integration of the Web with existing legacy systems offers exciting new applications.

Page 263

Go To INDEX

9 JavaBeans
9.1. Components • Overview of JavaBeans
An important aim of any modern programming language is to have the ability to reuse previously developed software components. If you reuse software, it reduces development time and cost while increasing flexibility. You can more easily standardize the user interface by using common, reused components. You can purchase software from third-party vendors, in forms such as tool kits, and integrate it with your own software. A component architecture model for a language specifies how components are built, customized, and used. JavaBeans is Java's component architecture model. The software components are called "Java beans", or simply "beans", while the model is called "JavaBeans". The emphasis during the design of JavaBeans was on the smaller software components likely to be distributed over networks. But JavaBeans also supports much larger software applications. Java beans can be GUI widgets, non-visual functions or services, or larger applications. While you will find small, lightweight beans easy to build and use, larger beans are also possible. JavaBeans was designed to be simple to use, so you can develop and use beans manually, although you will probably use a visual builder tool instead. Many new features introduced in JDK 1.1 are used by JavaBeans, such as event handling, JAR files, remote method invocation (RMI), reflection, and serialization. You only need to understand the Java language and the JavaBeans API before being able to develop beans. In keeping with the Java philosophy, JavaBeans is a platform-independent component architecture model. Java beans are portable across multiple platforms, just like complete Java applications. An important feature of beans is that you can manipulate them using sophisticated, visual programming tools. This is because the JavaBeans API specification was developed in tandem with many leading builder tool vendors. Individual beans can be built, customized, and integrated with other beans and Java applications using these tools. JavaBeans is compatible with existing component architectures, like: IBM and Apple's OpenDoc Microsoft'sActiveX/OLE/COM Netscape's LiveConnect OMG's (Object ManagementGroup's) CORBA JavaBeans' APIs link to these platform-specific component architectures through bridges developed and supported by JavaSoft's industry partners. Java beans are subject to the same sandbox security model as normal Java applets or applications. This means that beans that are part of untrusted applets cannot access files on the user's hard disk or arbitrary network hosts. However, beans that are a part of a trusted applet, or a stand-

Page 264

Go To INDEX

alone application, will have access to files and other network hosts in the usual way. Java beans were designed to run well in a network environment. Java's Remote Method Invocation API will allow distributed Java applications, including beans, to communicate over a network. Java beans can utilize Java IDL and IIOP, which is an implementation of the Object Management Group's (OMG's) industry standard CORBA distributed object model. And beans can use Java's Database Connectivity API (JDBC) to access remote SQL databases. Although you write a Java bean like any other Java program, it will have several unique features. These features allow beans to: Be customized by you at design time Maintain their customized state, using serialization Interact with other beans, using events Be manipulated visually by builder tools Every Java bean has properties that determine its appearance, state, and behavior. Properties can include a bean's color, size, or labels, and various internal states too. You can customize a bean through the modification of its properties in a variety of ways: Through a scripting language, such as JavaScript Through get and set methods defined in the bean Using property sheets provided by a builder tool Using specially written customization code that is runt design time Bean developers can provide specialized customization code that is run at design time inside a visual tool. In some cases, the customization code for a bean may even be larger than the bean itself. Java separates the customization code from the bean code to improve download efficiency at run time. Java beans fire events to interact with other beans or Java code. The JDK 1.1 delegation event model is used for defining event handling in JavaBeans. When a bean fires events, it results in method invocations in registered event listeners. Beans that implement event listener interfaces can be registered to receive events from other Java program elements. Beans must make their events, methods, and properties available at design and run times to builder tools and other Java components. The process of revealing the methods, events, and properties of a bean is called introspection (or reflection). There is a default introspection mechanism available using standard design patterns, or naming conventions. Alternatively, beans can publicize their behavior explicitly using a special BeanInfo class. Java beans need to be able to save their edited states across different platforms. The automatic Java serialization mechanism allows beans to persist over time. Beans can, alternatively, use an external stream mechanism that allows them to have full control over the resulting data layout.

Persistence and Packaging

Page 265

Go To INDEX

If you customize a Java bean's appearance and behavior, you will want to have those changed properties stored in a non-volatile location for later retrieval. Persistence is the storing of a bean's internal, customized state for later retrieval. The stored state is said to be a bean prototype. Bean prototypes are also platform-independent in the same way that beans are platform-independent. Java beans can be stored across a wide range of data formats, including existing industry standard formats such as OLE or OpenDoc. The state of a stored bean is relative to its container type, so Java beans are often stored as a component of a larger non-Java parent container. For example, a bean could be stored as an Excel document embedded in a Word document. Java beans can choose to use the automatic Java serialization mechanism, which provides a method of storing out and restoring Java objects. Java uses a hash algorithm that produces a 20-byte serial number of the data fields and methods. The serial number is like a "fingerprint" that uniquely identifies the stored object. Objects to be saved automatically must implement the java.io.Serializable interface. You do not have to do anything else in order to save the bean after implementing this interface. In general, a Java bean will save all its public properties and the internal states necessary to retain its customized appearance and behavior. You must mark the object fields that are not persistent with the "transient" keyword. Fields that should be marked transient will include any references to other beans within a container or event listeners. It is the responsibility of the container itself to track and restore these transient references and connections. Static variables will also not be saved. You can implement a writeObject method in the bean to control what information is saved, or to append additional information to the stream. Then you can write a readObject method to read the information written by the corresponding writeObject method. Serialized objects are saved as ser files. When you alter or recode the bean, the stored prototype may or may not persist, depending on the type of change you initiate. You do not affect a prototype when you: Add new fields to the bean Add references to classes Remove references to classes Change a field's access modifiers You will change the bean prototype if you: Delete fields from the bean Change a variable's position in the class hierarchy Change a field's transient or static status Change a field's data type You package a Java bean into a single, compressed file together with its related images, help files, .ser prototypes, and classes. The packaged bean is then ready for use by builder tools, containers, and applications. Packaging is based both on the object serialization API and Java archiving (JAR) files, both introduced with JDK 1.1. JAR files collect together groups of related Java files in a compressed, zipped format. The

Page 266

Go To INDEX

compressed JAR file is much smaller, so has the advantage of optimizing transmission over networks. JAR files also simplify the packaging and unpackaging of beans because all related files are kept in one place. Several beans can be collected into a single JAR file if required. A JAR file usually contains a manifest file that describes the contents of the JAR file, in such a way that all the beans can be listed and individual beans can be retrieved. Beans should follow the standard naming conventions for packages and classes. If no manifest file is present, then all class and ser files are assumed to be beans. The JAR file has a set of class files that define the bean's or beans' behavior, and optional ser files that provide customized state information. Additionally, each bean has: An optional help file in HTML format Optional internationalization information Other related files, such as images and sound

Event Handling

Beans use events to communicate with other components or beans. For visual beans, events occur when a user interacts with the bean by, for example, clicking a mouse button, entering text, or pressing a key. But non-visual beans can also use events to interact with other software components in an application or applet. JavaBeans uses the JDK 1.1 delegation event handling model. Event sources fire events at registered listeners. The system invokes methods in the listeners that match the event types being fired. Events and their relevant information are represented as Java objects, and are all descended from java.util.EventObject. You can create new event types by extending java.util.EventObject, or one of its descendants. Each Java event has a corresponding listener method defined for it. Groups of logically related listener methods are gathered into listener interfaces. For example, mouse-generated event methods are collected into the MouseEventListener interface. Listeners implement a listener interface and are then registered on a bean to receive event notifications. Multiple listeners may be registered to listen for the same event from the same source - called multicast delivery. How a particular Java VM handles multicasting is implementation-dependent, so you cannot make any assumptions about the order that event listeners handle events. Beans can be both a source and the recipient of events. The events that a bean fires or listens to must be discoverable by external builder tools. The tool, at design time, must be able to modify events and the relationships defined between event sources and event listeners. You can use adapters to interpose between a source and a listener or set of listeners. Adapters are extremely useful in JavaBeans because they: Add greatly to the flexibility of the model Can help separate a GUI from application logic Can simplify event handling tasks Adapters can queue events for delivery to multiple listeners in a non-standard way. Adapters, for example, might queue event handling by multiple listeners in a strictly
Page 267 Go To INDEX

ordered fashion, rather than relying on the particular Java implementation. Alternatively, adapters can de-multiplex multiple event sources onto a single listener. De-multiplexing means to channel many sources onto a single listener, with each source causing a different method call on the target listener. Adapters can be designed to augment the information about an event, or the behavior of an event. Unwanted events can be selectively filtered by adapters for efficiency reasons. Also, adapters can be used to choose an appropriate listener from among a larger set to handle the event. A standard event handling method will receive only a single parameter - an object of the event type. However, you may construct events to suit the requirements of an external, non-Java target environment. In this case, the event handling method may contain an arbitrary parameter list. Visual builder tools should be flexible enough to handle such exceptional situations.

9.2. Working with Components • Properties

Reusable components, or beans, do not have all their behavior and appearance determined once and for all when they are created. Beans are flexible enough to allow their basic behavior and appearance to be modifiable by a bean user. The attributes of a bean that determine its modifiable behavior or appearance are called properties. Properties are discrete, named attributes. Properties can be modified by a user in a variety of ways. They might be modified through a scripting language. Or they might be presented in a builder tool's property editor. In fact, regardless of the medium through which a property is edited, it is eventually changed through calls to its accessor methods. Accessor methods are responsible for getting or setting the value of a property. All readable properties have a getter method of the form: public PropertyType getPropertyName (); This form is the standard design pattern for properties. Design patterns are standard naming conventions used to declare properties, methods, or events for automatic discovery. All writable properties, or modifiable properties, have a setter method of the form: public void setPropertyName (PropertyType p); Any property which has both a getter and a setter accessor method is a read-write property. Accessor methods defined according to a design pattern enable automatic introspection of a bean's properties. Properties do not just have to be simple values. Since the properties are changed by the bean itself, the property can be quite complex. Properties can be any Java primitive type, Java class, or array. Java supports indexed properties, where the value is specified by using an int integer value. Indexed properties have different design patterns to the other property types. Getter accessor methods for indexed properties are of the form: public PropertyItem getPropertyName (int i); Setter accessor methods for indexed properties are of the form: public void setPropertyName (int i, PropertyItem p);
Page 268 Go To INDEX

Boolean properties support a different getter accessor method, in addition to the normal form. This additional design pattern is of the form: public boolean isPropertyName (); Alternatively, boolean properties may just have the normal getter method form. Let's see an example of a simple property and how it is accessed and modified. Imagine you have bought a bean that displays personnel details in an attractive format. You may want to use the bean to speed up the writing of an application that allows managers to browse through employee records. Let's imagine that the bean will display an image file of each employee if the bean's Displayable property is set to true. The way to set the Displayable property to true is to call the bean's setter accessor method for the Displayable property. This call will be: PersonBean.setDisplayable (true); And to prevent images being displayed you would use: PersonBean.setDisplayable (false); You can test the status of the property using this sample method invocation: boolean viewable = isDisplayable (); There are more complex property types available for the advanced beans user. Firstly, bound properties notify other components of any changes that they undergo. They are said to be bound to that other component. If you modify a bound property, then that change is notified to other registered listener components using PropertyChangeEvent events. PropertyChangeEvent events contain the property name, along with its old and new values. Bound property listeners implement the PropertyChangeListener interface. The bean notifies the property's registered listeners after the change has been made by invoking their propertyChange method and passing a PropertyChangeEvent. The Java API contains a PropertyChangeSupport class for convenient programming of bound properties. Constrained properties are properties that undergo validation by another software component when changed. The attempted change may be vetoed by the listener, so the property should notify the listener of the change before modifying the property value. This veto is notified to the property's bean through a PropertyVetoException exception. Setter methods for constrained properties must allow a PropertyVetoException exception to be thrown. Components listening for changes to constrained properties must implement the VetoableChangeListener interface. The listener throws a PropertyVetoException exception if it disapproves of the property change. The setter accessor method, when it receives a veto from a listener, may take some appropriate action to change the value and renotify the listener. A handy VetoableChangeSupport class will track constrained properties, listeners, and exceptions. Some properties may be both bound and constrained. In this case, the bean should have the change validated by the constrained listener before notifying any bound listeners. But when a property's new value is equal to its old value, you should not call any listeners for that change.

Page 269

Go To INDEX

Introspection

You must be able to discover a reusable software component's behavior and appearance. For example, builder tools at design time will need to be able to discover a bean's properties, events, and methods automatically. This process of automatic discovery is called introspection. JavaBeans provides a low-level reflection mechanism for discovering all of a bean's, variables, methods, and interfaces. This low-level reflection is, in turn, used by a higher-level introspection mechanism. Both the reflection and introspection facilities are written as Java APIs, so a beans developer needs no additional scripting language or specification mechanism in order to publicize a bean's behavior. JavaBeans supports a simple specification mechanism for smaller beans, using design patterns. Design patterns are naming conventions that declare a property, method, or event to an introspector. All properties, events, and methods that a bean developer wants to expose are declared according to a standard design pattern. An introspecting program can reconstruct all of a bean's important elements by extracting information supplied with declarations. A property's type is discovered through the return type of its getter accessor method. For example, the property called listLength is of type int and this can be discovered by examining the return type of its getter method.

Properties are discovered when an introspector finds get and set accessor methods.An introspector might find a write-only property with just a set method, a readonly property with a get method, or a read-write property with a pair of get and set methods. Events can be introspected successfully when pairs of "add listener" and "remove listener" methods are found.

All public methods in beans are assumed to be accessible to external software components. There is another, more complex, option for providing information about a bean. You can provide explicit information using a special BeanInfo class provided with the bean. A BeanInfo class does not necessarily have to supply all relevant information about a bean. It may provide some explicit information, and then allow the rest to be introspected using design patterns. Java has an Introspector class that can analyze a bean, even if it has mixed BeanInfo and design patterns. The java.beans.Introspector class analyzes a bean class in a rigorous fashion. Firstly, it checks the bean for a matching BeanInfo class, which will be of the form BeanNameBeanInfo and which implements the java.beans.BeanInfo interface. If one is found, then all its information is extracted. If one is not found, then low-level reflection is used to introspect elements written according to the design patterns. Once a bean's class has been introspected successfully, the Introspector proceeds to introspecting its base class, and so on up the hierarchy. This mechanism ensures that beans that are subclassed from other beans will still inherit explicit information from the
Page 270 Go To INDEX

original class. Bean users can extend beans from an independent software vendor (ISV), and assume that the Introspector will retrieve all relevant properties, methods, and events. Property and event names will follow the standard Java conventions. The Introspector will automatically apply these naming conventions to event and property names extracted by the reflection API.

Customization

When you buy a bean from an independent software vendor (ISV), that bean will probably not do exactly as you want. The ISV will have given the bean default, generic behavior and appearance. But the bean will be flexible enough for you to adapt it to your precise requirements. The process of modifying a bean's default behavior is called customization. You have seen how beans have properties, and how those properties can be introspected by external software, especially builder tools. Properties and introspection allow beans to be customized. That customized state of a bean can then be stored using serialization or some other persistence mechanism. You can modify bean properties in a variety of ways, depending on the builder tool you use and the bean itself. Normally, the bean will export a set of properties which your builder tool will present as a user-friendly property sheet and editor. You will use the property sheet to examine the bean's properties and then open the editor to modify property values. Another way you may change property values is through a scripting language environment. The scripting environment may be executing the bean alongside other software components, perhaps in a browser. You will adjust a bean's property values by assigning new values in explicit statements in the scripting language. Whether you customize the bean in a builder tool at design time or through a scripting language at run time, it will result in calls to the property's getter and setter methods. Property editors are classes that allow the editing of properties, and can range from very simple to quite sophisticated. The simplest property editors might only allow the reading and writing of a string value representing the value of a property. More sophisticated editors may be GUI AWT components. Property editors can come from a variety of sources: With the standard JavaBeans API As part of a larger JavaBeans toolkit From the ISV supplying the bean With an application builder tool Each property must be linked to a property editor that matches its data type. All the primitive Java types are supplied with default property editors as part of the JavaBeans API. Additionally, the JavaBeans API also supplies property editors for the classes: java.lang.String java.awt.Color java.awt.Font Property sheets and scripts will work best for smaller beans. If you want to customize larger beans, then Java allows the ISV to provide a specialized customization
Page 271 Go To INDEX

class along with the bean. This specialized customization class, or customizer, will be an AWT component that guides you through bean customization. All customizers will inherit from java.awt.Component, so will have a Java GUI interface. Normally, a customizer will constantly update a bean's appearance as you select customization options. Customizers give bean developers great flexibility in choosing how to let users edit beans. Invisible beans are beans that have no GUI appearance of their own when run. They may form a smaller section of a larger application, or be a server-side program. But invisible beans may still have customizable properties. These properties can be customized in the standard way, using a GUI customizer or property editor.

9.3. Creating and using beans • Using the BeanBox

Java beans are intended to be manipulated at design time. So, you will need to customize and integrate them with other beans, before they are serialized for later use. You will typically test, customize, and integrate beans in a visual tool environment. The JavaBeans Development Kit (BDK), which you will use with JDK 1.1, contains a test container called BeanBox. The BeanBox is a simple, Java-written, test environment for beans. It allows you to manipulate your beans visually, edit properties, connect beans together, and save their customized states. The BeanBox consists of: A ToolBox area for displaying the names of available beans A BeanBox area for displaying and testing sample beans A PropertySheet for editing selected bean properties

Page 272

Go To INDEX

The PropertySheet window presents the selected item's properties for editing. In this case, the name of the panel in the BeanBox is panel1, which you will want to change. Here you see the name changed to "Test Bean".

The BeanBox window contains a File option for: Saving beans Serializing customized beans Loading beans Loading JAR files Clearing the window Exiting The Edit menu contains options for: Deleting beans Copying beans Pasting beans Producing an introspection report Attaching events to another component The View menu contains switches that: Switch between design mode and execute mode Hide or show invisible beans Let's see how to use the BeanBox, toolbox, and property sheet with a sample bean. This bean is called TestBean and consists of a movable blob, either a square or a circle, within a larger rectangle. Its properties are the shape of the inner blob, the background and foreground colors, and the size of step taken by the blob in pixels. The TestBean bean appears in the ToolBox window, with other available beans. The beans have been loaded from JAR files in a default directory, when the BeanBox was started.

Page 273

Go To INDEX

You click the bean name with the mouse pointer, and drag it to the desired position on the BeanBox window. Then you click the mouse button to place the bean. Here, you have placed the bean onto the BeanBox window for testing.

And the bean is highlighted with a black-and-white border in the BeanBox window to show that it is selected. The property sheet automatically shows the new bean's properties. Let's edit some of the bean's properties now.

You can change the stepSize of the blob, in pixels. This stepSize property dictates how many pixels up, down, or sideways that the blob will shift each time it is requested to move. The stepSize has been set to 5. Colors are represented in the property sheet by a colored block.

Page 274

Go To INDEX

You click inside this block to call up the color editor. In this case, the editor is the standard editor for colors, sun.beans.editors.ColorEditor. Colors can be chosen by entering a triple-value color code, or by choosing from a preset list of colors. You click the down-point arrow to reveal the drop-down list of default colors. You then choose a new color, in this case green, and click the Done button to select it.

You can see that the foreground color of our bean has been changed dynamically in the BeanBox to green.

The shape displayed, or blobType, can be either a square or a circle. Let's choose to use a circle.

Page 275

Go To INDEX

Again, the change to a property has been dynamically reflected in the BeanBox display of the TestBean. All of the properties of TestBean are defined as bound properties. This means that any changes to their values can be signaled to another component. The listener component is a bean called TestWatcher, and you simply add this property change listener bean to the BeanBox below our TestBean.

The TestWatcher bean, labeled "Watcher Bean", is represented by a yellow colored box at the bottom of the screen. Now you must connect property change events to the TestWatcher bean. You start by clicking it with the mouse pointer. Then select the: Edit menu Events submenu propertyChange submenu propertyChange option Choosing an event produces a red line running from the event source to the mouse pointer. You simply place the pointer over the target bean and click to select it. This displays an EventTargetDialog box, which lists all the available methods in the target bean. In this example, you choose the flipColor method and click OK. You will see a message for a few seconds saying that the BeanBox is generating and compiling an adaptor class. The EventTargetDialog box has constructed an adaptor class for the TestBean to TestWatcher connection, which will operate immediately. If you now change stepSize from 5 to 6, for example, the TestWatcher bean flips color from yellow to blue to signal that it has received a property change event. Now let's add buttons to the BeanBox window that will be used to control the movements of the blob inside the TestBean. You

Page 276

Go To INDEX

select a bean from the ToolBox window in the normal way and drop it into position on the BeanBox window. In this case, you have selected an OurButton bean and placed it above your TestBean. It has the default label of "press", which you will want to change to "Up". You need to add a further three buttons to the BeanBox window, labeled "Left", "Right", and "Down". You need to connect each button to the appropriate method in TestBean, using the event handling mechanism. The central box is moved by four different methods, which can be activated by external beans firing events. You start by selecting the button that you want to connect. In this case, it is the "Right" button.

You then select the: Edit menu Events submenu Action submenu ActionPerformedoption You then place the mouse pointer over the TestBean and click. You will see the EventTargetDialog box appear again, this time listing the methods of TestBean.

Page 277

Go To INDEX

To move the blob to the right, you must select the incrementXPosition method and click OK. This method will be invoked whenever an action event occurs in the button labeled "Right". The BeanBox generates and compiles the adaptor class to handle the new event. Clicking the "Right" button during design time results in the TestBean moving the blob to the right, so you can immediately see the effect of any changes you make.

You go through the same procedure for each of the other buttons, except that a different method in TestBean is called depending on where you want the blob to move. If you want to prevent the stepSize from being changed to an invalid value, one way to do it is to use constrained properties. The TestWatcher bean has been designed to veto changes to the stepSize value of less than 1 or greater than 10. You select vetoableChange from the Events menu, and connect the event to the TestWatcher bean in the normal way. The TestWatcher bean contains a method called vetoableChange for handling attempted value changes to stepSize. If you attempt to alter the stepSize value to 65, for example, the TestWatcher bean displays a warning message and the change is vetoed. The stepSize will remain at its original value until it is changed to a value within the range 1-10. Finally, you have a set of running beans, which are connected together through events. You can disable design mode to more easily view the running beans. The ToolBox and PropertySheet windows disappear, as do the highlighting surrounds around beans.

You also have the option to hide any invisible beans you may have been editing.
Page 278 Go To INDEX

Creating a simple bean

Let's look at the Java code that produced our TestBean demonstration. The main class is the TestBean class itself. It contains all the getter and setter accessor methods for its properties, a paint method for drawing itself, code for moving the blob around the rectangle, and code for adding and removing event listeners. The TestWatcher is the bean that monitors the property changes occurring in TestBean during design time. The other classes are BlobTypeEditor for providing the property sheet with the means to edit the blob shape, and TestBeanInfo which provides standard property information. The first thing to notice is that you must place the TestBean class inside a package beginning sunw.demo, in order for it to run in the BeanBox.

In this case, it is placed in sunw.demo.TestBeans. Secondly, you must import the java.beans package, because it contains the beans API's that you will require to write the bean. Your bean is a normal extension to an AWT component, in this case Canvas. You implement the java.io.Serializable interface to allow the customized state of your bean to be saved away. That is all you need to do in order for a standard bean to be saved away in a serialized form. Static and transient variables will not be serialized. Here, the coordinates determining the position of the blob, and the size of the blob, will always be the same for all instances of the bean. In effect, the position of the blob is always going to be set to (0,0), and the size of the block will always be 20x20 pixels. These variables, for the step size and the type of blob, will be serializable. If your bean supports bound properties, then you should add this class as a member, passing the bean itself as a parameter to the constructor. When signaling changes later, you delegate this class the responsibility of doing the work. It maintains a Vector of property change listeners for the bean. This is TestBean's constructor.

Page 279

Go To INDEX

It initializes its property values when first instantiated in the design environment. However, the constructor is not run when reading in serialized beans, since they will have changed property values. The TestBean bean is limited to a size of 100x100 pixels. This is the paint method for the bean, which is called by the repaint method for all changes.

A rectangle, a horizontal, and a vertical line, are all drawn first. Then a blob of the right type, size, and position is drawn. These are the methods for moving the blob inside the rectangle.

Page 280

Go To INDEX

Each is written in essentially the same way: Adding (or subtracting) the stepSize Checking for boundaries Invoking the repaintmethod Here are the getter and setter accessor methods for the stepSize property.

This pair of methods, conforming to the design pattern for properties, informs the introspector that the property exists and is of type int. The getter accessor method simply returns the value of the stepSize variable, but the setter method is much more complicated. The stepSize property is both a bound property and a constrained property. This means that you must Signal the event to any vetoable changelisteners

Page 281

Go To INDEX

Change the value, but only if there is move to exception Signal the event to any property changelisteners The vetoable change listeners will throw an exception, which interrupts the method's execution before reaching the point in the code where it changes the value. These are the setter and getter accessor methods for the blob type.

The remaining code provides support for handling lists of property change and vetoable change listeners.

There are pairs of addChangeListener and removeChangeListener methods for both PropertyChange and VetoableChange events. Each add method simply hands on the new listener for recording by the propChanges and propVetos objects defined earlier. And each remove method simply hands on the listener for the propChanges and propVetos objects to delete. The TestWatcher bean is a simple example of how to implement a property change or vetoable change listener.

Page 282

Go To INDEX

In reality, the kind of vetoes you will implement are likely to be much more complex, but the principles remain the same. Firstly, the bean must implement the VetoableChangeListener interface. Bound properties in TestBean will invoke the flipColor method as a means of signaling a property change. Constrained properties will invoke the vetoableChange method.

This method checks that any changes to the stepSize value are in the range 1 to 10. It throws a PropertyVetoException when the range is exceeded. This simple class is the user-provided class to support property changes to the blobType.

Page 283

Go To INDEX

It is an extension to the PropertyEditorSupport class in java.beans. The getTags method returns an array of strings that the property editor then assumes are to be displayed as a list of options. To provide an icon for your bean, you override the getIcon method, load your chosen image file, and return it.

You have to provide property descriptors for each property. You must instantiate a new PropertyDescriptor object, passing the property name as a string, and the bean object to which it belongs. In this example, there are property descriptors for background, foreground, font, stepsize, and blobtype. The blobType property has its own editor, BlobTypeEditor, so we assign that editor to it explicitly using the setPropertyEditorClass method in SimpleBeanInfo.

Page 284

Go To INDEX

To declare a property as bound, you use the setBound method from the PropertyDescriptor class. The stepSize property is both bound and constrained, so you must signal that with a setConstrained method too. The property sheet in your visual tool can use these flags to handle properties in different ways, depending on whether they are bound, constrained, both bound and constrained, or neither bound or constrained. All the PropertyDescriptor objects are then returned from the getPropertyDescriptors method as an array. Finally, the catch block records an error message if an IntrospectionException occurred while building the PropertyDescriptors.

Adding new beans to the BeanBox

To add your new bean to the BeanBox window you must first package it into a Java archive (JAR) file. You can place this JAR file into the default jars directory for automatic loading by the BeanBox utility at startup. You can, alternatively, explicitly load the JAR file into BeanBox using the File, LoadJar command. A JAR file is a standard ZIP archive file. It contains an optional manifest file describing its contents. The contents will include the class files, optional HTML help files, and any other data files associated with the bean. Serialized beans, with the .ser extension, will also be packaged into JAR files. You create a makefile file describing the contents and then use an appropriate utility to generate the JAR file. For a Win32 system, the makefiles have the .mk extension and you use the Microsoft nmake utility. For a Unix system, the makefiles have the .gmk extension and you
Page 285 Go To INDEX

can use the gnumake utility. The BDK demo directory, created when you install the BDK, contains several examples of .mk and .gmk makefiles that you can edit for your own purposes. Let's see what a typical makefile looks like.

This is the TestBean makefile, for packaging the TestBean, TestBeanInfo, and BlobTypeEditor classes together with the .PNG icon file.

The TestBeanWatcher bean is packaged into another JAR file of its own. First, you must specify the names of the Java classes to be included in the JAR. Next, you specify the additional data files to be included. The only datafile for TestBean is the icon image file, which appears beside the bean name in the ToolBox listing. You specify the creation of a manifest file describing the contents of the JAR file. And then you can specify explicitly the name of the bean contained in the JAR file using the Java-Bean: True command.

You should be careful to follow this layout for your makefile in order for it to be processed correctly. You are advised to use one of the existing makefiles that come with the BDK, and to edit it for your own beans.

Page 286

Go To INDEX

10 Java Database Connectivity
10.1. Overview of JDBC • Introduction to JDBC
JDBC (Java Database Connectivity) is a Java API for accessing databases. JDBC is included in JDK 1.1. It consists of a number of classes and interfaces that make it easy to connect to and query a relational database. Java is ideally suited to this purpose and the introduction of the JDBC has enhanced its capability. JDBC is an attempt to define a generic SQL database access framework. The framework provides a uniform interface on top of a variety of different database connectivity modules. JDBC allows you to write to a single database interface. It enables DBMSindependent Java application development tools and products. It also allows database connectivity vendors to provide a variety of different connectivity solutions. JDBC is a lowlevel API that supports basic SQL functionality. It was designed to let you create higherlevel database access tools and APIs. A popular choice for accessing relational databases is Microsoft's Open Database Connectivity (ODBC). ODBC is based on the X/Open SQL Call Level Interface specification. The JDBC API is also based on this specification. The designers of JDBC took this decision because they could build on knowledge gained using ODBC and make it more worthwhile for developers to adopt. ODBC is a good basis for design but is not appropriate for direct use from Java. This is because essentially it is a C interface. Native method calls from Java to C code affect Security Implementation Portability Robustness ODBC offers the ability to talk with most databases on many different platforms. So why was the ODBC model not adopted instead and the JDBC model shelved? JDBC is not as difficult to learn as ODBC is. Java has many simple and intuitive methods as opposed to few, complex commands. Many features such as pointers are found in C, and correspondingly in ODBC, but are not natural in Java. JDBC implements the "write once, run anywhere" approach. It doesn't matter what type of database you are trying to access. JDBC allows you to write one program that will access many major databases, such as Informix, Sybase, and Oracle. JDBC is an API that lets you write code that is not dependent on the DBMS or database connectivity mechanism being used. Of course, your program will also run on different platforms. So it will not matter if a company has UNIX workstations, PCs, and Apple Macintoshes all trying to access the same database. Also with JDBC, installation and version control are greatly simplified. Another advantage is that the time taken to develop new applications is relatively short. One very

Page 287

Go To INDEX

important use of JDBC is the development of standalone applications. JDBC applications can provide strong database querying, updating, and managing facilities. This is because they have total access to file systems and remote servers and can open network connections. Using Java JDBC applications in this way is particularly useful in the context of corporate intranet communication. You can use JDBC for developing applets as well as applications. For instance, applets could be designed as database access applets which use JDBC to link to databases. Let's look at the basic series of operations that occur when a database is accessed and queried using JDBC. First the Java program opens a connection to the relevant database. Next it makes a statement object. Then it passes SQL statements to the underlying DBMS through a Statement object, a PreparedStatement object, or a CallableStatement object. Finally it retrieves the results as well as the information about the result set. The classes that allow it to perform these operations are in the java.sql package. However, a program using JDBC will also need a driver for the data source with which it wants to interface. Implementations of the following abstract classes provided by the JDBC API must be also provided by the database driver: java.sql.Connection java.sql.Statement java.sql.CallableStatement java.sql.PreparedStatement java.sql.ResultSet The Java SQL framework allows multiple database drivers. Each driver should supply a class that implements the interface java.sql.Driver. Upon initialization, the DriverManager class will try to load as many drivers as it detects. Then for any given request for connection, it asks each driver to try to connect to the specific URL. The JDBC driver manager locates drivers and establishes connections. Programs developed with JDBC communicate with the JDBC driver manager, which in turn uses the currently active drivers to communicate with the actual database. When the connection is established, the driver manager steps out of the way. In general terms, it is better to think of dealing with a data source rather than an actual database. This is because the form, content, and even location of the data does not matter to Java once there is a driver for that data. The JDBC management layer needs to know which database drivers are available for use. So when a Driver class is loaded, it should create an instance of itself. This instance will then be registered with the DriverManager. The Class.forName method allows you to explicitly load a driver class. Class.forName(“some.db.driver”); The JDBC driver manager will try to locate a driver that can use the protocol specified in the database URL. In this case, the newly loaded driver class must also register itself with the DriverManager. You do this by calling the registerDriver method in the DriverManager class. JDBC drivers can be written in Java so they can be downloaded as part of an
Page 288 Go To INDEX

applet. They can also be implemented using native method calls which link up with existing database access libraries. However, these types of drivers are limited to applications that exist on the same host as the database. This is because most browsers won't allow native methods to be called from within a remotely loaded applet.

Two-tier JDBC architecture

JDBC supports a two-tier model for accessing databases. The majority of client/server systems today have been implemented using the two-tier approach. The twotier model has gained acceptance because the popular tools that implement it are very easy to adopt and utilize. A two-tier architecture implemented using JDBC can have a Java applet or application on a client machine talk directly to a server. There is no intermediary server. The two-tier model is typically used in small environments (less than 50 users). In terms of JDBC, the two-tiered approach means that the user interface and business logic reside on the client workstation. However, the database resides on a central server. Because there is no intermediary server, a JDBC driver is needed. The JDBC driver communicates with the particular database management system that is being accessed. When you query the database, SQL statements are passed to the database via an underlying JDBC driver and these queries are processed. The results of your queries are then passed back to you. The query could also take the form of a specialized SQL extension or derivative. All JDBC drivers should support at least the entry level version of SQL-2. The databases that you query may be located on another machine to which you are connected via a network. For instance, you could access and query a database over the Internet. And corporate enterprises are increasingly using intranet-based solutions for their database access requirements. With database connectivity there will often be performance considerations. For example, network response times are affected when the database you connect to is on the other side of the planet. Response times will obviously be quicker over a LAN. JDBC was designed to cope with these problems. Database technology in all its forms relies on strong security measures being implemented. Data integrity is of the utmost importance. You must also give serious consideration to network security when using JDBC. Remember JDBC is an API for database connection and not a network protocol. Any network protocol you do use should be thoroughly examined to ensure it provides sufficient security to cater for your database connectivity system. DBMS vendors and database connectivity vendors have in many cases identified and fixed a number of network protocols suitable for accessing databases. Drivers themselves also have security responsibilities. You can write drivers that are secure and will not allow applets to connect to a database illegally. A driver which performs well under certain conditions could be installed on a user's hard drive. It could then be a trusted part of that Java environment. So it is vital that such a driver has no problems that could allow applets to compromise the security and integrity of the user's system. The JDBC extensions to the Java security model allow you to download a JDBC driver and register it with the driver manager. If the applet is not trusted, that driver can
Page 289 Go To INDEX

only be used for connections from the server that holds the applet. Information specific to the local system can not be accessed. This is a basic security measure. You can connect to databases with both Java applets and applications. Database access applets are often used across untrusted boundaries. For instance, you could download an applet from a company that is based on the other side of the world. You might also download an applet from another machine on an intranet. In either case, the security of your machine is an important issue. Untrusted applets are restricted as far as accessing certain resources belonging to the client. They can't access the files on your system. Nor are they able to open network connections to unnamed arbitrary hosts. As a rule, JDBC should follow the guidelines of the standard Java applet security model. Normal unsigned applets should be untrusted. Java database access applications are becoming more widespread. The code for Java applications is considered trusted. It can read and write files and open network connections in the normal manner. There is also no restriction placed on loading drivers from the local classpath. While Java applications are useful for accessing databases over the Internet, they can be used extensively in intranet solutions also. The centralized computing model reduces the problem of managing code on the client. Changes to user interface and functionality can be done simply by changing code on the server. This is obviously preferable to the cost and disruption to services that would occur if code on perhaps thousands of client desktops had to be updated individually. Java aims at being a universal language. JDBC aims to take care of the database access side of this aim. It is hoped that Java and JDBC will eventually replace the many proprietary database languages that are used. It is also hoped that they will replace the many incompatible basic derivatives that are used. Many of the software vendors endorse JDBC. There are many JDBC drivers for the top DBMSs currently provided by them.

Three-tier JDBC architecture

A three-tier model of JDBC architecture is becoming increasingly popular. The model inserts a middle tier of services into a two-tier architecture. This middle tier is also known as middleware. The three-tier model moves some of the application logic from the client workstation onto an application server. So an advantage of three-tier architecture is that the application logic is made independent from the user interface or the database access. The structure for three-tier architecture is as follows: Tier 1 - user interface Tier 2 - application logic Tier 3 - database access JDBC database applications for a three-tier architecture are: Better designed Easier to maintain Faster as the number of users grows large
Page 290 Go To INDEX

In the three-tier model, you send queries and commands to the middle tier. The services implemented in the middle tier then send SQL statements to the database. The database processes the SQL statements. Finally the middle tier sends the results back to you. There are performance considerations associated with the choice of a programming language you use to implement the middle tier. Typically, C and C++ have been used for this purpose. This is because they offer fast performance. It is becoming more practical and popular to use Java to implement a middleware layer. The introduction of optimizing compilers has aided this. Optimizing compilers translate Java bytecode into efficient machine-specific code. There are certain inherent qualities in Java, which make it a good choice for implementing a middle tier. These are its: Security features Robustness Multithreading capabilities Three-tier JDBC architecture offers an advantage over two-tier with regard to scaling. Often a new application is developed and prototyped for a small two-tier environment. When use of the application expands, the system administrators scale up by simply adding more users to the server. However, this approach often results in an ineffective system. This is because the server becomes overwhelmed. To properly scale to hundreds or even thousands of users, it is usually advisable to migrate to a three-tier architecture. Projects for implementing three-tier architectures require much more planning and management. Also, the tools designed to support three-tier applications are typically more difficult to learn and use. However, using JDBC three-tier architecture can provide performance advantages over two-tier implementations. Developing solutions with a JDBC implemented three-tier model is beneficial because you can employ a higher level API, which is easy to use. The higher level API is translated into the appropriate low-level calls. Calls from your database access application to the middle tier can be made using an object request broker (ORB) or through remote method invocation (RMI). In Java, an object paradigm might be the best way to define the middle tier in either of these scenarios. Three-tier JDBC access to databases is also desirable because it provides additional security measures. It allows you to explicitly define the legal and permitted operations on corporate data. This is preferable to a situation where direct unrestricted updates to the DBMS servers are allowed.

10.2. JDBC Configuration • JDBC drivers

Java Database Connectivity (JDBC) is a low-level API that supports basic SQL functionality. Using the classes and interfaces defined in the JDBC package, Java applications and applets can access, query, and update a wide range of relational database servers. To make a connection between a Java application (or applet) and a database, your system requires at least one JDBC driver. Drivers translate Java application-level calls into calls that can be understood by an associated database. Each database driver is specific to the type of database server used.
Page 291 Go To INDEX

This means that a single application can connect to different databases simply by moving between various drivers. Four main interfaces are included in the JDBC API package: java.sql.Driver java.sql.Connection java.sql.Statement java.sql.ResultSet These interfaces must be implemented by each database driver. The Driver interface is used to send queries to a database and returns results to the querying application. And the Connection, Statement, and ResultSet interfaces provide methods that allow the programmers to create statements and retrieve results. You can test the functionality of a JDBC driver using the JDBC driver test suite. Most current databases connect to applications using ODBC. ODBC incorporates a driver and, in some cases, a client-side library that is specific to the client's operating system. Both ODBC and JDBC drivers are based on the X/Open SQL Call Level Interface (CLI). So translating between them is relatively straightforward. To make a connection between a Java application and an ODBC driver, you need to install a special JDBC driver called a JDBC-ODBC bridge. The JDBC-ODBC bridge driver is a standard component of the JDBC package. This bridge implements JDBC method calls by translating them into ODBC function calls on a platform-specific basis.

In effect, the bridge sits on top of ODBC and implements JDBC for any database that has an available ODBC driver. Using the JDBC-ODBC bridge, JDBC can make use of the database connectivity provided by the existing array of ODBC drivers. However, there are some limitations associated with this type of database driver. Whenever ODBC is used, ODBC drivers components must be manually installed on every client machine. In addition, calls from Java to C code tend to diminish database security and limit the portability of applications. This makes the JDBC-ODBC bridge impractical for Web-based applications. The bridge driver is most appropriate on a corporate network where client installations are not a major problem. And it works well for Java applications that use a three-tier database access model. Other JDBC drivers can be divided into three categories depending on their construction and the type of database they are designed to support. For example, early JDBC drivers were implemented using native methods that bridged existing database access libraries. The native-API partly-Java driver uses C language libraries to convert JDBC method calls into calls on the client API. This type of driver is used for accessing client library-based database systems such as DB2, Informix, Oracle, and Sybase. Like the bridge driver, this style of driver requires that some binary code be loaded on each client machine. Many major database vendors
Page 292 Go To INDEX

are currently developing drivers that are written entirely in Java. All-Java drivers offer a significant advantage since they're capable of being downloaded as part of an applet. The JDBC-net all-Java driver translates JDBC calls into a network protocol that is independent of any specific database. The translated calls are then sent to a middle-tier server. A middle tier can connect to a variety of databases on behalf of the client application. Once the calls reach the middle tier, they are converted to a specific database protocol. In general, the JDBC-net all-Java driver is the most flexible JDBC alternative. And it can deal specifically with the security requirements of web applications, such as passing data through firewalls. The native-protocol all-Java driver converts JDBC calls into the network protocol used by the DBMS (database management system) server. That means that this type of JDBC driver can communicate directly with the DBMS server. It offers a practical solution for intranet use. It's likely that the JDBC-net all-Java driver and the native-protocol all-Java driver will become the preferred way to access databases from Java applications. In the meantime, the JDBC-ODBC bridge and the native-API partly-Java drivers offer reliable interim solutions.

The DriverManager Class

The DriverManager class is a utility class that comes as part of the java.sql package. This class is responsible for passing connection requests and loading database drivers. It also provides methods to manage and support the connection between Java applications and JDBC drivers. In effect, the DriverManager acts as a point of communication between the application and the driver. Using the methods defined in the DriverManager class, Java applications can: Obtain a connectionthrough a driver Register De-register Set up logging Set login timeouts for database access All of the methods in the DriverManager class are static. And they can be referenced through this class name. public class java.sql.DriverManager Let's look at an example of a DriverManager method. This method tries to return a reference to an object implementing the Connection interface.

The first Driver class that returns a connection is used. Before they can be used by the JDBC management layer, drivers must first be loaded. Some drivers are loaded by default when the DriverManager initializes. During initialization, the DriverManager class will attempt to load each of the Driver classes that are defined in a system property called sql.drivers. Remember, each of the named classes must implement the Driver interface.

Page 293

Go To INDEX

Drivers that are not loaded at initialization can be explicitly loaded by programmers using the standard Class.forName method. Class.forName(“main.db.driver”); For example, this code lets you load the Main.db.Driver class. Once a driver has been loaded, its name must be registered with the DriverManager class. Drivers cannot connect to applications until their names have been registered. Each driver name is a class filename that includes the package name.

Newly loaded drivers are automatically registered with the DriverManager as soon as an instance of the driver is created.

Creating a middle tier

As you know, the JDBC API supports both two-tier and three-tier database access models. In the two-tier model, a Java application communicates directly with a database server. In the three-tier model, Java applications send calls to a middle tier of services on a server. The services implemented in the middle tier are also known as "middleware". Let's examine how the middle tier affects the interaction between a client application and a database. You can see that this database supports two tables called Student and Workgroup.

Let's suppose a new student called Joe is joining workgroup 1. In a two-tier structure, the client application would send an "add student" SQL request directly to the database. But in three-tier models, client applications work at a higher level. In this example, the client application sends its request to the middle tier using a protocol such as RMI, CORBA, or TCP/IP. When the middle tier receives a request from the client application, it maps that request to a piece of middleware code. Parameters included in the client request, such as student name and workgroup number, are used to generate specific SQL statements that will carry out the client request. In this example, an SQL statement requests that a new student record is added to the Student table, with an associated workgroup value of 1.

Page 294

Go To INDEX

Another SQL statement might request that the Workgroup table be updated by adding 1 to the No. of students field for workgroup 1. The middle tier will access the database using JDBC, and execute these SQL queries. If any results are generated by a query, they are sent from the database to the middle tier. The middle tier then passes the results back to the client applications. In the past, middleware services were typically written in languages such as C or C++ because these languages offered fast performance. But with the introduction of optimizing compilers, Java bytecode can be translated into efficient machine-specific code. So it has become practical to implement middleware in Java. This means that three-tier architectures can now take advantage of Java's multithreading and security features. A multithreaded middle tier is capable of handling multiple client connections to one or more database servers. And it can accept connections from clients using a variety of protocols, such as HTTP and TCP/IP. Three-tier database access models offer several advantages over the traditional two-tier design. For example, the middle tier can be programmed with a set of business rules that manage the manipulation of data. This makes it possible to explicitly define the operations that can take place on data. And it lets you restrict access to corporate databases. By using a middle tier, users can employ an easy-to-use higher-level API which is translated by the middleware into the appropriate low-level calls. So, in many cases, the three-tier architecture can provide performance advantages.

10.3. Connections • Establishing a Connection

To query a database, you must first establish a connection through the Connection object by calling the DriverManager.getConnection method. One application can make several connections to a database. It can also make more than one connection to a number of different databases. As the argument for the DriverManager.getConnection method is a URL string, let's first look at some URL syntax. URL means Uniform Resource Locator. URLs are a way of identifying resources stored over the Internet. Here are two examples of URLs.

Page 295

Go To INDEX

The protocol part of the address is always followed by a colon. URLs containing Internet references use a double forward slash "//" following the protocol name. A single forward slash "/" follows the protocol name for local resource files. This part of the URL contains host information. It can include port numbers, and paths to files and directories. Here's an example of a URL that specifies the port number, 80, for the hostname java.sun.com.

This URL specifies the file tutorial.html in the doc directory. A JDBC URL is a means of providing a driver with the appropriate information for establishing a connection with a database. From the URL, drivers must be able to recognize the subprotocol of the database being accessed. The address must be as complete as possible, providing the application with sufficient details to run without the need to perform additional administrative tasks. For some connections, the name of the database is all that's required by the driver. But for accessing databases over the Internet, you may need to specify port numbers. JDBC names must be compatible with network naming systems like DNS or NIS. When a user specifies a database or hostname, a network name resolution system can be used to locate the resource. This way of locating a database is referred to as network indirection. The JDBC URL naming system has many of the features of the Internet URL naming standard with some additional syntax options. This is the syntax for a JDBC URL contained in a string. The protocol name for JDBC URLs is always jdbc. The subprotocol refers to the name of the database connectivity mechanism. A subprotocol name can be reserved for a particular driver by registering it informally at JavaSoft. You register subprotocol names by sending an email to jdbc@wombat.eng.sun.com A list of registered driver classes is maintained by the DriverManager class. One example of a reserved subprotocol is odbc, which is reserved for the JDBC-ODBC bridge. The special syntax for this subprotocol allows application writers to include attribute names and values in the subprotocol name. This is an example of a URL where the odbc protocol is used to connect to host sambat. The attributes CacheSize and ExtensionCase are defined with appropriate values. The third part of the JDBC URL is the subname. This is used to identify the location of the database. jdbc:subprotocol:subname Here's an example of a URL for a locally stored database called datab. jdbc:odbc:datab

Page 296

Go To INDEX

If you want to access the same database over the Internet, you need a more specific URL. jdbc:odbc://java.sun.com:401/datab Here the odbc protocol is used to connect to the database datab on port 401 at the java.sun company site. Let's look at an application, which connects to a remote database. The public class, Dbclient2, is responsible for loading the driver, drivers.databases.qDriver and for connecting to the remote database, dataB.

Here's the URL string that specifies the location of the dataB database. The subprotocol for this string is called quickD. The database dataB is located at home.sun.com on port 401. This code initializes the Connection object, dbCon.

The code states that to connect to the database, the DriverManager.getConnection method must use the username "Jones" and password "Pencil". The closing lines of this
Page 297 Go To INDEX

application contain the error message to appear in the event of an unsuccessful attempt to connect to the database or to close the connection. When a connection to a database is initiated by the getConnection method, the DriverManager class checks the list of registered drivers. It searches the driver list until it finds a driver that supports the subprotocol specified in the URL. When the appropriate driver is found, the connection is made to the database. An instance of a Connection object is then returned and allows a user to send SQL statements.

Using threads to monitor connections

Java is unusual among programming languages because it has a built-in multithreading capability. This means that the threads in Java programs can execute concurrently. This important capability has been taken account of in the design of JDBC. SQL statements can execute asynchronously under some database APIs. Java allows you to create a separate thread if you wish to execute statements asynchronously with respect to the main thread of execution. Because Java provides a multithreaded environment, JDBC drivers don't have to specifically provide support for asynchronous statement execution. The processing that is carried out by JDBC is synchronous. The database application program must wait for the SQL statements to be completed. But because Java is a multithreaded platform, using threads to simulate asynchronous processing is recommended. The degree of concurrency available may depend on the particular driver that you are using. Also the driver will provide any synchronization, which may be necessary. However, you can assume that fully concurrent execution is supported when programming using JDBC. You can execute two statements that are on the same Connection concurrently. You can also process their ResultsSets concurrently. Some drivers will not send a statement until a previous one completes. Other drivers will provide full concurrency. When accessing a database using JDBC, there are often situations when several threads will simultaneously access the same object. The JDBC driver must ensure that any operation on a java.sql object must be able to handle such situations. These operations must be multithread-safe. Multithreading is very useful in terms of connecting to and querying a remote database. For instance, suppose you send a query statement to a database and find that you are waiting an inordinate amount of time for a reply. You need some mechanism that can cancel the thread that is executing the statement. If one thread is being used to execute a statement, another thread can be used to kill it. The Statement class contains a cancel method for doing this. The cancel method is declared like this. public abstract void cancel () throws SQLException It throws an SQLException if a database-access error occurs.

Page 298

Go To INDEX

10.4. Querying the Database • Sending SQL Statements

When a connection is established to a database, statement objects can then be used to send SQL statements. Statement objects are defined based on what's contained in SQL statements. The methods used to create Statement objects are part of the Connection interface. And the methods involved in executing statements and returning results are part of the Statement interface. In this example of code, an instance of the Statement object, myStatement, is created using the createStatement method.

This is the simplest Statement object and is used for passing SQL statements where no parameters are specified. The PreparedStatement object is responsible for passing pre-compiled SQL statements containing parameters with input arguments.

Input arguments are the same as IN parameters. The prepareStatement method is used to create an instance of the PreparedStatement object, which in this example is the object stat. Here the prepareStatement method of the Connection object someConnection is called. The locations of the IN parameters in the statement are marked question marks. The PreparedStatement object provides a number of additional methods to supply values for these parameters. And as this object extends the Statement object, it inherits the Statement methods. As PreparedStatement objects are pre-compiled, they can be used to decrease the execution time associated with simple SQL statements. This is particularly useful where a simple statement is executed many times throughout an application. Before the PreparedStatement object stat is executed, the value of each parameter has to be set. There are many different set methods defined by PreparedStatement which are sometimes referred to as the "setXXX" methods. The one you choose depends on the type of the relevant parameter. The setXXX methods for setting IN parameter values must specify types that are compatible with the defined SQL type of the input parameter. For instance, if the IN parameter has SQL type Double, then setDouble() should be used. If arbitrary parameter type conversions are required, then the setObject method should be used in conjunction with a target SQL type. Lets look at an example using one of the setXXX methods. This statement will set the first parameter to 1010101010. Stat.setLong(1, 1010101010) The first argument is the ordinal position of the parameter to be set. The value it is to be set to is the second parameter. Generally, parameter values remain set for repeated use of a statement. However, it is often wise to immediately release the resources used by the current parameter values.

Page 299

Go To INDEX

They can be cleared by a call to the clearParameters method of the PreparedStatement class. This is the declaration for clearParameters().

The setXXX methods do not perform any general data type conversions. Instead the driver maps the Java type to a corresponding SQL type. This SQL type is what eventually is sent to the database. You have to be sure that the Java type of each IN parameter is compatible with the SQL type that the database is expecting. A failure to take adequate care here could affect the portability of your database access program. You can also explicitly convert an IN parameter to a particular SQL type. You do this by calling the setObject method of the PreparedStatement class. This is the declaration for setObject().

The setObject method can accept any Java object. This allows an application to be generic. It also allows it to accept input for a parameter at run time. Once the application has been compiled, the type of the input is not discernible. If a SQL type is not given, the driver will simply map the Java object to its default SQL type before sending it to the database. It is also possible to send an SQL null value as the IN parameter to the database. To do this, you use the setNull method. This is the declaration for setNull().

You must specify the SQL type of the relevant parameter. Java and JDBC allow you to use an input stream as an IN parameter. This makes it convenient for you if you want to send large amounts of data. You simply send it in smaller pieces along a stream instead of one large piece. There are three different methods provided by JDBC for dealing with setting IN parameters to input streams: setUnicodeStream setBinaryStream setAsciiStream The three methods for dealing with streams as IN parameters take one more argument than the other setXXX methods. This is because you are required to specify the length of the stream. Before data is sent, some databases need to know what quantity is involved so specifying the stream length is important. Another type of statement object is the CallableStatement. This is used to send SQL statements to procedures stored in databases. The Connection method prepareCall creates the CallableStatement object.

The question marks denote the place for the IN, OUT, and INOUT parameters appropriate for the getSampleData stored procedure. The CallableStatement object extends the PreparedStatement object and inherits the methods for setting IN parameters. Additional methods are then added to handle OUT and INOUT parameters. Suppose you had a stored procedure called getNumber in a database. You prepare a call to it by passing the call as a parameter to the prepareCall method.

Page 300

Go To INDEX

The prepareCall method of the Connection class is optimized for handling stored procedure call statements. Parameter placeholders must be used for the return values if there are any. They must also be used for any output arguments because the parameter marker is bound to a program variable in the stored procedure. The CallableStatement class should be used if you are executing a stored procedure call. If your stored procedure returns OUT parameters, you must use the registerOutParameter method. This registers the SQL type of the OUT parameter prior to the execution of the statement. After the statement has executed, you must use the corresponding getXXX method. This is used to retrieve the parameter value. The correct one to use is again the Java type that corresponds to the SQL type registered for that parameter. After the stored procedure is executed, the DBMS returns the result value to the JDBC driver. CallableStatement does not provide a special mechanism for retrieving large OUT values incrementally. A given OUT parameter could have the SQL value NULL. To determine if this is so, you first read the parameter. Next you call the CallableStatement.wasNull method to discover if SQL NULL was returned by the read. JDBC also has a parameter that supplies input and accepts output. This parameter is the INOUT parameter. An INOUT parameter requires a call to the appropriate setXXX method before sending the statement to the database. A call to the registerOutParameter is also required to register the relevant SQL type as an output parameter. The parameter's value is set as an input parameter by the setXXX method. The JDBC driver converts a Java value, which the setXXX method provides into a SQL value. The SQL type of this IN value should be the same as the SQL type supplied to the method registerOutParameter. The SQL value is the one sent to the database. A call must be made to the corresponding getXXX method in order to retrieve the OUT value. Let's look at an example. Suppose you have a parameter whose Java type is short. You would then use the setShort method from PreparedStatement to assign an input value. The driver converts this to an SQL SMALLINT value when it sends it to the database. You should then use the getShort method to retrieve the value from the database. When a Statement object is created, it can then be executed by specifying the appropriate method. These methods are provided by the Statement interface. Three execute methods that form part of the Statement interface include: executeQuery executeUpdate execute These methods are used to access data contained in database tables. When executed, they produce the return value while the SQL statement produces the result. Let's look at some code that illustrates the executeQuery method.

Page 301

Go To INDEX

This method is used for statements that return a single result set in the form of a ResultSet object. In this example, the ResultSet object provides the result of executing myStatement. The executeUpdate method is used for statements containing the constructs: INSERT UPDATE DELETE SQL statements that contain these constructs cause changes to rows or columns in a table. Executing the executeUpdate method returns an update count. This is an integer value indicating the number of rows of a table affected by executing the statement. The executeUpdate method is also used for SQL Data Definition Language (DDL) statements. These contain parameters such as CREATE TABLE or DROP TABLE. When a statement containing CREATE TABLE is executed, it returns a new table. This new table is the result, but the return value for the executeUpdate method is zero. A return value of zero for the executeUpdate method indicates that there was no change to the rows of a table, or that a DDL statement was executed. Before a statement can be executed again, the ResultSet object must finish processing the current result set. The execute method is used for more advanced applications. It's useful where the result of executing a SQL statement is unknown but could be a combination of ResultSet objects and update counts. As the execute method is more complex than the executeQuery or executeUpdate methods, it requires additional methods to identify the type of results returned by the statement. A method called getResultSet is called to determine the first ResultSet object. If this method returns a null value, this indicates that there are no additional results but that there may be update counts. The getUpdateCount method is then called and returns an integer indicating the number of rows of a table changed by executing the statement. If the integer -1 is returned, this indicates that there are no further update counts but that there could be a result set. You'll already know if there is a result set from the results when the getResultSet method was first called. If the getResultSet method returns a value other than null, the getMoreResults method is called. The results of this method can be true or false. A true result indicates that there is a result set which is then retrieved by running the getResultSet method again. A null result for this method indicates that there could be an update count result, which is then retrieved by running the getUpdateCount method. A false result from the getMoreResults method indicates one of the following: An update count result No further result sets

Page 302

Go To INDEX

To identify which, the getUpdateCount method is then called. A completed statement is one where all the results are returned. Usually this occurs after one of the execute SQL statements methods is run. The execute method requires that all results sets or update counts are completely returned before the statement is considered complete. As the PreparedStatement interface extends the Statement interface, it can override the executeQuery, executeUpdate, and execute methods. These execute methods are all part of the Statement interface. You have seen the syntax used by the Statement object, CallableStatement, to send SQL statements.

The syntax within the brackets "{}" is referred to as escape syntax. This syntax is an indication to the driver that the code needs additional translating before it is passed to a database. JDBC supports the same syntax as ODBC for: Stored procedures Dates Times Scalar functions This is an example of escape syntax used to call the stored procedure called getSampleData.

In this example, values are required for the parameters in the statement indicated with question marks. If the procedure call returns a result parameter, the escape syntax for calling the database would look like this. The escape syntax provides a variety of additional features to a programmer and is independent of database server. Database servers use different types of syntax to represent dates and times. Here's the escape syntax for specifying a date in a JDBC SQL statement where yyyy is the year, mm is the month, and dd is the date. { d ‘yyyy-mm-dd’ } The driver will format this date according to the underlying database and the result could be 09-July-98 or September-07-98. Here's the escape syntax for TIME and TIMESTAMP where hh represents hour, mm is minutes, ss is seconds, and f is a fraction of a second.

Page 303

Go To INDEX

The keyword escape can be used for special escape characters used in strings. Here's an example of using the escape character '+' in a statement used to find product names where the sales field is a '2' followed by another character, followed by a '%'.

String, numeric, date, and system functions are supported by DBMSs on scalar values. DBMS is Database Management System. JDBC allows the use of these functions. The keyword "fn" is specified and is followed by the function name and arguments.

Handling Transactions

A transaction is one or more statements that have been executed and completed. They will also have been either committed or rolled back. Transactions are supported by most JDBC drivers on the market today. If a Connection is in autocommit mode, then all its SQL statements will be executed and committed as individual transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by either the commit or rollback methods of the Connection interface. When either commit or rollback is called, the current transaction ends and another one begins. By default, a new Connection is in autocommit mode. In other words, each statement is executed as a separate transaction in a database. When the statement is completed, commit() will be automatically called for that statement. The transaction consists of only one statement because each statement is committed individually. You must disable autocommit mode if you want to execute several statements in a transaction. You do this by calling the setAutoCommit method of the Connection interface. You must pass it a boolean value of false. You can, for example, disable autocommit and then group two updates into one transaction. For instance, you might not want a particular change to occur unless another change does also. The rollback method will be called if one or both fail. This means that the values that existed prior to executing the updates are restored. The commit method is called if both updates are successful. The effects of both updates are then made permanent. An implicit transaction is always associated with a Connection when autocommit is disabled. Also a transaction will not terminate until the commit method or the rollback method is called. In this case, all the statements in the transaction are committed or rolled back as a group. If a SQL statement changes a database, the commit method will make these changes permanent. Any locks that the transaction holds will also be released. On the other hand, the rollback method will discard those changes. Problems can often occur when two users try to access a database at once. For instance, one user may read a value from one place a short time after another has written or changed that value in another place. The information is no longer up to date, consistent, or reliable. Two transactions operating on the same database at the same time must be prevented from having this effect. The DBMS can process transactions so as to minimize error and disruption. You can specify a transaction isolation level that helps it to do this. A dirty read is where a value is read before it is committed. You can instruct the DBMS to

Page 304

Go To INDEX

allow dirty reads by calling the setTransactionIsolation method of the Connection instance. This is the declaration for the setTransactionIsolation method.

The setTransactionIsolation method should not be called while in the middle of a transaction. The argument "level" refers to the five transaction levels which you can specify. These are: Connection.TRANSACTION_NONE Connection.TRANSACTION_READ_COMMITTED Connection.TRANSACTION_READ_UNCOMMITTED Connection.TRANSACTION_REPEATABLE_READ Connection.TRANSACTION_SERIALIZABLE Your application will generally perform faster if a lower level of transaction isolation is used. This is because of increased concurrency between users of the databases. It is also affected by the decrease in locking overhead that occurs. With the first level, TRANSACTION_NONE, transactions are not supported. public static final int TRANSACTION_NONE The second level, TRANSACTION_READ_UNCOMMITTED, allows dirty reads, non-repeatable reads, and phantom reads to occur. public static final int TRANSACTION_READ_UNCOMMITTED Here's an example of a phantom read. Suppose a user wishes to perform two read operations on a database and after performing the first read operation, another user alters the database. When the first user performs the second read operation, the two sets of data don't match. If a lock is not in place to ensure the integrity of the object, a phantom read will occur. The third level is TRANSACTION_READ_COMMITTED. public static final int TRANSACTION_READ_COMMITTED Dirty reads are prevented but non-repeatable reads and phantom reads can occur. The fourth level is TRANSACTION_REPEATABLE_READ. public static final int TRANSACTION_REPEATABLE_READ It prevents dirty reads and non-repeatable reads but phantom reads can occur. The fifth and final level is TRANSACTION_SERIALIZABLE. public static final int TRANSACTION_SERIALIZABLE Dirty reads, non-repeatable reads and phantom reads are all prevented with this field. TRANSACTION_NONE is the lowest transaction isolation level. TRANSACTION_SERIALIZABLE is the highest transaction isolation level. At this, the highest level, more consideration is given to the avoidance of conflicts. No transaction can make changes to the data read by another transaction that is operating on a database. When you are choosing which transaction isolation level to use, you must consider: Data integrity and consistency

Page 305

Go To INDEX

Performance rates You should also ensure that the level you choose is supported by the underlying DBMS. The driver that is being used often determines the transaction isolation level for a new Connection object. However, usually the underlying database provides a default level. If you change the level by calling SetIsolationLevel, the new level will remain active until the end of the Connection session. You might also want to change the transaction isolation level for a single transaction. In order to do this, you need to set it before the transaction begins and after it ends. If you try to modify the transaction level during a transaction, a call to commit() will be immediately made. This will cause any changes at that point to be made permanent. For this reason, it is recommended that you avoid modifying the transaction isolation level during a transaction.

Processing ResultSet Objects

When a SQL statement is executed, the ResultSet interface defines methods that allow you to retrieve data returned as a result. The ResultSetMetaData interface provides methods for retrieving the type of data returned after a statement is executed. Metadata includes statement parameters, row results, and database properties. Here's an example of code used to execute a SQL statement.

The result set returned by executing a query statement generally takes the form of a table as shown here.

This code specifies that the executed statement should return columns called name, id, and parttime from the EMPLOYEETABLE. The data contained in the name column takes the form of a string. The data in the id column takes the form of an integer and the data in the parttime column is a boolean. The ResultSet object provides a number of methods to access the data contained in columns of the ResultSet. These are known as getXXX methods. The first step in retrieving a value from a ResultSet is to point to a row by calling the ResultSet.next method. The location of the current row is indicated by a cursor. When the ResultSet.next method is first called, the cursor moves from before the first row to point to
Page 306 Go To INDEX

the first row as the current row. With JDBC, the name of the cursor can be obtained by the ResultSet.getCursorName method. The cursor name can be used in positioned updates or partitioned deletes of data. When the correct row is located, getXXX methods are then used to obtain specific column values. A column value can be retrieved by referring to its name or number.

Both these lines of code refer to the same column called modelname, which is the third column of the ResultSet object res. A SQL query can be executed for data contained in tables where more than one column has the same name. When this happens, the getXXX method returns the first column name that's matched in the search. Column numbers are a more efficient way of referring to tables that contain columns with identical names or where no names are specified. Numbering of columns starts from left to right with the first column on the left numbered 1. To determine the type of data contained in a table, the resultSet.getMetaData method is called which returns the ResultSetMetaData object. Let's look at what happens when a getXXX method is called to retrieve data values stored in a ResultSet.

The SQL data types are mapped to Java objects and values returned by the driver. Examples of SQL type mappings to Java objects are shown in this table. Methods used to retrieve these as objects include: ResultSet.getObject CallableStatement.getObject Here's an example of code that demonstrates the ResultSet.getObject method and how it maps SQL types to Java objects.

The getObject method returns an integer for the values in column a, and a string for the values in column b. The CallableStatement.getObject method is similar to the ResultSet.getObject method. But the SQL data type must first be specified using a method called Callablestatement.registerOutParameter. Using the PreparedStatement.setObject, the object is first mapped to a default SQL type.

Page 307

Go To INDEX

It is then converted to a specific SQL type before being sent to the database for processing. Large amounts of data can be retrieved by the ResultSet in the form of streams using methods like: getBinaryStream getAsciiStream getUnicodeStream This is useful for SQL data types like LONGVARBINARY or LONGVARCHAR. The ResultSet.getBytes and ResultSet.getString methods are also used to retrieve LONGVARBINARY or LONGVARCHAR SQL data types. But there is an upper limit on the amount of data they can read. java.io.Input streams are returned by the ResultSet.getXXXStream methods. The data is broken into smaller sets that must be immediately accessed. The method ResultSet.getBinaryStream returns a stream of unconverted bytes from the database. To return a stream of one-byte ASCII characters, you use the ResultSet.getAsciiStream method. And to return streams of two-byte Unicode characters, you use the ResultSet.getUnicodeStream method.

Page 308

Go To INDEX

11 Java and Distributed Objects
11.1. Overview of distributed objects • Introduction to Distributed Objects
Distributed systems are composed of a set of software components running on a number of networked computers. Interaction between the components is complicated because of the differences between the underlying computing environments and networks. Distributed systems are designed to provide a communications infrastructure that abstracts the low-level communication layers. They can also take advantage of object-oriented programming (OOP) to provide a framework for encapsulation and reuse of code. This combination of distributed technology and OOP speeds up application development and facilitates the integration of diverse applications. In today's model of OOP, there is tight binary coupling between an application and the classes of objects that it uses. Often, an application implements everything in a single language running in a single process on a single machine. An "interprocess object model" allows a process in one address space to request the services of an object in another address space. And in an object-oriented distributed system, an object on one host can invoke a method across a network on an object located on a remote host. There are several conditions that must be met before objects in different address spaces, and on different hosts, can communicate. For example, an application that is making a request must be able to locate the processing capability that it requires. A requesting application must be able to send parameters and data to the process it is calling, and must be able to receive results from that process. The parameters and results of the process must be meaningful on various machine architectures within the network environment. And the process may need to be usable in different implementation environments and languages. Java has always had networking capabilities through the classes in the java.net package. These classes support sockets, which provide a basic level of general communication. However, using sockets means that the client and server both need to use applications-level protocols to encode and decode messages for exchange. And creating these protocols is a tedious and error-prone process. Remote Procedure Call (RPC) is another method of network communication. It abstracts the communication interface to the level of a procedure call. This allows programmers to work as if they're calling a local procedure, although in reality the arguments of the call are packaged and sent to a remote procedure. RPC systems encode arguments and return values using an external data representation (XDR). RPC does not work well in distributed systems as they feature communication between program-level objects that reside in different address spaces. Objects provide an enabling technology for distributed systems. Their high level of abstraction shields the programmer from the complexity of the underlying communication mechanism. As you know, they inherently combine data and methods, and separate
Page 309 Go To INDEX

interface from implementation. So they are ideally suited to distributing data and methods to applications across a network. Distributed object systems extend the capabilities of object-oriented programming. Objects can be located on different computers across a network, residing in their own dynamic libraries. So although they are not part of an application, the application can use them in the same way as it would if they were local to it. And software and hardware resources on several platforms can be utilized in a single application. Clients in a distributed object system are not aware whether objects are local or remote. In fact, they do not need to know what kind of machine an object resides on. So migrating implementation objects from platform to platform becomes easier as it does not affect the clients. Both local and remote objects communicate in the same way. This means that the components of an application can be distributed to computers that are most suited to the task of each object. For example, objects that require intensive processing can be located on computers that are more powerful than standard PCs. Distributed objects are most often deployed in a client/server configuration. Distributed objects that provide services or resources in response to message requests are often called Server objects. Client objects, on the other hand, are those that request services and resources. Both types of object operate on the basis that the requester and provider may live on different machines across a network. Distributed object systems can be made compatible with existing systems using object wrappers. An object wrapper is an object-oriented interface to legacy code. So existing systems that are not object-oriented can be encapsulated with multiple object wrappers that will allow them to function within new systems. In fact, a single object can represent information derived from several existing systems. In the Java distributed object model, a distributed object is one with methods that can be invoked from another JVM. A distributed object is described by one or more remote interfaces. These are Java interfaces that declare the methods of the distributed object. There are several similarities between the Java distributed object model and the Java local object model. For example, a reference to a distributed object can be passed as an argument or returned as a result in any method invocation. A distributed object can be cast to any of the set of remote interfaces supported by the implementation using the builtin Java syntax for casting. And the built-in Java instanceof operator can be used to test the remote interfaces of a distributed object. The Java distributed object model differs from the Java object model in several areas. For example, clients of distributed objects interact with remote interfaces, and never with the implementation classes of those interfaces. Non-remote arguments to a remote method invocation and results from it are passed by copy instead of by reference. This is due to the fact that references to objects are useful only within a single JVM. A distributed object, on the other hand, is passed by reference instead of by copying the actual remote implementation. For distributed objects, the semantics of some of the methods defined by the class Object are specialized. The failure modes of invoking distributed objects are considerably more complicated than the failure modes of invoking local objects. Clients must deal with the additional exceptions that can occur during a remote method invocation.
Page 310 Go To INDEX

Two-tier and Three-tier platforms

Distributed systems can be characterized as either two-tier or three-tier architectures. A two-tier architecture consists of multiple client computers that provide the application logic for accessing a shared server. In a two-tier system, the business logic and information may be contained in either the client tier or the server tier, or it may be shared across both tiers. Business logic is the functionality required to complete a specific business task. In a two-tier system, each client operates using a separate database transaction that must fulfil certain conditions. For example, if a transaction fails before it completes, all of its results must be undone. And the results of a transaction must preserve the invariant properties of its objects. Further conditions that transactions in two-tier systems must fulfil are: The intermediate states of a transaction must not be visible to other users or transactions The intermediate states of a transaction must appear to execute serially The results of a completed transaction must be persistent Scalable two-tier client/server systems can be difficult both to create and to maintain, and do not have the flexibility that large organizations require. In response to these limitations, there is a movement towards three-tier distributed application systems. In a three-tier architecture, business logic and information are moved out of the client and server tiers, and placed in a middle tier. This structure frees the client from having to process business operations. So processing is offloaded from the client and the choice of platforms that can be used for the client is increased. This middle component of a three-tier system consists of a further server that may be located in a more powerful and better administered computing environment. In a distributed object system, the middle component is called an "object server". An object server is a distributed system component that stores persistent objects that are accessed through a communications protocol. The persistent objects reside on disk and are brought into memory as requested by applications. And the activation of the objects is transparent to programmers. When objects are activated in memory, they are capable of the direct execution of their methods. And they send execution requests to the object server through an Object Request Broker (ORB) or equivalent mechanism. Object servers must be able to store large numbers of objects, although all objects will not be active at any particular time. They must be able to handle hundreds of simultaneous requests for object method execution. These requests can come from different clients, each operating in a separate transaction environment. Object servers must also prevent unauthorized access to objects. Transactions between object servers and clients must fulfil the same conditions as do those between clients and servers in a two-tier system. This can be complicated if business object models contain relationships that cross object server boundaries. In this case, multiple servers must be coordinated for each transaction. Business objects can be used to describe real-life concepts such as customer, product, and order. These objects can be seen as being independent of applications, tools, databases, and

Page 311

Go To INDEX

systems. A consortium called the Object Management Group (OMG) has set up a Business Object Task Force to provide concrete definitions for them. In fact, applications will merely provide the environment to execute groups of business objects. Business objects are ideal for creating scalable three-tier client/server systems because they are not single unbroken pieces of code. They can be broken down into components and reassembled into a three-tier client/server structure. The breakdown of business objects into components followed here is that of the Common Object Request Broker Architecture (CORBA) model. The first tier consists of visual objects that each provide different views of the business object. These objects define user interfaces and usually reside on the client. The middle tier contains the persistent server objects, which represent the persistent data and business logic functions. And the third tier contains legacy servers and databases. The middle-tier server objects communicate with the client view objects and implement the logic of the overall business object. These server objects provide an integrated model of the different data sources and legacy servers. So the clients do not need to be aware of the various functions, stored procedures, and databases that reside in the third tier. Clients should never directly interact with the third-tier components. These components should be completely encapsulated and abstracted by the middle-tier server objects. For example, the clients should not be affected if one third-tier server is swapped with another.

Distributed Object Models

There are several distributed object models currently available. The main competitors are the Object Management Group's Common Object Request Broker Architecture (CORBA) and Microsoft's Common Object Model (COM). The Object Management Group (OMG) is an non-profit consortium formed in 1989 to devise standards for object-oriented technology. Its membership consists of over 700 software companies, and includes many of the leading software providers. The OMG's aim is to simplify distributed object systems and reduce their cost, making it possible for greater numbers of companies to use them. To achieve this, it has created standards that allow interoperability and portability of distributed object applications. The OMG have created an Object Management Architecture (OMA) which is made up of four main standards. The Common Object Request Broker Architecture (CORBA) standard specifies the architecture of an Object Request Broker (ORB). An ORB lets objects transparently make requests to and receive responses from other objects located either locally or remotely. The Common Object Services Specification (COSS) provides a standard interface for system-level services. These include tasks such as creating and relocating objects. The Common Facilities standard specifies common application functions such as document management, printing, and e-mail. And standard objects, known as Applications Objects, have been developed to provide common business functions. The OMG's Business Objects provide a natural way to describe application-independent concepts. One of the ultimate aims of object-oriented technology is to provide components such as these that more accurately mirror objects and behavior in the real world. CORBA defines a system that allows objects to communicate in a heterogeneous, distributed environment.

Page 312

Go To INDEX

It specifies an architecture and an interface that allow applications to make requests from objects in a transparent, independent manner. So it is designed to be both platform and language independent. CORBA's design is derived from the OMG Object Model. The OMG Object Model defines common object semantics for specifying the external traits of objects. It strictly separates interface from implementation, and defines an object's class by its interface. In the OMG Object Model, clients request services from objects through an interface. The interface is specified in the OMG's Interface Definition Language (IDL). An IDL is designed to allow language-independent expression of interfaces, including the complete signatures of methods. This is achieved by mapping the IDL syntax to whatever language is used to implement client and server objects. A client request is an event containing information such as: An operation Parameters (if any) The object reference of the service provider The object reference is an implementation-defined type that reliably identifies an object. CORBA's most important component is the ORB. The ORB allows objects to interact in a heterogeneous, distributed environment, regardless of the techniques used to implement the objects. When it receives an invocation request, it determines which object can handle the request, and passes the request and any parameters to that object. The part of the ORB that is responsible for communicating of requests is called the ORB Core. Requesting clients communicate with the ORB Core through an IDL stub. Clients can also communicate with the ORB Core through the Dynamic Invocation Interface (DII). An IDL stub represents a mapping between the language of implementation of the client and the ORB Core. So the client can be written in any language as long as the implementation of the ORB supports this mapping. When the ORB Core receives a request, it transfers it to the object implementation. This receives the request as an up-call through either an IDL skeleton or a dynamic skeleton. A skeleton is a server-side entity for a remote object that contains a method that calls the remote object implementation. If an object that is the target of a request is not currently active, the ORB Core loads it and passes the request to it. Clients do need to inform an object when they are finished using it - so CORBA does not define exactly when an object stops running. There are many implementations of CORBA currently available. These vary according to: Compliance with CORBA Quality of support Portability Availability of additional features Orbix from Iona is fully compliant with CORBA version 2.0 and offers comprehensive support. There is also a version of Orbix called OrbixWeb that supports Java. VisiBroker from VISIGENIC is also compliant with CORBA 2.0 and it provides interoperability with Java. There are many other ORBs currently available. These include:
Page 313 Go To INDEX

IBM's System Object Model (SOM) Digital's ObjectBroker Sun's NEO/Joe CORBA-compliant ORBs from the various vendors can interoperate. OMG has defined a General Inter-ORB Protocol (GIOP) to specify a standard transfer syntax between ORBs. It defines seven message formats that cover all ORB request/reply semantics - so no format negotiations are necessary. GIOP is designed to work over any transport protocol that meets a minimal set of conditions. And although versions of GIOP implemented using different transports are not directly compatible, their interaction is much more efficient. OMG have specified how GIOP will be implemented using TCP/IP - the most popular vendor-independent transport protocol. This protocol is known as Internet Inter-ORB Protocol (IIOP). It makes it possible to use the Internet as a backbone ORB through which ORBs from different vendors can communicate. GIOP provides for a set of Environment-Specific Inter-ORB Protocols (ESIOPs), which would be used wherever implementations using their transport are popular. GIOP also defines a format for Interoperable ORB References (IORs), which describe how to contact a remote object using a specific ORB's mechanism. There are several products that perform similar functions to CORBA, but are not based on it in any way. Microsoft's Object Linking and Embedding (OLE) aims to provide standards for language-independent, distributed object systems. However, Microsoft's conception of distributed object technology is different to CORBA's. OLE's many functions, such as embedding, dragging and dropping, and in-place activation, are based on Microsoft's Component Object Model (COM). COM specifies a binary standard for object interaction. The basic components of COM are known as "Windows Objects". These objects provide functionality for COM and obey the object-oriented principle of encapsulation. When a client accesses a Windows Object, it cannot directly manipulate it - however, it is allowed to see the object's interfaces. An interface acts as a pointer to a table of function pointers, and an object may support several interfaces. So a client may hold several interface pointers to a single object. COM differs from CORBA, where an object presents only one interface to a client, and each client holds only one object reference to the object. However, COM is similar to Java, which also allows one object to present many distinctly identifiable interfaces to its clients. All Windows Objects must support the most basic interface - "IUnknown". It supports three methods that give all Windows Objects their basic functionality. The QueryInterface method allows a client to ask which interfaces an object supports. And the AddRef and Release methods manage reference counting for objects. Reference counting allows a system to track the number of clients holding a pointer to any of an object's interfaces. Once the system detects that no clients have any pointers to the interfaces of an object, it can delete the object and recover its resources. As you know, CORBA does not define exactly when an object stops running. Microsoft has specified a set of more than 60 interfaces that make up the OLE architecture. These interfaces enable OLE's functions such as linking, embedding, and in-place activation.

Page 314

Go To INDEX

COM has a different way of identifying objects to that of CORBA. Making object names globally compatible across distributed systems can cause problems for CORBA. In a dynamic environment, name clashes can result in applications linking up with the wrong object, with potentially serious results. Microsoft has created Globally Unique Identifiers (GUIDs) to avert the problem of name collision. These are 128-bit integers that are guaranteed to be unique. Run-time support for COM is provided by a library of API functions (compobj.dll) for object creation and marshaling. Marshaling refers to both: Translating parameters and delivering them to a method invocation across address spaces Returning results from method invocations across address spaces When compobj.dll creates an object, it returns a pointer for the first interface of the object to the requesting client. This type of implementation creates a barrier between users of the object and its implementation language. When a client requests that an object be created, it specifies the class identifier (CLSID) of the kind of object that it wants to create. This maps to a particular file to load, and controls which code runs for the created object. However, it does not necessarily indicate all the interfaces that the object supports. If the name of the file that the CLSID maps to is changed, the interfaces that an object supports may also be changed. So in this respect, COM does not conform to the generally accepted object-oriented conception of class. Implementation inheritance is the ability of one object to inherit code from another. COM does not support implementation inheritance as Microsoft believes that it is not suitable to interprocess object models. Instead, it offers "aggregation", which allows an object to be constructed from subobjects, as an alternative model of code reuse. Aggregation is not a complete substitute for implementation inheritance. While inheritance is a syntactic mechanism enforced automatically by the language, aggregation is a convention that can be implemented in many ways. And while inheritance requires little or no code to support it, programmers must support aggregation completely. CORBA does not support implementation inheritance either. IBM's version of CORBA - System Object Model (SOM) - does support implementation inheritance in some cases. However, both CORBA and COM support interface inheritance - the ability of one interface definition to inherit from another. So in this respect both COM and CORBA are equally object-oriented. Recently, Microsoft has upgraded OLE version 2.0 to ActiveX, and created a distributed version of COM called DCOM. IIOP allows limited interoperability between OLE and CORBA. And CORBA developers have also built point-to-point gateways between OLE and the various ORBs. These bridges can be used as gateways to link OLE desktop front ends to CORBA servers. However, the differences between the models make interoperability complex and difficult.

Page 315

Go To INDEX

RMI and IDL

Java has the potential to end the era of competitive, incompatible object models. JDK 1.1 has addressed the needs of distributed object computing directly by adding distributed extensions to Java. It provides two separate new solutions for allowing objects to communicate over a network. Remote Method Invocation (RMI) allows an object to invoke another object that is running on a remote JVM. It is a Java-specific mechanism that works seamlessly with the existing Java environment. In fact, it can be seen as the Java version of a remote procedure call. Using RMI, once a reference is obtained for a remote object, it is treated just like a local object. The code accessing the remote object may not be aware that the object resides on a different machine. But obviously RMI can be used only to interface with servers written in Java. RMI uses Java interfaces and a stub compiler to provide access to remote objects. The interface specifies the methods contained by the remote object and a Server class is defined to implement the interface. The stub compiler is invoked to create classes that form the link between local and remote objects. RMI contains a naming feature that allows servers to bind object references to URLs. And a Naming class allows Server classes to make remote objects visible to clients. JavaIDL provides a Java interface into CORBA, so it goes beyond simple remote method invocations. As CORBA is language-independent, JavaIDL can allow a Java client to make a remote method call on a C++ object for example. And it can also allow a C++ client to make a remote method call on a Java object. The CORBA functionality in Java 1.1 provides a standard mapping from CORBA data formats to Java. The current Alpha 2 release of JavaIDL is not yet compliant with CORBA version 2.0, so it is not yet IIOP-compliant. However, it is intended that this functionality will be built into its next release. An IDL compiler is used to map the Java code to IDL. The IDL compiler provides stub classes, which call an ORB Core that determines the transport mechanism and marshaling parameters. An application that uses the IDL compiler can connect to any object server that is CORBA-compliant and uses a compatible transport mechanism. Java objects must be specified using the IDL, and access must be through library routines that translate the calls into the appropriate CORBA messages. The introduction of RMI and IDL have raised the level of abstraction from RPC, as each provides a way to access data and methods in the form of objects over a network. In fact, distributed object systems require remote object invocation to match the semantics of object invocation. As you know, Java can be used to provide interoperability between IIOP and COM. This is also true for DCOM - the new distributed version of COM. CORBA-compliant Java objects can be created and located on web servers, and Java applets can call them back using IIOP. Microsoft's implementation of the JVM relies completely on COM, and is treated as an ActiveX component. This means that for a Java programmer, there is no difference on Windows platforms between Java methods operating on COM objects or native Java objects. And ActiveX components written in a non-Java language, such as C++, can operate seamlessly with native Java objects in the JVM. Netscape's open network environment (ONE) is a network-centric application development environment that is supported by Netscape's Navigator and Suitespot products. It incorporates Java and IIOP, and will also include some ActiveX compatibility. So there is a growing Java-driven convergence of

Page 316

Go To INDEX

distributed object technologies. This convergence overcomes the limitations of traditional two-tier and three-tier client/server models. However, due to the scale and complexity of the Internet and intranets when compared with LANs and WANs, the new generation of environments will be difficult to design. Java ORBs that are CORBA compliant are currently being created. These are CORBA IIOP ORBs that are written entirely in Java for portability. The ORB must be able to generate Java bindings from CORBA IDL. Any code generated by the IDL compiler must be pure Java and therefore able to run on a JVM. A Java ORB allows an ordinary Java applet to invoke methods directly on CORBA objects using IIOP over the Internet. The client and server establish a direct communication link using the ORB. So the currently used HyperText Transfer Protocol (HTTP) and Common Gateway Interface (CGI) are totally bypassed. Sun has created two separate new products that enhance Java's ability to support distributed object computing. NEO is a distributed object environment, and a new release of Sun's previous product Distributed Objects Everywhere (DOE). The name NEO is not an acronym for anything. It is a three-tier client/server architecture that brings together Java, CORBA, and the Web. It also supports IIOP. NEO is comprised of several components including: Solaris NEO - the operating system component Workshop NEO - the software development environment NEO Administration Tools -converted versions of Sun applications NEO focuses on networked objects and shared applications. NEO Workshop tools can convert an application into a networked object if some new code is added. It is also hoped that NEO will provide version control at the application level, rather than at the programming level. The NEO system includes a Java ORB called Joe (Java Objects Everywhere). It interfaces Java programs to a distributed computing environment. By adding a little code, it allows Java programmers to declare a remote object, locate it with respect to a NEO name server, and then execute any of its methods. And it includes an IDL-compiler that generates Java client class stubs automatically from standard CORBA IDL files. Joe is primarily a client-side Java ORB. However, Joe clients can receive asynchronous calls from a server using a callback service. A callback is a call from a server to a client - allowing the server to become a client. CORBA ORBs are typically both clients and servers. Three steps are required to create a Joe client application. The first is to compile the IDL interface of the remote object using the IDL-to-Java compiler. Next the Java application that will access the remote object should be created. Finally, everything should be compiled with the Java compiler. With the common format provided by Joe, it is possible to communicate with nonJava applications, and non-Sun platforms. This is comparable to the way TCP makes it possible for UNIX, Windows, and other platforms to communicate, and may be as significant for distributed object computing.

Page 317

Go To INDEX

11.2. An overview of Java RMI • RMI architecture

Java Remote Method Invocation (RMI) is a distributed object model for the Java language. It defines a set of remote interfaces that can be used to create remote objects. It uses the semantics of the Java object model, making it easy to implement and use distributed objects. In fact, in order to match the semantics of local object invocation, distributed object systems require remote method invocation. Java RMI is designed specifically to function in a Java environment. It works seamlessly with the homogeneous Java Virtual Machine (JVM) environment so it can take advantage of the Java object model whenever possible. However, it can only work with objects that are written in Java. Java RMI is included as part of JDK 1.1. And RMI applets that are created with JDK 1.1 can be run with the applet viewer. Other distributed object systems, such as CORBA, can be configured to work with Java objects. However, they cannot achieve as much integration with Java as Java RMI because they must also interoperate with other languages. Java uses RMI to support distributed objects in order to facilitate: Integrating the distributed object model into Java Allowing seamless remote calls on objects in different JVMs Supporting callbacks from servers to applets Simplifying writing distributed applications Maintaining the security of the Java run-time environment In order to fulfil its goals the RMI model must be simple, easy to use, and must fit naturally into the Java language. RMI must also allow extensions such as garbage collection of remote objects, server replication, and activating persistent objects to service a call. These extensions should create as little extra work as possible for the servers that use them, and should be transparent to the client. In order to support extensions, the RMI system needs to provide several invocation mechanisms and it should be extensible to other invocation models. It also needs to support different types of reference semantics for remote objects, such as live references, persistent references, and lazy activation. And it should support multiple transports and the distributed garbage collection of active objects. RMI can be defined as the action of invoking a method of a remote interface on a remote object. Invoking a method on a remote object uses the same syntax as invoking a method on a local object. A Java program can invoke a remote object once it obtains a reference to the remote object. When a client object makes a remote call, method parameters must be sent to the server object, and a return value must be sent back to the client. This is made possible through two new Java features - object persistence and serialization. A Java object does not usually last after the program that created it is finished running. However, a "persistent" object has the ability to record its state so that it can be reproduced at a later time. And a file can be used to store the state of a persistent object. An object records itself by writing out the values that describe its state. This process is
Page 318 Go To INDEX

known as serialization because the object is represented by a stream of bytes. Object serialization is available as part of JDK 1.1. The main task of serialization is to record the instance variables of an object. If a variable is a reference to another object, the other object must also be serialized. In fact, all referenced objects in an object's ownership hierarchy - or graph - must be serialized. Java contains classes that write objects to streams and restore objects from streams. However, only objects that implement the Serializable interface or the Externalizable interface can be serialized. RMI uses serialization to transmit parameters and return values across address spaces. In this way, a Server object can access a Parameter object's entire graph of referenced objects. And if a remote method constructs and returns a complicated object that includes references to other objects, its entire graph can be returned. The Java RMI system consists of three layers. The stub/skeleton layer contains client-side stubs and server-side skeletons. The remote reference layer carries out the semantics of the invocation. And the transport layer sets up and manages connections, and tracks remote objects residing in its address space. The boundary at each layer is defined by a specific interface and protocol, making each layer independent of the others. This means any layer can be replaced with a different implementation without affecting the other layers. The application layer sits on top of the three layers that make up the RMI system. It consists of client and server objects. A method call from a Client object to a remote Server object travels down through each layer of the RMI architecture to the client-side transport. It then travels up from the server-side transport through each of the other serverside layers to the target object. The stub/skeleton layer is the interface between the application layer and the rest of the RMI system. It does not deal with the specifics of any transport. It is responsible for transmitting data to the remote reference layer by serializing the data and marshaling it to the marshal stream. A stub resides locally on a client's machine, and acts as a proxy for a remote object. It implements exactly the same set of interfaces that are supported by a remote object. When a client invokes a method on a remote object, it uses the stub to connect to the object. So the reference that a client holds to a remote object is in fact a reference to a local stub. A stub initiates an invocation on a remote object by calling the remote reference layer. It obtains a marshal stream from the remote reference layer and it serializes the call's parameters and passes them to the stream. It then tells the remote reference layer that the call should be invoked. When a stub receives return values or exceptions (if any) from a call, it deserializes them and informs the remote reference layer that the call is complete. Serializing and deserializing can be referred to as marshaling and unmarshaling. A skeleton for a remote object is a server-side construct that contains a method that dispatches calls to the remote object implementation. The skeleton unmarshals arguments from the marshal stream and makes the up-call to the remote object implementation. It then marshals the return value of the call (or the exception) on to the marshal stream and passes it back to the remote reference layer. So objects themselves are not passed across distributed systems - instead references are passed using stubs and skeletons. The appropriate stub and skeleton classes for a call are determined at run time and are dynamically loaded as needed. Stubs and skeletons are created using the rmic compiler. The remote reference layer maintains a specific remote reference protocol that
Page 319 Go To INDEX

is independent of any stub or skeleton models. This provides the flexibility to change this layer without affecting the other two layers. The remote reference layer handles the reference semantics for the server. For example, it abstracts the ways that objects are referred to, taking into account whether they are implemented on: Servers that are always running Servers that run only when they receive a method call So the layers above the remote reference layer do not need to be aware of these differences. The remote reference layer consists of a client-side component and a serverside component. During each method call, the two components carry out specific remote reference semantics. And they communicate with the lower-level transport layer. The client-side component contains information about the Remote Server objects. It communicates through the transport layer to the server-side component. And the serverside component delivers the remote method invocation to the skeleton. The transport layer carries out the implementation details of connections between Client and Server objects. It consists of several abstractions that are used to denote transport processes. It also maintains a table of remote objects that reside in its address space. When the transport layer receives a call from the client-side component of the remote reference layer, it locates the RMI server for the requested remote object. It then establishes a socket connection to that server and passes the connection to the client-side component. It also adds a reference for the remote object to its table of remote objects. And the client is then connected to the remote server. The transport layer monitors the activity level of the connection. It will shut connections down if the designated period of ten minutes elapses without activity. Once the connection is established, the transport layer forwards that remote call up to the remote reference layer. As you know, this handles any necessary server-side requirements and hands the call over to the skeleton. And the skeleton makes an up-call to the remote object implementation, which carries out the actual method call. The return value of the call is sent back through each of the three layers on the server-side. And then it is sent up through the transport, remote reference layer, and stub on the client-side until it reaches the requesting client.

Remote classes and Interfaces

The RMI system consists of several different interfaces and classes. These are defined in the java.rmi and java.rmi.server packages. There are certain rules that remote interfaces must follow. All remote interfaces must be declared to extend the java.rmi.remote interface. And a remote object that is passed as an argument or return value must be declared as the remote interface, not the implementation class. This is true regardless of whether the remote object is passed directly or embedded in a local object. The methods of remote interfaces must follow certain rules. The RMI Compiler (rmic) requires that each method is public. And each method must declare the java.rmi.RemoteException exception in its throws clause, as well as any application-specific exceptions. RemoteException is the superclass of all RMI exceptions that can be thrown by RMI at run time. It is used to indicate that a problem occurred during a remote invocation - for example, an interruption
Page 320 Go To INDEX

of the physical network connection. RemoteException enables interfaces to distinguish local or method-specific exceptions from exceptions that are thrown by the distributed object mechanisms. And it allows the application making the invocation to determine how to handle the remote exception. RMI server functions are provided by the java.rmi.RemoteObject class and its subclasses: java.rmi.RemoteServer java.rmi.UnicastRemoteObject The RemoteObject class implements the Object class behavior for remote objects. It implements the Remote interface and the Serializable interface. And it provides the remote semantics of the Object class by implementing methods for hashCode, equals, and toString. Implementing the hashCode methods allows remote object references to be stored in hashtables. And implementing the equals method allows the references to be compared. This method returns true if two Remote class objects refer to the same remote object. Implementing the toString method returns a string describing a remote object. The syntax and contents of this string may vary as they are specific to the implementation. All other Object class methods retain their original implementations. The RemoteServer class abstractly provides the functions needed to create objects and make them available remotely. It is the superclass of all servers that provide remote access to objects. And it provides the framework that supports a wide range of remote reference semantics. The getClientHost method allows an active method to determine the host that initiated the remote method active in the current thread. The ServerNotActiveException is thrown if no remote method is active on the current thread. The setLog method logs RMI invocations to the output stream that is specified. If this field is left empty, call logging is switched off. And the getLog method returns the stream for the RMI call log so that information on the application can be written to the log in a coordinated manner. The subclasses of the RemoteServer class concretely provide the functions needed to create remote objects and identify the semantics of remote references. The UnicastRemoteObject class is currently the only subclass supported by RemoteServer. It defines a remote object as a unicast (singleton) object, which means that it can only handle one client reference at a time. If several clients try to access a remote object, they will be queued and each will gain access only when the last reference is given up. The UnicastRemoteObject class provides support for point-to-point active object references using TCP-based streams. However, references are valid only while the server process is running. The java.rmi.Naming class allows server classes to make remote objects visible to clients. All the methods of the Naming class are static and do not need to use an object instance. When a server wants to make an object available, it calls the bind or rebind method with the name of the object. It also includes a reference to an object that implements an interface that extends Remote. A program called the RMI Registry provides a simple naming look-up service and maintains the binding when an object is called. The Naming class allows remote objects to be defined using the URL syntax. So clients can call the lookup method with a string representation of the URL for the object they want to access. The URLs are of the form rmi://host[:port]/name. Here, rmi is the
Page 321 Go To INDEX

protocol, and host is the name of the host that the object's server resides on, and port gives the option to include its port number. And name is the name of the object.

11.3. Creating an RMI application • Creating interfaces for remote objects

There are several steps that you need to follow to create, compile, and run an RMI application. First, you need to define interfaces for the remote classes. Next, you create and compile implementation classes for the remote classes. And then you use the rmic utility to generate the stub and skeleton classes from the implementation classes. Your next step is to create and compile your server application. Then you start the RMI Registry and the server application you have just created. Next, you create and compile a client program to access the remote objects on the server application. And finally, you test the client. The following example describes how to create a simple banking application. This application allows a remote client, such as an automatic teller machine (ATM), to access bank account data on a server. The server allows transactions to be performed against bank accounts, and allows new accounts to be created. The application uses two remote classes: Account AccountManager The Account class allows the balance of an account to be displayed, and money to be deposited in and withdrawn from an account. It also sets the personal identification number (PIN) for an account. The AccountManager class keeps a list of Account objects, and allows new Accounts to be created. This is the interface for the Account class.

You can see that it belongs to the atm package. The rmic requires that the stub and implementation code resides in a package. And you can see that it imports the java.rmi package. As you can see, the Account interface is a public interface and, like all remote interfaces, it extends the Remote interface. The first method retrieves the current balance
Page 322 Go To INDEX

of an account. The next two methods allow money to be deposited in and withdrawn from an account. And the last method allows the ATM card's personal ID number to be set. You can also see that each method throws the RemoteException. This is the AccountManager interface and, as you can see, it belongs to the atm package.

It also imports the java.rmi package. And like the Account interface, it is a public interface that extends the Remote interface. The first method of the AccountManager interface finds an existing account for a given customer. If there is no account for a given customer, null is returned. The second method creates a new account and allows a PIN number and a starting balance to be specified.

Generating stubs and skeletons

Once you have created your remote interfaces you need to create server classes that implement them. These implementation classes must contain the code for the methods declared in the remote interfaces. The implementation class for Account is called AccountImpl.

It is the remote object that will be referenced by the stub on the client-side and the skeleton on the server-side. As you can see, the AccountImpl class belongs to the atm
Page 323 Go To INDEX

package. It imports the java.rmi package and the java.rmi.server package. It also imports the java.io.Serializable interface to allow the parameters and return values of its objects' methods to be transmitted across address spaces. The AccountImpl class extends the UnicastRemoteObject class, which all remote implementation classes must extend. And it implements the Account interface and the Serializable interface. The AccountImpl class has three private member variables which specify the current balance of the account, its PIN number, and the name of the account holder. This method constructs the AccountImpl class. Here, each of Account's methods are implemented: Returning an account balance Depositing money in an account Withdrawing money from an account Changing the pin number

This is the AccountManagerImpl class - the implementation class for the AccountManager interface.

Page 324

Go To INDEX

As you can see, it belongs to the atm package. The AccountManagerImpl class imports the java.rmi and java.rmi.server packages to give it remote capabilities. It also imports the java.util.Hashtable class so that hashtable data structure facilities can be used. As you can see, AccountManagerImpl extends UnicastRemoteObject and implements the AccountManager interface. This is the default class constructor code. In this case it does nothing. The first method creates a new account by putting the account holder's name and account details into the hashtable. It contains the accountName, pin, and startBalance parameters. This method searches the hashtable for an existing account when information on an account is requested.

If it cannot find an account for a given customer name, it throws Exception. As you have seen, both interfaces and both implementation classes belong to the atm package. Each of these four sources needs to be compiled with the -d directoryname option in order to specify a destination directory. The compiler will automatically create a subdirectory called atm in the destination directory. And it will create the class files in the atm subdirectory. The destination directory that you supply to the -d option should be in the class path. You can do this easily by specifying the current working directory as the destination directory, as shown here.

Page 325

Go To INDEX

After the implementation classes have been created, they are given to the rmic utility. And it automatically creates the stub and skeleton classes from the interface and implementation class details. This is the syntax you use to run the rmic.

And this is the necessary code to run the rmic for the AccountImpl class and for the AccountManagerImpl class.

The rmic creates four class files for the AccountImpl and AccountManagerImpl stubs and skeletons in the atm package directory. These are: AccountImpl_Skel.class AccountImpl_Stub.class AccountManagerImpl_Skel.class AccountManagerImpl_Stub.class

Creating a remote client and server

Once the stubs and skeletons classes are created, the next step is to create the server-side application. This part of the application allows remote clients to make method calls on the stubs and skeletons. In this case, the server class is called Bank. It instantiates the AccountManager object and makes it available to remote clients. The Bank class belongs to the atm package, and imports the java.util and java.rmi packages. Its first method creates and installs an RMI security manager.

Page 326

Go To INDEX

The next method creates an instance of AccountManager, and the system printout is "Bank: create an AccountManager". This method binds a specified name to the AccountManager object instance and registers it with the RMI registry. Any machine on the network can refer to the AccountManager object by specifying the host machine and the object name. You can see the system printout is "Bank: bind it to a name". You can see that the static rebind method is used so that naming conflicts are avoided. The bind method also associates a name with an object. But when the name being bound has already been bound to an object, bind() will throw the AlreadyBoundException, whereas rebind() will discard the old binding and enforce the new. The next method creates a sample account. As you can see from the parameters, the account holder is "jonwoo", the PIN number is "1234", and the initial balance is $1000. If the binding is successful, the system prints out this message: AccountManager is now ready The next step in the process is to compile the server class. You can do this by executing this command. javac –depend atm\bank.java Next, the server application should be started up. However, the RMI Registry must be running before the server application can be invoked. It resides in the java/bin directory and can be invoked by typing rmiregistry at the command prompt. This is the code you use to invoke the RMI Registry and start up the Bank server. rmiregistery java atm.bank If there are no errors, you should see this output.

Once an object has been passed to the RMI Registry, a remote client may request a reference to the object. So a client application that can make requests needs to be created and compiled. This client is called ATM and it simulates the behavior of an ATM machine that is connected to a remote banking server.

Page 327

Go To INDEX

For simplicity, a user interface is not implemented in this example. The ATM application checks the command line and expects two arguments and an optional third from it. The first argument specifies the server. If you are testing on a single machine, you should specify localhost as the server name. And the second argument is a string that provides an account name. The next piece of code creates and installs the RMI security manager. The ATM application needs to obtain a reference to the AccountManager object on the remote server so that it can access it. It assumes that the server name has been passed in as the first command-line argument.

This name is used to create a URL-type string of the format: rmi://hostname/AccountManager The string is passed to the static lookup method of the Naming class. The lookup method call communicates with the remote server. The lookup method returns the handle to the remote object that was created when the RMI Registry was run. This handle actually refers to the stub that communicates with the remote object. The lookup method returns the Remote interface, which is the parent of all stub interfaces. When the return value is cast to type AccountManager, the methods of AccountManager can be invoked on it. Once this has been done, the client can find the account manager object that represents a customer's account.

Page 328

Go To INDEX

The client program then makes several withdrawals and one deposit.

Once the client code has been written, the next step is to compile the client application. You can do this by executing this command. javac –depend atm\atm.java The final step in the process is to test the client. You can execute the client code from any machine that has access to the server and to the supporting classes. This is the command you use to execute the code for the "jonwoo" account, which is running locally on the server. And this is the output that is returned from a successful execution.

As you can see, it details the transactions that were included in the client application code.

Page 329

Go To INDEX

11.4. Java IDL • Java IDL mapping

The Object Management Group (OMG) was established to design specifications for object-oriented technology, which includes distributed object systems. These specifications make it easier for communication across a variety of hardware platforms and operating systems. The Common Object Request Broker Architecture (CORBA) is a standard introduced by OMG. It is designed to handle client requests on remote objects without the need to know: Where the object is located The programming language The type of operating system CORBA allows transparent access to objects and allows remote objects to appear as local objects to the client. CORBA deals with client requests using an Object Request Broker (ORB). To examine how an ORB works, imagine a banker (client) located in Chicago who wants to update the account details (remote object) for a customer's account on a remote server in San Francisco. When the banker submits the request for the account details, methods are invoked on the remote object. In this example, a single remote object contains the account details. The ORB intercepts the request and locates the appropriate object capable of implementing the method. It's responsible for passing the parameters to this object, invoking the method, and returning the results to the client. The banker in Chicago doesn't need to know specifics about the object like the programming language or the operating system to access the account detail information. ORBs can implement client requests through interfaces defined by an Interface Definition Language (IDL). IDL is used to describe object interfaces. As IDL cannot implement object interfaces, it's not described as a programming language. IDL definitions are used to map CORBA object specifications with objects in programming languages like C, C++, or Java. As CORBA is designed to be language - and platform-independent, this allows systems written in different languages and on various platforms to interoperate. Mapping IDL into Java entails converting CORBA objects into Java classes. The process of Java IDL mapping starts with writing the IDL definition. IDL code is compiled by a compiler such as the idlgen utility. When the IDL definition is passed through idlgen, a number of skeleton and stub files are automatically generated. Stubs are referred to as client mappings and contain methods required for client requests. Skeletons are referred to as server mappings. The programmer must add implementation code to the skeleton files by creating the implementation objects and a server application. At this stage of the process, there are a number of Java files that can be compiled by the Java compiler, javac.

Page 330

Go To INDEX

The compiled client stubs can be used by a programmer to build client applications to enable access to a remote server object. As IDL to Java mapping begins with an IDL definition, let's look at the features of an example IDL file.

This file, Account.idl, is used to manage bank accounts for the ABC bank. IDL definitions of object interfaces contain: Attributes Operations Operation parameters

Page 331

Go To INDEX

IDL attributes are the equivalent of Java variables, and IDL operations are the equivalent of Java methods. The Account interface defines the attributes accountName and balance. Both attributes are specified as read-only which means that any client can read these values but not set them. The operations provided with the Account interface are credit and debit. The "in" attribute on each parameter specifies that they are passed in by value. IDL uses comments in a similar way to Java. Here they're illustrated with two forward slashes "//". IDL definitions can be compiled into Java source code using a utility called idlgen which generates stub and skeleton mappings for remote objects. The idlgen development tool currently runs on: Solaris 2 on SPARC Windows 95 Windows NT on x86 systems. You invoke the idlgen compiler at the command line using the syntax: Idlgen <filename>. It's not necessary to specify the filename extension .idl. There are a number of options available when you run this utility. This syntax incorporates options into the idlgen command. Idlgen [options] <filename>. To specify a directory where Java files are written to during compilation, you use the -j option.

Here the Account.idl file is written to the ABCBank directory located on the c drive. The default mapping of IDL modules is to the global Java package. Mapping IDL declarations in this way could result in duplication of class names. The -p option places Java symbols in a specified package. Idlgen –p <filename>. Examples of symbols used in this option are the Java dot symbol "." and the directory/filename forward slash "/" symbol. The -J option allows you specify a filename, for example, genlist.txt which contains a list of all the files generated when Account.idl is compiled.

Specifying a list of Java files in this way is useful when you want to: Clean up Control building Another useful option is the -e envfile or environment file. An environment file supplies attributes required for mapping IDL to Java. These attributes represent variables in Java and when you specify an environment file, the idlgen compiler reads the contents of this file. Options passed during preprocessing of IDL files include: -I, which is the name of a directory where a search is performed on IDL files containing #included statements
Page 332 Go To INDEX

-D, which is the symbol to be defined during preprocessing -U, which is the symbol to be undefined during prepossessing When the IDL definition is compiled by idlgen, the programmer then provides the implementation code required to continue the mapping process.

Implementations for IDL objects
Let's examine how implementation code is written for this IDL file.

Modules in IDL are a means of grouping interfaces or definitions with common functions and are mapped to a Java package.

The IDL module ABCBank is mapped to a Java package of the same name. IDL declarations not defined in a module are mapped to a global package in Java called "idlGlobal". When writing IDL declarations, it's a good idea to define them inside IDL modules. The public class, AccountImpl, implements the interface ABCBank.AccountServant. This interface was automatically generated by the idlgen utility. Two instance variables associated with AccountImpl are accountName and balance.

These are derived from the accountName and balance attributes specified in the IDL file.

Page 333

Go To INDEX

When a new instance of the AccountImpl class is created, the constructor AccountImpl is invoked. The arguments specified by this constructor are copied into the instance variables, accountName and balance. Two get methods that return the name and balance of the account are getaccountName and getbalance.

These two methods are used to return the contents of the two member variables. The credit method adds a specified amount to the account balance and the debit method subtracts a specified amount from the balance. To make the Account object available to clients, a server class is created. When an ORB receives a request from a client for a remote object, the ORB uses the server class information to locate the object.

Page 334

Go To INDEX

This code must be written by the programmer as it is not automatically generated by the idlgen utility. The server class, AccountServer, is created as a public class and is responsible for implementing a server. The ORB that handles client requests is called the sunw.door.Orb and is part of the Java IDL system. It calls the method sunw.door.Orb.initialize to start the ORB listening for client requests.

The AccountImpl class is instantiated and the AccountSkeleton.createRef method returns a reference r to this object. To make this reference available to an ORB, the reference is published by passing it to the sunw.door.Orb.publish method. This step involves binding the reference so that it's accessible by a URL. This code specifies the error message that appears in the event of a problem arising when trying to bind the object reference to a URL.

A message appears when the server class AccountServer is successfully implemented.

Page 335

Go To INDEX

Writing clients which access remote objects

Using the compiled client stubs, programmers can build client applications and applets that allow access to remote server objects. Here's an example of an applet used by clients to request or update bank account details for a bank account residing on a remote server.

This applet contains fields where a client can enter: The host name of the machine on which the account resides Transaction amounts Account details are queried by entering the host name and selecting the Connect button. The results are displayed in the Account Balance field. When a client selects the Debit or Credit button, this invokes a change to the account balance by the amount specified in the Transaction Amount field. This change is displayed in the Account Balance field. The status line displays system messages associated with client requests. A reference to the remote object is held in the member variable ABCBank.AccountRef. The user interface is created using the init method.

Page 336

Go To INDEX

This method initializes the fields and buttons that appear in the applet. When a client enters a host name for an account, a URL is first created by the connect method for the account server.

To construct this URL, the account server is assumed to run on the default.sunw.door.ORB port.

A reference to an AccountStub ABCBank.AccountStub.createRef method.

is

created

by

calling

the

This reference r is then bound to a URL by the sunw.corba.Orb.resolve method. If problems arise when resolving the remote object reference to a URL, an error message is displayed. The updateStatus method uses the reference to the remote object to return the account name and balance details. This reference was returned by the connect method. The variables accName and balance are defined as strings and two get methods are called to return values associated with these variables. The results returned for a client query on account name and balance are displayed in the: Account Name field Account Balance field Exceptions arising from the updateStatus method are recorded by the systemException method and appear in the status line. The credit method describes what happens when a user enters an amount in the Transaction Amount field and then selects the Credit button. If the user selects the Credit button without first specifying the host name, a message is displayed on the status line informing the client to connect to the server first. If the user selects the Credit button before entering an amount in the Transaction field, an error message is displayed when a transaction is complete, the status line displays a message and the updateStatus method is called to return a new balance. The debit method describes what happens to the account balance when an amount is specified in the Transaction Amount field and the Debit button is selected.

Page 337

Go To INDEX

12 Java Animation and Images
12.1. Working with Images • Loading images from URLs and files
The Image class in java.awt provides abstract methods to represent common image behavior. Image data referenced by an instance of the Image class may exist anywhere on the Internet. The Java class library was designed to support asynchronous image loading. This background loading of images ensures that a large amount of processing time isn't taken up during initialization. Special methods defined in the Applet and Graphics classes enable you to load and display images. For example, the Applet class provides a method called getImage that retrieves an image and automatically creates an instance of the Image class for you. Images are stored separately from Java class files. To access these images, you have to tell Java where to find them. Currently, Java supports images in GIF and JPEG formats. Loading images is fairly straightforward provided you know their URLs and filenames and they are in a GIF or JPEG format. To load an image, you need to import the java.awt.Image class and then pass the image's URL to getImage(). The getImage method can take one or two arguments.

If you use getImage() with a single argument, you retrieve the image at that particular URL.

This form of getImage() is simple but inflexible. If any part of the URL changes, you have to recompile your Java code to take the new location into account. The twoargument form of getImage() allows for greater flexibility. The first argument is the base URL and the second is a string representing the path or filename of the actual image.

The Applet class provides getDocumentBase and getCodeBase methods to handle the base URL argument of getImage(). getDocumentBase() returns a URL object representing the directory of the HTML file containing the applet. In this example, the image1.PNG file is in the same directory as the HTML files that refer to the applet.

The getCodeBase method returns a string representing a directory containing the applet. In this case, the file image2.PNG is in the same directory as the applet itself.

Page 338

Go To INDEX

If you are using more than one file, it's useful to put them into their own subdirectory. In this example, getImage() looks for the file image2.PNG in the images directory.

Using either the getDocument or getCodeBase methods is more flexible than hardcoding a URL or path name into getImage(). Provided these methods are used, Java can still find your images even if you move HTML files and applets around. getImage() should be called from inside a method such as init(). If called in the constructor of an applet, getImage methods won't work because the applet doesn't have the full context (AppletContext). The Toolkit class also supplies getImage methods. You can get a Toolkit object either by calling Toolkit's getDefaultToolkit() or by invoking the Component getToolkit() instance method. Both Java applications and applets can use Toolkit's getImage methods to load images.

The loading of an image doesn't actually start until the first time a program tries to draw the image. The image that getImage() refers to isn't loaded until it's needed so that Java doesn't have to keep enormous images around in memory. Instead, Java just keeps a reference to the image data and retrieves what it wants at a later date. To ensure a program doesn't try to draw an image before it's fully loaded, some programs monitor the image loading process. Java provides the MediaTracker class and the ImageObserver interface to implement this.

Drawing Images

The getImage method can't display or draw an image. It merely returns an Image object. The Graphics class supplies several drawImage methods for displaying images. Although all drawImage methods have common elements, some provide additional features such as the use of background color.

If the image has finished loading, the return boolean value of drawImage() is true. The first form of drawImage() displays an image in its original dimensions. It takes four arguments - the image itself, the x and y positions of the top left corner, and the keyword this. The “this” keyword is an instance of a class that implements the ImageObserver interface.

The “this” argument is used to pass in an object that, implements the ImageObserver interface.
Page 339 Go To INDEX

The applet shown inherits from the Applet class which, by default, implements ImageObserver. The ImageObserver is responsible for checking whether an image is ready to be drawn or not. The Applet class contains default behavior for observing images and the “this” parameter refers to our applet. The second version of drawImage() takes an additional two arguments to represent the width and height of the image's bounding box.

If you specify dimensions larger or smaller than the original image, the image is automatically scaled to fit the box. Scaling an image smaller or larger than its original size can cause image degradation or distortion. To avoid this, you can check the size of an image in advance, and then scale it to a specific size. You use getWidth() and getHeight() to determine the size of an image. getWidth(ImgaeObserver) getHeight(ImageObserver) These methods take a single argument, an instance of a class that implements ImageObserver, which monitors image loading. In most cases, you can use the this keyword as an argument to getWidth() or getHeight(). The drawImage method returns after displaying the loaded image. If you want to make sure that only complete images are drawn, then you need to track the loading of images. Let's take a look at a sample program that displays an image and then scales it to half its original size. Retrieving the image is the first step.

Here let's draw the actual image of the car after it has been loaded. The getWidth and getHeight methods retrieve the image's original dimensions.

Page 340

Go To INDEX

You can then go on to specify the proportions you want in the width and height arguments of g.drawImage(). In this example, the size of the image is halved.

The image observer

For many programs, Java's background image loading works well. However, some programs need to keep a close track of image loading. In such cases, Java provides the media tracker and image observer to ensure that applets don't draw images until all the image data is loaded. The image observer is an update interface that monitors image information as an image is being created. The Component class implements the ImageObserver interface. As image data is loaded, the repaint method is called. When you call repaint(), you are asking Java to repaint your applet as soon as it can. Repaint() triggers Java to call paint() which is responsible for drawing the current frame. You can track image loading by calling the imageUpdate method. imageUpdate() is called when information about an image becomes available. This method signals that an image has changed in some way. The imageUpdate method returns false if all of the image data has been loaded.

If further updates are needed, true is returned. Let's take another look at the syntax of imageUpdate(). Its first argument represents the image you want to track. The flags argument provides status information about image loading. The x, y, width, and height parameters specify the bounding box of the image. If an image encounters an error while loading, the error flag is set and any attempt to draw the image will fail. The abort flag is set at the same time to indicate that image production has terminated. If an image is terminated before production is complete, the abort flag is set. Unless action is taken to trigger another image sequence, no more information on image loading will become available. If the error flag is not set at the same time, accessing any of the image data will restart production. When a previously-drawn static image is complete, the allbits flag is set. The image can then be redrawn in its final form. In the case of a multi-frame image, the framebits flag is set when another complete frame is ready to be redrawn. Let's take a look at some code that illustrates how the loading and drawing of images work.

Page 341

Go To INDEX

The init method is the first to run and here you specify which image to load. Next paint() is called, and it's at this point that the image actually starts to load. You must pass the this argument, an instance of a class implementing ImageObserver, to drawImage(). When image information changes, imageUpdate() is called. The imageUpdate method calls repaint() which in turn calls paint() over and over again. Because all the image data may not be loaded when the paint method finishes, only a partial image may be displayed. This means that the paint method has to be called repeatedly until all of the image is loaded. This constant redrawing of the screen catches your eye and creates flicker which detracts from the overall appearance of an applet. Fortunately, there are specific techniques you can use to produce professional, flicker-free applets.

The media tracker

Sometimes an applet or application cannot do anything practical until its images are fully loaded. This is particularly true of animations where delays can occur or blank frames appear if an animation starts before all its frames are ready. To address such problems, Java provides the MediaTracker utility class and addImage() to monitor the image loading process. Media tracker ensures that images are fully loaded before they are displayed. It can also group a set of images together. These features make it ideal for animation where tracking of groups of images is vital. Let's take a look at a sample program that ensures an image is loaded before it's drawn.

Page 342

Go To INDEX

First you use getImage() to retrieve your first frame. Then you create a specific instance of MediaTracker to track your images. It takes an instance of ImageObserver as an argument. The “this” argument of MediaTracker is an instance of ImageObserver and refers to the applet. Now you can use addImage() to include the image in group 0 and start the loading process. The first parameter of addImage is the image itself. The second variable is an ID number that controls the order in which images are fetched. Normally a program that produces several different animations will use one ID for each animation. All images for the first animation might use an ID of 0, and an ID of 1 for the second animation. Here checkID(0, true) determines whether images tagged with an ID of 0 have been loaded. If loading is finished, then a true value is returned and drawing can begin. The checkID method also returns true if an error occurs while loading or scaling an image. When loading is finished, the image is drawn in the top left corner of the applet's bounding box. Otherwise, the paint process is invoked again by repaint. The media tracker can monitor the loading of every image, or can be set to monitor only those with a specific ID. The waitForAll method pauses until all images have finished loading, are aborted, or receive an error. waitForID(int id) is more selective and will wait until all images with a specific ID have been loaded or terminated for some reason. You can also specify how long to wait for loading to complete.

The time is expressed in milliseconds. You may not want to take the time to load an image before starting your applet. When you call statusID(), you pass on the ID you want and a boolean flag to indicate whether it should start loading that image.

If a true value is passed, image loading will begin.

12.2. Animation • The animator applet

The easiest way to create an animation is to use the Animator class provided by the JDK. Animator is basically a general-purpose animation tool that allows you to create an animation quickly and easily. Animator is available in both 1.02-compliant and 1.1-only versions. To use the Animator tool, you first need to copy the following files to your classes’ directory: Animator.class ParseException.class ImageNotFoundException.class You also need to create image and sound files for your animations. Then you add the appropriate Animator applet parameters to your HTML file so that the Animator applet can read them. The animation can then be viewed in a Java-enabled browser. Most of the Animator applet tags are self-explanatory.

Page 343

Go To INDEX

For example, the BACKGROUND tag refers to the image to be displayed in the background. The PAUSE tag indicates the number of milliseconds of delay between frames. The default number of milliseconds is 3900. The tags for images need further explanation. The default name of images used by the Animator class starts with the letter T followed by a number. For example, if you have two GIF files that form part of your animation, you name them T1.PNG and T2.gif. If you wish to change the default name for images, you should use the NAMEPATTERN parameter. For example, if your images are called anImage3.jpg and anImage4.jpg, you should use the NAMEPATTERN parameter in this form.

You can specify the image sequence in an animation in two ways: Using STARTIMAGE andENDIMAGE tags Using the IMAGES tag The STARTIMAGE and ENDIMAGE tags let you specify a range of images, and both have default values of 1. If you specify an ENDIMAGE that has a lower numerical value than STARTIMAGE, the images will be displayed in reverse order. For example, setting STARTIMAGE="4" means that your frames will be displayed in reverse order from 4 to 1. If you specify ENDIMAGE="5", frames 1 to 5 will be shown. The IMAGES tag takes multiple inputs, and you can arrange animation images in any order. Let's assume you wish to display images T1.PNG, T6.gif, and T2.gif in that sequence. To do this, you simply insert the appropriate image numbers in the HTML file, using the “|” character as the separator.

The SOUNDS tag works in the same way as IMAGES, except that a value can be left blank if no sound is to be played. If a PAUSE tag is omitted, a standard pause is set up between images in the animation. The coordinates for displaying images are specified by using the POSITIONS tag. The default coordinates are (0,0). However, a "@" character is used to separate the x and y values of a coordinate. If you want an image to remain in the same place as the previous image, insert a blank in place of the coordinate. Let's assume you wish to display your first and second images at (50, 30), and the third image at (80,80). To do this, you insert an additional “|” separator between the coordinates of the first and third images.

A simple animation thread

Animation creates the illusion of movement by displaying successive images at a relatively high speed. Normally you need to display ten to twenty frames per second to create effective computer animation. To animate in Java, your first step is to set up an animation frame. Then you get Java to paint that frame. Repeating these steps will simulate the movement you need. Because animation programs draw repeatedly at regular intervals, they need an animation loop. An animation loop should be included in a thread's run method. It should never be included in paint() or update(), since it would then
Page 344 Go To INDEX

take over the AWT thread that is responsible for all drawing and event handling. An animation loop keeps track of the current animation frame. It also forces periodic screen updates. To run the animation, the loop should be in its own separate thread. To use threads in animation, you need to make a number of changes to an applet's code. First you need to add the words "implements Runnable" to the applet's declaration.

To hold the animation applet's thread, you need to include an instance variable. thread animator; Another animation requirement involves setting up start() so that it simply creates and starts the animation thread.

You also need to include stop() to suspend execution of the animator thread when a reader leaves the page.

Setting the animator variable to null makes it available for garbage collection so it can be removed from memory after a certain length of time. The run method handles the principal work of the animation thread. Here you include the actual animation loop that cycles through the images and displays them.

Let's take a look at a complete program that creates a simple animation thread.

Page 345

Go To INDEX

First, the words "implements Runnable" are added to the applet's declaration. You then set up an array of images for your animation. To keep track of each image in the array, you use an index counter. Next, you add the instance variable animloop to hold the applet's thread. To ensure images won't be displayed before they're fully loaded, you create an instance of the media tracker. Then you use getImage() to retrieve the images and add them to the media tracker. Here in the paint method, you check to see if all the images in group 0 have been loaded.

When the images are fully loaded, you display them using g.drawImage(). Now let's use start() to create and start the animation loop thread. The stop method suspends the animation thread when the reader leaves the page. The run method is called directly after start() and forms the main body of the program. Here you set up the for(;;) function to loop indefinitely. You use the index counter to cycle through the sequence of images.

Page 346

Go To INDEX

Then repaint() is called to display them. This code handles exceptions or errors and the animation thread pauses for 200 milliseconds. Finally you need to override update() so that it simply calls paint(). This will prevent the background being repainted between frames.

The IFC extension and animation

Sun's AWT is the effective standard for Java applet and application user interface elements. In theory, the AWT contains the basic tools to create a cross-platform graphical user interface. In reality however, many developers find that the interface elements of AWT's classes differ greatly from platform to platform. For example, a Java interface designed to look well on a Windows 95 computer may look considerably different on a UNIX machine or a Mac. Struggling to get the AWT to display consistently across platforms can be one of the most frustrating aspects of coding for Java developers. To overcome the limitations of the AWT, Netscape developed the Internet Foundation Classes (IFC) library. The IFC is completely written in Java and is built on top of the AWT. The IFC library replaces many of the standard AWT elements with its own. For example, it supplies its own classes in order to carry out simple tasks such as animation. Probably the most helpful animation features provided by the IFC include: A push/pop graphics model with a changeable clipping rectangle Off-screen buffering for memory-efficient, flicker-free drawing By enhancing the AWT system, the IFC library makes it easier for developers to create fairly complex user interfaces that will display consistently across platforms. This work can be accomplished without a huge coding overhead. IFC's main limitation is its size. An applet written in the IFC can routinely exceed 600K, whereas the AWT equivalent would be much smaller. The size issue will be overcome to some extent when Netscape starts including the IFC classes with Java classes in future releases of Navigator. On April 2, 1997, Sun and Netscape announced they were combining their technologies to jointly develop Java Foundation Classes (JVC). JVC will combine Java's AWT and Netscape's IFC to provide a powerful unified framework for Java application development. The IFC library provides the following classes for images and animations: Image
Page 347 Go To INDEX

Bitmap DrawingSequence ImageSequence The Image class is the IFC's foundation class for imaging and animation. In IFC terms, an image is an object that can draw itself within a rectangle. Both Bitmap and DrawingSequence classes are extensions of the Image class. The Bitmap class is used for working with bitmaps. The DrawingSequence class is used for working with animation frames. The ImageSequence class, a subclass of DrawingSequence, is used specifically for animating images. The fact that Bitmap, DrawingSequence, and ImageSequence are all subclasses of Image gives you a certain degree of flexibility. It allows you to supply different values for methods that take image as an argument. For example, you could specify either an animation or a bitmap in the setImage method. The DrawingSequence class provides IFC's basic animation support. To create an animation, you can subclass DrawingSequence to create a custom class. Then you override drawAt() to specify how the animation sequence should be drawn. To supply the animation's dimensions, you need to override width and height. If you want to define the direction of an animation sequence, you use setPlaybackMode(). The setFrameRate method specifies an animation's speed in milliseconds. You must use setFrameCount() to specify the number of animation frames or an exception will be thrown.

Methods of avoiding flicker

Flicker is a side-effect of creating Java animations. It arises because Java paints and repaints each frame of an animation applet. And each frame is painted incrementally. The constant repainting of an applet creates flicker, which detracts from the overall appearance of the applet. If you want professional-looking applets, you need to eliminate flicker as much as possible. In Java, a call to repaint() results in a call to update(). Applet's update method clears the applet by filling it with the original background color. It then calls on paint() to draw the contents of the current frame. Animation flicker actually happens because the update method clears the applet between frames. As a result, the parts of the frame that don't change switch rapidly between being painted and then being cleared. If the original color is white, for example, and paint() creates a much darker image, then the flicker effect will be very obvious. As a result, the animation will seem very jerky and disjointed. There are a variety of techniques you can use to eliminate flicker and produce smoother Java animations. These techniques can be applied to all images. In many animations, you only need to redraw a small area of the screen to simulate the movement you want. Unfortunately, paint() can only redraw the entire applet and not small portions of it. By limiting repainting to a part of the applet, you can eliminate much of the flicker that arises from redrawing a full screen. Clipping is a mechanism that allows you to limit the area of the screen that gets redrawn. Once you know the area that needs to be redrawn, you use the Graphics class clipRect() method.

Page 348

Go To INDEX

In this case, you reduce the drawing area to a rectangle sixty pixels wide and fifty pixels high. The clipRect method tells the system that it needs to draw only within a specific rectangle. While the full screen may get instructions to redraw, only the portions inside the clipping area are actually drawn. Besides reducing flicker, this is efficient and much faster than doing a full redraw. If a component consists of just one image, then overriding the update method will be sufficient to prevent flicker. The default implementation of update() clears the screen before calling paint(). Overriding update(), so that it simply calls paint(), prevents the background from being repainted.

When applets display more than one image, you need to use a technique called double buffering to eliminate flicker. With double buffering, you first create the image off screen. As soon as drawing is finished, you copy the off-screen image to your drawing area in one quick call. The drawing surface is then updated immediately. Because all the drawing is done in the background, partial images won't appear suddenly to disrupt the smoothness of an animation. Let's take a look at a program that prevents flicker by implementing double buffering.

First you store your off-screen image and graphics context in instance variables that can be passed on to paint(). During initialization, you create Image and Graphics objects and assign them to the instance variables shown here. The off-screen image area is based on the size of the applet. Here the program draws a white rectangle inside a black one. The i variable will be incremented later in the animation loop to change the dimensions of the white rectangle. Once the rectangles have been set up off screen, you
Page 349 Go To INDEX

can then draw the off screen image. To prevent screen clearance between frames, you need to override update() so that it simply calls paint().

The paint method will then display the contents of the current animation frame. By default, update() clears the screen by filling it with the original background color. Flicker arises when update clears the screen between frames. The start method creates and initiates the spinner animation thread. The run method is invoked directly afterwards. The i variable gets incremented each time it passes through the animation loop. Once the center of the rectangle is reached, i is reset to 0 and the animation loop begins again. The repaint method then displays the sequence of images. Finally, stop() suspends execution of the spinner animation thread when the user leaves the page.

12.3. Image manipulation • Creating Images

Java provides a set of classes and interfaces to help you create and modify image data. These classes and interfaces are grouped together in the java.awt.image package. Two of the most important interfaces defined in the java.awt.image package are: ImageProducer ImageConsumer Objects that implement the ImageProducer interface generate raw data for an image. And objects that implement the ImageConsumer interface can receive information
Page 350 Go To INDEX

about the development of the image. The creation of an image is the result of communication between ImageProducer and ImageConsumer objects. These are the public methods defined in the ImageProducer interface.

Communication between an image producer and an image consumer is initiated when the consumer object calls the ImageProducer's addConsumer method. The startProduction method directs the producer to begin computing pixel values. As image data is generated, the ImageProducer object passes information about the image to the image consumer. The ImageProducer does this by invoking methods defined in the ImageConsumer interface.

The constants defined in the ImageConsumer interface are passed as values to the setHints() and imageComplete() methods. More than one image consumer can be registered with an image producer at a given time. In cases like this, the image producer is expected to maintain a list of its consumers and report information to each one. When the image producer has delivered all the image pixels, it calls the ImageConsumer's imageComplete method.

Page 351

Go To INDEX

This notifies the image consumer that the task has been completed. It's important to realise that the component that displays the image isn't the image consumer. The AWT uses image consumers behind the scenes, in response to drawImage() requests. In fact, unless you want to do low-level manipulation of image data, you never need to use or implement an image consumer. As you know, images can be loaded from external files. But they can also be created from scratch and stored in memory. To create a new image, an application must first convert pixel values into an integer or byte array. Remember, image data is just a collection of different colored pixels. This array, or image data, is generated using some user defined method. In this example, the build method is used to load pixel data into an array.

Once an array of pixels has been formatted, you can construct an Image object from it. Image objects can be constructed from the entire array or from just a portion of it. As you know, the Image class is abstract, so you can't instantiate an Image object directly from it. Instead, your application uses the MemoryImageSource class to create a MemoryImageSource object. Like ImageProducer and ImageConsumer, this class is defined in the java.awt.image package. As you can see, various constructors can be used to create MemoryImageSource objects.

Page 352

Go To INDEX

MemoryImageSource constructors must be passed the specific following about the image, including: The size of the desired image The int or byte array An offset into the array The width of hypothetical imagerepresented by the int array They can also include information about the ColorModels and Hashtable properties used for an Image object. You can see that the instance methods defined in MemoryImageSource implement the standard ImageProducer interface. So a MemoryImageSource object can be considered an image producer. The MemoryImageSource constructor returns an ImageProducer object. This object acts as an input parameter for the createImage method.

This method is defined as part of the java.awt.component class. Using createImage, applications can take an ImageProducer object and create a new image from the pixel array defined with MemoryImageSource. And this results in a usable image.

Image filters

The java.awt package supports image manipulation by supplying image filters. An image filter is an object that sits between an ImageProducer and an ImageConsumer. Using image filters, your applications can modify image data before the consumer receives it. Any number of filters can be inserted between an image producer and an image consumer to produce a highly-filtered image. In this example, an unfiltered image is run through a red filter and then through a contrast-enhancing filter.

Page 353

Go To INDEX

You could use one filter to perform both functions, but it is good policy to create separate filter classes. This results in cleaner, reusable code. ImageFilter is the superclass of all image filters. Because objects derived from the ImageFilter class must intercept data intended for the ImageConsumer, they implement methods defined in the ImageConsumer interface. But filters also need to communicate with ImageProducers. To do this, they make use of a support class called FilteredImageSource. The FilteredImageSource class implements the ImageProducer interface.

So ImageProducer objects derived from this class can communicate with ImageConsumer objects. While FilteredImageSource objects can be viewed as image producers, their real function is to filter image data that has been produced by some other image producer. Each FilteredImageSource object communicates with a specific ImageProducer and a specific ImageFilter. Two important ImageFilter subclasses perform filtering: CropImageFilter RGBImageFilter CropImageFilter creates a new Image from a region of an existing Image. In this piece of code, you can see an image being downloaded and then cropped using CropImageFilter.

The RGBImageFilter class is an abstract class that enables you to create color filters. By subclassing RGBImageFilter, your application can modify the colors of the individual pixels making up an image. ImageFilter subclasses don't need to implement every ImageConsumer method. They only implement the methods that transmit data you

Page 354

Go To INDEX

want to change. The CropImageFilter class implements the following ImageConsumer methods: setDimensions() setProperties() setPixels() And the RGBImageFilter class implements the following methods: setColorModel setPixels Using an existing image filter is easy. First, your application needs to load an Image object to be filtered.

This is usually achieved with the getImage() method. Next, the program uses the getSource method to obtain a data source, or ImageProducer, for the Image object. An instance of ImageFilter can now be created and intialized. The next step is to create a FilteredImageSource object. This passes the constructor, the image source, and filter objects. Finally, the Component class's createImage method is used to create a new Image object. FilteredImageSource is the image producer of this new image. If you can't find an image filter that does exactly what you need, you can create your own. But all customized image filters must be subclasses of the ImageFilter class. Before creating your own ImageFilter, it is good policy to become thoroughly familiar with the ImageProducer and ImageConsumer interfaces.

Color models

The image you see on screen is a "rendered" image, or an arrangement of colors. But at its base level, an image is just a collection of binary numbers. Some sort of model is required to translate binary numbers into screen colors. In Java, this is the job of an abstract class called ColorModel. Methods defined in the ColorModel class can be used to translate an image's pixel values into its color components and transparencies.

Page 355

Go To INDEX

Let's examine how Java maps binary numbers to colors using ColorModel. In Java, individual pixels are represented as a composite of four color components: Alpha Red Green Blue By default, each color component occupies eight bits. So the color of each pixel is represented by a 32-bit value. When an array of integers is passed to a MemoryImageSource constructor, all the integers are expected to be in this format. A pixel's alpha value determines its opacity, or degree of transparency. In current Java implementations, alpha is either on (transparent) or off. Two subclasses are derived from the ColorModel class. The DirectColorModel subclass extracts the red, green, blue, and alpha values directly from the bits of a pixel. Using this class, you can specify how many bits of an integer represent each of the four color attributes. In Java, the default color model - called the default RGB color model - is derived from the DirectColorModel class. You can create an object that implements this default model by using the static ColorModel method getRGBDefault. The other ColorModel subclass is called IndexColorModel. The IndexColorModel class determines the color values for a pixel by using the pixel value as an index into color map arrays. This IndexColorModel is useful for images that: Use relatively few colors Represent digital information Applications are free to create their own color models. For example, in a customized color model, the red component might occupy 15 bits, as opposed to 8 bits in the default RGB color model.

The PixelGrabber class

A pixel grabber is used to extract a rectangular subset from a specified Image or ImageProducer object. This image subset is then converted into an array of integers. The

Page 356

Go To INDEX

PixelGrabber class is part of the java.awt.image package. As you can see, the PixelGrabber class implements an ImageConsumer interface.

This means it can be attached to any Image or ImageProducer object to retrieve a subset of the pixels in that image. Because PixelGrabber can be applied to Image and ImageProducer objects, it supports two constructors. One takes an Image object and the other takes an ImageProducer object. Let's look more closely at the PixelGrabber constructor that uses an image producer as an argument.

The ImageProducer constructor is generally more useful than the Image constructor for filtering situations. As you know, this constructor creates a PixelGrabber object that can grab a subset of an image. The pixels are captured from the image produced by the ImageProducer, ip. And the subset to be grabbed is defined by x, y, w, and h. Once grabbed, the pixels are stored in the array specified by []pix. The offset value marks the point in the array where the PixelGrabber should start putting pixel values. And the scansize parameter gives the width of the original image. Once the pixel grabber has been constructed, it is ready to grab pixels.

Page 357

Go To INDEX

In this code, pixel capture is initiated using the grabPixels method. This is a JDK 1.0 method but it can be used with JDK 1.1. The method getPixels() is new for JDK 1.1. When the grabPixels method returns, all the grabbed pixels are in an array. As you can see, this method is placed within an exception handler to catch any errors. But it's good practice, after using this method, to check for errors by calling the getStatus method.

12.4. Working with sound
Java applets can play audio clips, but this capability has not yet been fully developed. The ability to play multiple, digital waveform audio sounds is supported in the java.applet package. There is also provision for playing sound in Java applications. The easiest way of playing a sound clip is by using the play method in the Applet class. There are two constructors for play(), shown here.

The upper method plays the audio clip at the specified absolute URL. The lower method plays the clip given at the URL and a specifier that is relative to it. The url parameter supplies an absolute URL giving the base location of the audio clip, and the name parameter gives the location of the clip relative to the url argument. In both cases nothing happens if the audio clip cannot be found, so there is little or no control. The methods are limited to simple situations. To play an audio clip in an applet, you follow four stages:
Page 358 Go To INDEX

Create the AudioClip object Load an .au sound file into AudioClip Play the sounds once or loop continuously Stop the playback The basic code that implements the stages is shown here.

Both the AudioClip object and the getAudioClip method are part of the java.applet package. The getAudioClip method is used in the same manner as getImage() is used to get images. The getCodeBase method is used to get the URL specifying the directory on the web server with the audio file. The standard, and currently the only supported, format for audio data in Java is Sun's .au format. This format is suited to small audio files. The .au format has good compression, but is lacking in quality. The .au format utilizes an advanced storage technique that enables 14-bit digitized wave sounds to be stored in only 8 bits of data, with minimal loss. It is 8000Hz and monaural (one channel). It is generally used in Unix workstations, including Sun and NeXT machines. As only one format is currently supported, it is not necessary to specify this format to getAudioClip(). Other formats contained in the sun.audio package are expected to form the basis for more extensive audio support in future releases of Java. The Java Media Framework (JMF) application programming interface is designed to incorporate a diversity of media types into Java applications and applets. These formats include MPEG-1, MPEG-2, QuickTime, AVI, and MIDI, as well as the .au and .wav formats. The .wav audio format, developed by Microsoft and IBM, has both 8-bit and 16-bit forms and can be either monaural or stereo. The play method of getAudioClip() is used to start and play a sound file until the end of the file. The loop method is used to play an audio file until the end, and repeat this continuously. The stop method is employed to stop playing a sound file. Continuous sounds, for example background music, the hum of conversation, or water trickling are best represented using the loop method. Discrete sounds, such as a jingle or car horn are best implemented using play(). Control, using play() and stop(), is needed to stop a sound playing for a page other than the one being viewed, and to stop iconized programs playing sounds. Currently, there is no way to pause the sound file or to play part of the full clip. The limitations of sound in Java are likely to be short-lived as development is proceeding rapidly to remedy the shortcomings. A number of audio files can be played at the same time, making up a composite of sounds. This is, of course, hardware-dependent. While current sound cards normally support four channels, many still only support two. One of the applications for such computer-based, discrete sounds is in virtual reality (VR).
Page 359 Go To INDEX

Experimenters have found that small improvements in sound enhance the experience of immersion in three-dimensional VR, more so than small improvements in graphics quality. The MediaTracker class is designed for coordinating displays ("tracking") in various media - graphics, audio, and so on. For instance, it can be used with the getImage method to load and display slides. Although audio is not currently supported by MediaTracker, it is planned to extend tracking to audio files. Let's examine a complete Java applet that plays an audio clip.

The sample program begins by importing the Java packages. You use MouseListener so that the user can click the applet to replay the sound. The audio clip named audio is declared. The first method called is init(). The addMouseListener method tells the applet to listen for mouse events. Using the getAudioClip method, parameters are passed to reference the audio clip hello.au. The audio data will start to download when the play function is executed. When the applet stops, the audio clip is also stopped. The drawString method prints the line of text "Click here to replay sound file".

Page 360

Go To INDEX

When the user releases the mouse button, the applet stops the sound and plays it again. The methods at the end of the code are included because they are part of the MouseListener interface. Now, let's look at the use of audio in Java applications. Methods that are part of java.applet cannot be used in applications. Instead, you rely on the sun.audio package to provide audio in them. The features now described relate to the current JavaSoft JDK (up to version 1.1), but are not officially documented by Sun Microsystems. There is no guarantee that they will work in future releases. Importing the sun.audio package is achieved by the code shown. import sun.audio.* import java.io.* The displayed code opens an input stream, called istream, to the audio file.

To create an AudioStream object, named astream, from the input stream (istream) just opened, you use the code shown.

Playing and stopping the audio clip is achieved by means of the class member "player" from class AudioPlayer. This is now shown.

To play a stream continuously, use the ContinuousAudioDataStream class.

It is also possible to use a URL as the audio stream source. You define the URL and then replace the input stream and audio stream setup with the code shown.

Page 361

Go To INDEX

13 Java Tips and Techniques
13.1. Features of the Language • Language Issues
The Java language with its large libraries often provides you with different ways of doing the same thing. Although each way might have the same end result you need also to look at how the particular task is done. This is because performance is of critical importance in building applications and applets. The speed and efficiency of your application are often determined by the choice of implementation you make. For instance, the efficiency of string handling is a major concern for programmers. Java uses a different implementation of strings to C++. In C, operations on strings occur on character arrays. But in Java they occur through class methods. Java strings are constant, so their values cannot be changed after they are created. Instead you apply methods to the strings in order to create new String objects. Java can perform optimizing actions in your programs by sharing immutable String objects. So you should use a String object for strings that are not intended to be changed. The intern method can be used to facilitate the sharing of Strings. Suppose you create a String object x which contains "Hello". At another time you might create another String y which also contains "Hello". Calling the intern method in the declaration of y tells Java to share the String object so y now points to x.

You should take care when using String objects in Java. In many cases it is more useful and efficient to use StringBuffers. This is because StringBuffer objects essentially are modifiable strings. StringBuffers should be used when strings need to be modified. When you use the + operator to concatenate strings, the Java compiler uses the StringBuffer class to perform the operation. The StringBuffer class allows you to specify a particular size for a memory block. The capacity which you specify for the StringBuffer represents the number of characters the object is capable of storing. If the capacity of a StringBuffer is exceeded, the extra characters will be accommodated by expansion of the capacity. The internal buffer is automatically made larger if it overflows. This piece of code uses a String object called someString.

It allocates a new String each time the for loop iterates. This piece of code is, however, far more efficient. It appends new characters onto a string rather than repeatedly allocating a new string, which is a concatenation of the others. This could provide a critical
Page 362 Go To INDEX

increase in the performance of your application. The performance of Java programs is aided by one of Java's revolutionary features - its support for multithreading. Of course problems can still arise if there are many threads executing and many methods being called at the same time. For example, one thread might read a certain value into a field and another perform an operation on the contents of that field concurrently. The field may end up containing a value contrary to that intended. Java allows you to suspend the activity of one thread until another has finished what it is doing. The order in which the threads execute is determined by a priority rating. The synchronized keyword is used to indicate that a method cannot be entered until another call to it has finished. This is a security feature that allows you to control data that is not being modified correctly. However, if you place the synchronized keyword in front of every method you will severely affect the performance of your network, causing it to slow up. This is because you are effectively imposing single thread execution on your program. The overhead of a synchronized method can be about six times that of a standard method. All calls to a method could occur only providing previous calls to that method have finished. One way of enhancing the multithreaded capabilities of your program is to synchronize blocks of code rather than whole methods. Synchronizing blocks of code marks some sections off as being critical. These are the sections that are most likely to cause problems if executed concurrently. Any section which is not critical can be executed at any time. Java does not provide a mechanism for turning synchronization off. Many of the methods that are defined in JDK 1.1 are already synchronized. If you want to speed up the performance of your program, you could remove the synchronized statements. Sometimes the amount of code placed inside loops can be excessive. In this case, calls are made to length() and to getSize() with each iteration of the for loop.

A more efficient way would be to use local variables to store the result of a method call. The results of the calls made to length() and getSize() are copied into the local variables len and wdt respectively.

The loop expressions can now use these local variables. Note that you need call a method only once and copy its result into a local variable for further use. Repeated method calls are slower to carry out than using local variables, which store the result. You could also simplify your code by using references. Often programmers use the entire package.class.field naming style when they are accessing fields. If you are dealing with a primitive type, you could copy it to a local variable. If you are dealing with a reference field, you could use a local reference to the class. Suppose you were writing to the console frequently during the program's execution. Your code could contain numerous calls to the println method of the System.out class.

Page 363

Go To INDEX

Having to write this line of code over and over is time consuming. You can assign a PrintStream object to the output of System.out.

This lets you use a shorter piece of code to do fundamentally the same thing. One useful feature of C is the way you can define compile-time constants. To use a compiletime constant, you simply let a textual name represent some value. This makes your code easier to read and maintain. It is also faster at run time than using a normal variable. In Java you can do this by creating public static final variables in a Java interface. You use the import statement to make the interface visible.

The value stored in the age variable is concatenated to the string that is passed to System.out.println.

Java is of particular importance to Internet and networking technologies. The speed at which an application can carry out its tasks is of prime importance in this area. Delays in transmission make it a useful practice to have all input buffered. You do this by using an object derived from BufferedInputstream. When you use a BufferedInputstream a number of logical blocks of data are read from a file. They are read as one large physical input operation into a memory buffer. Blocks are taken from this buffer as they are needed by a program. If the buffer becomes empty, the next logical blocks are read in by the next physical input operation from the input device. So the number of actual physical input operations is small compared with the number of the program's read requests. Buffering inputs in your program will yield significant performance benefits. This is because standard input operations are particularly slow compared to typical processor speeds. When all input is buffered, repeated seeks through the data are in the buffer. This means that retransmission across the Internet is not required. This saves time while your program is operating. You can access an input stream as follows.

Memory leaks have affected programs designed using traditional programming languages. Many object-oriented languages require that you keep track of all the objects you create and that you destroy them when they are no longer needed. Java's memory management and garbage collection facility have cut down on the risk of memory leaks occurring. However, sometimes a "zombie" object can cause problems. An object that is still referenced cannot be returned to system resources. Such objects are known as "zombie" objects. An object is eligible for garbage collection when

Page 364

Go To INDEX

there are no more references to that object. References that are held in a variable are naturally dropped when the variable goes out of scope. You can also explicitly drop an object reference by setting the value of a variable whose data type is a reference type to null. Because Java has automatic garbage collection, you don't have to explicitly delete objects. Each class in Java can have a finalize method that can be used to tidy up a class before it is deleted. When a resource is no longer referenced, it is ready to be returned to the system. For instance, when the method 'setZombie' is called the string s is allocated.

The zombie string is then set to reference the string s before the method ends. When the setZombie method ends, the String s is marked for garbage collection. However, it will not be destroyed until the object itself (instance of ZombieExample) is destroyed because there is still a reference to it. Finalization is a disciplined way to begin to give resources that are no longer needed back to a system to avoid resource leaks.

Security Issues

Applets are the source of one of the major security concerns connected with Java. Browsers such as Netscape prevent Java applets from accessing files on the machine on which they are run. Users would be less likely to download an applet if there was a risk that it would tamper with their local files. You can view the HTML code source for the web page that the applet is embedded in. However, you cannot view the Java source code for the applet. The Java applet class file that is downloaded to your machine contains bytecodes and not source code. Therefore, you can not always be sure that there is no sinister function built in to the applet. Reverse engineering is a process where an application can be decompiled. In other words, the source code for the application is extracted and is capable of being viewed. This also means that an application can be modified in a way that the original developer never intended. Reverse engineering defeats the purpose of the principle of information hiding and can facilitate infringements of copyright. Decompilers read the bytecodes contained in one or more class files and convert them back to Java source code. Many of the available decompilers are written in Java. Often the code that is generated by a decompiler is not a perfect match for the original source code. However, they are usually close enough to enable you to understand how the implementations were made. The solution to the security risk presented by decompilation is to use an obfuscator. An obfuscator is a tool that obfuscates or confuses the contents of a class file so that decompiling them is made pointless. Not all obfuscators guarantee total protection from decompilation but they can make it exceedingly difficult to make sense of the decompiled bytecodes. Obfuscators work by scrambling symbolic names in your Java class files. So things such as class,

Page 365

Go To INDEX

interface, and method names are scrambled. The JVM needs this information in its correct form to create links from your classes to the relevant library packages. The code is all but useless if these links can't be created. When you run an obfuscator on your class files, another file is generated. It is this obfuscated class file that will be transmitted and not the original insecure version.

Designing programs for Java

When you are designing anything, the materials you will be working with are of the utmost importance. Software design is directly affected by the language you use to develop your application. By paying attention to design issues you can build better applications. In object-oriented programming, such as Java, the emphasis is on mapping real world objects into a software solution. Java provides an efficient way to design the interaction between problem domain objects and human interaction objects. This is the Observer/Observable object model. The Observer/Observable object model can be used to implement the Model/View/Controller architectures that are part of other languages such as SmallTalk. Java is flexible enough to allow you to go beyond the basic Model/View/Controller architectures. Both models represent a way of building problem-domain objects and reusing them again even though the human interface may be quite different each time. The idea that one object can notify other objects if a change in its state occurs is central to the Observer/Observable mechanism. It is useful in systems that make use of multiple, synchronized representations of the same data. Observer and Observable are particularly useful in the area of AWT GUI programming. Your application can have the capacity for different views and displays, which are updated in synch with one another. The java.util package provides you with the functionality to implement the Model/View paradigm. The relevant parts are the Observer interface and the Observable class. The Observer interface is declared like this. public interface observer A class that wants to be informed of changes in Observable objects implements this interface. The object that can change state should be derived from Observable. The Observable object maintains an editable list of the observers associated with it. The update method of the Observer interface is called to notify the Observers that the object being observed is changed. Each Observer has its update method called with two arguments - o, which is the observable object and arg, which is passed to the notifyObservers method. The notifyObservers method is then called and the Observer should then interrogate the Observable object to determine its new state. public void notifyObservers() When an Observer wishes to be notified about changes in the state of an Observable object, your program notifies the Observable object. It does this by calling the Observable object's addObserver method. public synchronized void addObserver(Observer o)

Page 366

Go To INDEX

This modifies the list of Observers by adding a new one represented by o. You can optimize the performance of the Observable by shielding it from the direct passing of information to the Observers. You can do this by placing a threaded FIFO queue between the Observer and the Observable. The threaded FIFO queue becomes the single Observer to the Observable, and the Observers in turn become Observers of the threaded FIFO queue.

Native methods and JNI

Sometimes there may be situations where you will want to integrate Java with code written in a different language. For instance, you may have a large amount of C code that has already been tested rigorously and debugged. Porting this code to Java may not be the best use of your available resources. It could take a large amount of time and debugging would again be necessary. Java has a facility called native methods which allows you to call precompiled code written in a different language. Native methods are libraries of code that are compiled natively. These libraries are dynamically called by the Java run-time interpreter for use by Java applications and applets. They are several times faster in execution time than compiled and interpreted Java bytecodes because they are compiled outside the Java environment. JDK 1.1 provides a native programming interface called the Java Native Interface (JNI) which allows you to use native methods. It allows Java code that runs inside a JVM to interoperate with applications and libraries written in other programming languages. These languages include C, C++, and assembly. You can write one version of a native application or library and expect it to work with all Java VMs that support the JNI on that platform. This is because JNI does not restrict the implementation of the underlying Java VM. So JVM vendors can add support for the JNI without affecting other parts of the VM. There are three primary reasons for you to choose to use native methods. The platform-dependent features needed by your application may not be supported by the standard Java class library. You might want to make a library written in another language accessible to Java code through the JNI. Finally, you might want to implement a small portion of time-critical code in assembly or some other lower-level language. Programming through the JNI allows you to use native methods to: Create objects Inspect objects Update objects These objects could be strings or even arrays. Using JNI you can also: Call Java methods Perform type checking at run time Throw and catch exceptions Load classes Obtain class information

Page 367

Go To INDEX

You must write a nativemethod declaration for each native method that you want to use. This declaration is similar to the declaration of a normal Java method interface but you must specify the native keyword. public native void copyLink(); The native keyword alerts the Java compiler that the definition for the method is external. Native methods can be static or non-static. For example, the basic steps for accessing native C code are: Writing and compiling the Java code for the program with the native method Generating the stubs and headers from the Java class file using the java utility Writing the native code for the required method Compiling the native code into a Dynamic Link Library (DLL) Making the library available to the Java run time Loading the library into the Java run-time interpreter

13.2. Advanced GUI Features • Lightweight Components

Currently, GUI components in Java are implemented as windows of the native operating system, and are called heavyweight components. Each instantiation of a GUI component gives rise to the creation of a full, native window. This one-to-one mapping of heavyweight components and native windows gives rise to some serious problems. These heavyweight components are not very efficient of resources, because each is implemented as a full native window. When you place a large number of these inefficient components into a container, it can severely impact on your application's performance. Native windows are opaque - meaning that they cannot be used to implement transparent regions on screen. And because extended components are implemented natively, it is hard to implement a common look and feel across different platforms. The JDK 1.1 includes the implementation of alternative, lightweight components that help eliminate these problems. Lightweight components do not have a one-to-one mapping to a native window. They are rendered entirely within Java itself. Transparent regions, for example, cannot be rendered in the component's paint() method in order for them to be transparent. Lightweight components are more efficient because they no longer need to have native data structures or peer classes. They are implemented entirely in Java, meaning that a common look and feel can be maintained across platforms. You can freely mix lightweight and heavyweight components in your applications. Each can be the child or parent of the other. Heavyweight components that overlap lightweight components will always appear on top, regardless of the z-order. Z-ordering is the order in which components are painted onto the screen. You now have greater control over the z-ordering of components, because you control fully the paint method for a component. Additionally, non-rectangular shapes for components are possible.
Page 368 Go To INDEX

Printing

The JDK 1.1 AWT has a set of APIs to enable printing. The AWT components are printed using the facilities of the native platform on which the Java program is running. Printing relies on the current AWT graphics model, and it is AWT components that are printed. To print, you first create an instance of PrintJob to contain all the necessary information for the print job. Then you create a print graphics object suitable for printing with. Thirdly, you must paint the component onto the print graphics object by invoking the component's print() method and passing the print graphics object as a parameter. You then flush the print graphics page to the printer. Finally, you end the print job, which initiates the actual printing. The PrintJob class contains all the print request information, such as user-defined printer settings and defaults. For example, you are responsible for pagination and may need to obtain the dimensions of the page. You get a PrintJob object through a call on the getPrintJob method of the java.awt.Toolkit class.

You pass three arguments, the first being a Frame dialog box object. You can design an arbitrarily complex frame dialog, depending on the application. Then you pass a job title string to identify the print job you have initiated. Lastly, you can pass a list of properties to the printer, which is an instance of the java.util.properties class. These properties are printer configuration parameters, like the printer's name or the page order. Let's look at the relevant methods of PrintJob that return printer properties. You can obtain the size of the print page, in pixels, using the getPageDimension method. You can then get the resolution of the page, in pixels per inch, from the getPageResolution method. Dividing the total number of pixels by the page resolution gives you the size, in inches, of the printing page. You can find out the order the pages are printed in by checking the lastPageFirst method. It returns true if the printing sequence starts with the last page. Printing the last page first is useful for printers where the pages are gathered front-side up. After setting up the print job, you must get a print graphics object using the getGraphics method.

The getGraphics method returns a Graphics object that has been prepared for printing. You then invoke the component's print method, passing the print graphics object. The print method, in turn, calls the component's paint method.

The printAll method of java.awt.Component allows you to print an entire containment hierarchy. You pass the Graphics object obtained from printJob.getGraphics(), and all components are printed in sequence.

Page 369

Go To INDEX

You then flush the print page to the printer using the dispose method on the print graphics object.

You then end the print job with the end method. printing.end(); There is also a finalize method, which overrides the finalize method of Object. It cleans up and ends the print job once a reference to it no longer exists. Printing is subject to the same security constraints as for normal applets. There is no restriction on applets creating print job objects or painting components into print graphics objects. It does mean, however, that untrusted applets are not allowed to initiate print jobs.

Java Foundation Classes (JFC)

Sun, IBM, and Netscape have combined resources to release a new set of GUI APIs. This entirely new GUI API is called the Java Foundation Classes (JFC), and will greatly enhance Java's usability. The JFC will combine the best of the current AWT with the best from Netscape's Internet Foundation Classes (IFC). The JFC will offer a set of comprehensive and state-of-the-art GUI tools for Java application development. Because it is based partly on the current AWT, programs developed now will be easily upgradeable to take advantage of the new JFC. The JFC will be included as part of future JDK releases, as standard. Let's look at the Internet Foundation Classes (IFC) from Netscape, to see what the new JFC will be likely to contain. The IFC is written entirely in Java and is delivered as a class library that can be easily integrated with the standard JDK. It replaces some of the AWT features and adds many new ones. The IFC contains a Drawing/Event framework with features like: Advanced clipping Flicker-free drawing Cursor management Superior handling of mouse events and tracking Transparent components The drag-and-drop paradigm is used extensively in the IFC. Dragging and dropping is a very intuitive element of modern GUIs. The IFC contains an entire framework to allow programmers to add drag-and-drop facilities to their applets and applications. You may often need to design programs that do several tasks at once, using Java's support for multithreading. Sometimes, writing threads for Java programs can add unnecessary complexity. The IFC offers objects called Timers that enable a form of concurrent programming that avoids the use of threads. A Timer sends messages to a target object at a particular, predefined rate. The Timer places TimedEvents into the main thread's event queue. This means that, using Timers, you can achieve simple concurrency without recourse to multithreading. The IFC provides a simple way for you to connect components together without knowing their exact types. Components implement the single, generic Target interface. The Target interface allows arbitrary objects to communicate with each other. One of the IFC's main strengths
Page 370 Go To INDEX

is the ability to customize components. You are able to create a strong, custom look and feel very easily with the IFC. You can do all this without having to extend the basic components offered in the AWT or IFC. The flexibility and range of windowing capabilities in the IFC is a huge improvement on the AWT. Internal windows work within the Java application and can be layered on top of one another, and become transparent. External windows are created by the underlying environment's windowing system, but are still controlled by the Java application. Other features of the IFC include: User interface controls, including a color picker and font chooser Animation Multi-font text support Object persistent system

Advanced AWT

You can provide mouseless operation for users with JDK 1.1. Expert users may prefer to use the keyboard, rather than the mouse, to issue commands and move between components on screen. You should consider offering mouseless support for expert users as it greatly enhances the usability of your GUI. For example, text fields into which the user is currently typing are said to have the keyboard focus. The user traverses, or moves, from field to field using the mouse or Tab key. They may just move the mouse pointer over a field, or they may have to click inside that field. Using the Tab key is an alternative traversal method. The components that can receive the focus are placed in a traversal order. Pressing Tab moves the focus forward to the component next in order, while Shift+Tab moves the focus back to the previous component. Standard components return a boolean true from the isFocusTraversable method if they can receive the focus, false otherwise. For your custom components, you override isFocusTraversable() and return true or false depending on whether you want the component to receive the focus or not. To grab the focus when an appropriate mouse event occurs, use requestFocus(). When a keyboard event results in the focus being gained, a FocusEvent event with the FOCUS_GAINED id is received. Losing the focus due to a keyboard event results in a FocusEvent event with a FOCUS_LOST id. You should then alert the user that the focus has been received, or lost, through some visual change to the component. Shortcut keys can be enabled to trigger menu commands under JDK 1.1.

You construct a MenuShortcut object, passing it a shortcut character and, optionally, a boolean value stating whether Shift is to be used or not. Then you use the
Page 371 Go To INDEX

MenuItem constructor with the new MenuShortCut object as an extra parameter. When the user presses the platform-dependent menu shortcut modifier key with the shortcut character, then the menu command is issued. Popup menus are a new feature of JDK 1.1. They are subclasses of the java.awt.Menu class. They differ from ordinary menus in that they are not related to a menu bar, and appear in the middle of the screen. You add the popup menu to the component parent as you would any other component.

The menu is displayed using the show() method, with the parent component as a parameter, and the x and y coordinate position relative to that component. You can use the mouse event's pointer coordinate position to allow the menu to be popped up where the pointer is. While Java is fully platform-independent, there may arise occasions when you will want to know more about the system on which your program is running. In java.lang.System there is a series of methods for returning local system properties. Properties include such things as the user's base directory, the host operating system, and the version of the Java VM being run. Here is a list of the standard properties, which are available on all systems.

These include information on: The Java version and Java vendor The Java home directory and class path The user's name, home directory, and current working directory The host IO system separators The host operating system

Page 372

Go To INDEX

All property keys and property values are defined initially as String objects. You can retrieve the relevant values of different types, such as boolean, by using the wrapper classes for primitive types defined in java.lang. However, applets are restricted from reading properties that may lead to security breaches, such as the user's home directory. In addition to the standard properties, you can access system-dependent properties. And you can set system property values too, using the System.setProperty() method. Let's look at how to get Color and Font values from system properties. There is a static method called getColor() in the Color class that loads a property from the system properties table matching a string value passed to it. It interprets the property's value as a 24-bit integral RGB color. Here is an example of a call to getColor(), looking for a property called "Colors.backColor". Let's see how you might set that property value. Here, you first create an instance of the Properties class from a call to the static getProperties method of System.

Then you set the property value to the RGB equivalent of the octal value 0x00ff00 (green). Finally, you reset the system properties to reflect the new value of Colors.backColor. Font values are specified using a string of the form "<FontName><FontStyle>-<PointSize>". The styles are plain, italic, bold, and bolditalic. These style strings are all lowercase, unlike Java variable names, so bolditalic is not to be spelt boldItalic. Both the style and size are optional. A Times Roman, bold, 16-point font is specified as "TimesRoman-bold-16", for example. Reading and setting Font property values is the same as for reading and setting Color values.

13.3. Using the Java packages • The Java Archive tool (jar)

JAR - or Java Archive - is a file format that is used to aggregate multiple files into one. JAR is based on the popular ZIP format and can be used for general archiving purposes. ZIP files are generated by the PKZIP program or one of its variants. This form of data compression is widely used on a number of computer platforms. Java uses it particularly for the purpose of combining all of an applet's required files into a single file. This considerably reduces the time required to download the applet. An applet often consists of a number of files: One or more .class files Image files such as .jpg or .PNG Audio files such as .au Each time a browser downloads one of the applet's component files, an HTTP transaction occurs. Each transaction takes a measurable amount of time. In addition, the server might be busy when subsequent downloads are attempted. This could cause the
Page 373 Go To INDEX

applet to fail. JAR addresses this problem by combining all of an applet's files into a single JAR file. This results in the applet being downloaded in a single HTTP transaction. This greatly improves the applet's execution speed. Like the ZIP file from which it was developed, the JAR format also supports compression. This further cuts the download time by reducing the file size. When you create a JAR file for an applet you have to modify the <APPLET> tag in the relevant HTML page. The JAR file on the server is identified by the ARCHIVE parameter. The name of the class file that starts execution of the applet still has to be specified by the CODE parameter. However this class file is not stored separately - it is extracted from the JAR file.

It is also possible to specify multiple JAR files with the ARCHIVE parameter. For example, you might want to store all your class files in one archive and your multimedia files in another. In this case you specify the JAR files in a comma-separated list.

After the JAR file is downloaded it is separated into its constituent files. As the applet executes it searches the downloaded archive for required files. If it can't find a file it then searches the server from which it was downloaded. This search can be directed by the optional CODEBASE parameter. The Java archive tool jar is used to combine multiple files into a single archive file. The syntax for the jar tool is very similar to that of the UNIX tar command. It is launched from the command line and always takes the following arguments: The name of the destination JAR file The names of one or more files to be archived The advantage of using jar on a single file is the compression achieved - a small file downloads more quickly. The jar command can take several options. The c option creates a new or empty archive on the standard output. The t option lists the table of contents from the standard output. The x file option extracts only the specified file, rather than all the files. And the v option generates verbose output, which gives more information on the files, such as their size and last modified date. The jar tool generates a manifest file, which contains a list of all files present in the archive. The manifest file is named META-INF/MANIFEST.INF, and it is always the first entry in the JAR file. If you have a pre-existing manifest file that you want the jar tool to use, you can specify it with the m option.

Iner-applet communication

The greater the functionality built into an applet the larger it is likely to be. But large applets take longer to download, and this can discourage their use. One solution to this

Page 374

Go To INDEX

problem is to split the functionality of the overall program between several smaller applets. Java provides the following ways for applets to communicate with each other: They can communicate with other applets running in the same Web page They can interact with applets running in different Web pages in the same browser They can also communicate across a network with a server-side application To find other applets Java provides two methods from the AppletContext class. The getApplet method looks up another applet by name and returns the actual applet instance. If the named applet does not exist the method returns a null value. The getApplets method returns an Enumeration that lists all the accessible applets. The Enumeration interface is part of the java.util package. Different browsers implement security policies in different ways, and this may affect the way getApplets works. Usually getApplets returns a list of all the applets that are running in a particular page and that originated on the same server. An applet's name is specified in the HTML code of the page that calls it, not in the Java code itself. You give an applet a name by specifying a NAME attribute within its <APPLET> tag. Alternatively you can specify the name with a <PARAM> tag. Let's say we have two applets on a page - a Transmitter and a Radio. The Transmitter applet has to find Radio before it can transfer information to it. The name of the Radio applet is passed to the getApplet method, and the return value is assigned to the radio variable. This variable is then checked to make sure it is a valid instance of the Radio class. If it is, a method of the Radio object is called to acknowledge the Transmitter.

It is possible for applets to communicate by using static variables and methods within a common class. This is because the information in a static variable is the same in all instances of the class. And static methods can not be overridden by any subclasses. Using static methods and variables you can create a class that coordinates the activities of several applets. For example, this class can maintain a list of all running applets. And it can use a thread to synchronize activity among the applets.

Page 375

Go To INDEX

When using static variables for inter-applet communication the applets do not have to be on the same Web page. The getApplet and getApplets methods require the applets to be on the same page. This can be a great advantage in certain circumstances. For example, using static variables, you could build a navigation system for a Web site. You could then have a navigation applet on each Web page, and a central applet coordinating all activity. Another way for applets to communicate with each other is through a network connection. Like any Java program, an applet can use the features of the java.net package to communicate with a host computer. However the security restrictions imposed by Java limit the usefulness of this approach. An applet can only communicate with the server that it was downloaded from. It can't communicate directly with applets on other computers, even if they were downloaded from the same server.

Using URLs to access files

Using a browser to read Web pages can be interesting and fun. However while you are connected to the Internet you are tying up bandwidth and possibly incurring connection charges. One solution to this problem is to download Web files and view them offline. Let's write a simple Java application that will download a file from the Internet and write it to the local disk. This program will take two input parameters: The URL of the file to be copied The name the file will have on the local disk You will need methods and classes from Java's network and I/O packages, so you first import these packages. The program copies files from the Internet to disk, so you give it the class name WebtoDisk. Then you write the main method, which accepts command line arguments. The user is told what parameters the application expects. If the user does not enter two parameters an error message is printed and the program terminates. The main section of code is put in a try block to handle exceptions. A URL object is now created from the absolute URL entered on the command line. Then a URLConnection object is created with the URL object's openConnection method.

Page 376

Go To INDEX

Now that a connection has been made you will need an input stream to handle the data. This is created with the URL object's openStream method. You will also need to write the data to a file on the local disk. So you create a FileOutputStream object from the second input parameter. Now that the data streams have been set up, you can use them to transfer the data. The input stream's read method reads the data one byte at a time. This method also controls the while loop - when the end of the stream is reached a -1 is returned and the loop terminates. The file output stream's write method writes the data to the disk, one byte at a time. You then close the data streams and print a message to the standard output. You then finish the code by handling the exceptions that are most likely to occur. The MalformedURLException can be thrown by the URL object's constructor. And the IOException can be thrown by the read, write, openStream, and openConnection methods.

This application could have been written so as to use the URL to create the local file name. It's possible to write a program that runs as both an applet and as an application. Such a program can use URLs to locate files on both the Internet and on the local disk.

Converting applets into applications

There are several reasons why you might want to convert an applet you have developed into an application. For example, it is easier to debug a standalone application than an equivalent applet. Or you may wish to distribute it to users who do not use a Web
Page 377 Go To INDEX

browser. There are two basic steps that must be taken to convert an applet into an application. First, you must provide it with a main method, as the Java interpreter requires one. Second, you must provide a framework in which to display the applet. This is usually done by extending the Frame object. In most cases you will have to do more than just provide a main method and a framework. This is because applets usually rely on features that the browser provides. So you will in effect need to simulate a browser for any other than the simplest applets. The Java API provides two interfaces that can be used to simulate a browser - AppletContext and AppletStub. Both of these interfaces are in the java.applet package. The AppletContext interface corresponds to the applet's environment. This consists of the browser - or applet viewer program - and the HTML page that calls the applet. The AppletStub interface is used to simulate the browser. It provides methods, which an applet might call, such as getParameter and getAppletContext. Let's create a class called BrowserSim that will simulate the browser environment. BrowserSim is derived from Frame, which provides many useful window features. It implements the AppletContext and AppletStub interfaces, which are necessary to simulate the browser environment. It also implements the WindowListener interface, which is required to handle window events. The BrowserSim class needs a main method to work as an application. This method calls the BrowserSim constructor. This constructor takes an argument list made up of the values entered on the command line. The BrowserSim constructor can take a number of forms. If the applet is actually contained within the BrowserSim class you can use the “this” keyword and set the size explicitly. Alternatively you can write it so that you can specify the starting width and height of the application. After instantiating the BrowserSim object the constructor calls Frame's addWindowListener method. This method adds the specified window listener to receive window events from this frame.

At the end of the BrowserSim class you handle the window events that could occur.

In this example it's only necessary to handle the windowClosed event, which happens when the "close" icon is clicked. However, you still must write dummy handlers for the other window events. The AppletStub interface defines a number of methods that have to be implemented. The exact implementation of these methods depends on the requirements of the application.
Page 378 Go To INDEX

The appletResize method is called because it is necessary to resize the Frame when the applet is resized. It uses Frame's insets method to return values for border and title bar thickness. The getAppletContext method gets a handler to the applet's context.

The getDocumentBase and getCodeBase methods both return URLs. There are some platform-dependent issues to consider when writing the code for these methods. On Windows computers the path to a file will contain backslashes. To convert to a URL these slashes must be replaced with forward slashes. Another point to watch out for is that the colon in a Windows filename should be replaced with a vertical bar. The vertical bar is also known as the pipe symbol. The getParameter method returns one of the applet's parameters. How you implement this method depends on what you have done with the parameters. For example, if you had created a hashtable called paramset you would use the get method. The final AppletStub method is isActive. You can set this to always return true as the applet is running when the application is running.

The AppletContext methods also have to be implemented. These methods are used by an applet to obtain information from the applet's environment. The getApplet method simply returns null as in this case the applet is not named. As you have seen already, an applet's name is specified in the HTML code of the Web page that calls it, not in the Java code itself. The getApplets method returns an Enumeration listing the accessible applets. In this case the method returns an Enumeration of one element - the applet itself. The getAudioClip method returns an audio clip. As this applet does not use sound, the method simply returns a null value.

The getImage method takes a URL as a parameter and returns an image. This involves downloading the image from the Internet - this would be the normal behaviour of an applet. In the context of an application it is usually the case that images are stored locally. So how you implement this method depends on whether or not you will be converting URLs to filenames. In this case you use a method called localFileName to convert the URL into a filename appropriate for the operating system. You then use two methods from java.awt.Toolkit. This class is used to bind the abstract AWT classes to the
Page 379 Go To INDEX

toolkit implementation of the operating system. The getDefaultToolkit method returns the default toolkit. And the getImage method returns the pixel data from the specified file.

There are two versions of the showDocument method. When it takes only a URL as a parameter, it loads the document from that address into the browser. public abstract void showDocument(URL url) When showDocument takes a String as well as the URL, it loads the document in a separate window specified by the string. public abstract void showDocument(URL url, String target) This method is not very likely to be used in an application, so it's not implemented here. The showStatus method is used to display a message in the browser's status bar. You can use a non-editable text area to display such messages.

Page 380

Go To INDEX

Sign up to vote on this title
UsefulNot useful