Previous Section
 < Day Day Up > 
Next Section


Java-based Cross-Platform Development

The current software industry has available myriad combinations of operating systems and development platforms, which confuses many developers who are trying to settle on a platform for their career. Apart from the mere availability of these platforms, the developers are mostly guided by the Enterprise requirements, as that is what is going to drive their needs. In addition, the developers also need to get up to speed in the most recent technologies (which is a variable factor these days) in order to survive in the fast-growing technology market. At the same time, many Enterprises are facing a similar state of confusion, because they must try to protect their IT investments over a long period of time. This poses a big challenge, given the current speed of technological developments. The third entity in this scenario is the technology innovators and vendors, who are also facing challenges to produce technology that will last a long time (at least considerably longer than many other competing technologies).

So, the ideal situation is to identify a technology that is going to support every possible need of the industry and that is going to be long-lasting without sacrificing performance, security, ease of use, and other factors. However, it is almost impossible to identify such a technology that would satisfy every individual’s needs. One specific technology that has emerged over the past few years is the Java language and its associated components from Sun Microsystems, Inc. Although it cannot satisfy every programming need of every individual, it is rapidly emerging in the industry in many dimensions of the programming world. In this section, we are going to discuss the features of this language and how it differs from C++ and Delphi. The discussion in this section attempts to provide a ready reference of several features and some of the most commonly used classes, in addition to some specific programming scenarios.

Features of Java Language

Java was designed to support specific programming features that made it distinct from other languages that existed at the time of its inception. It was also meant to support many of the proven object-oriented programming principles and implement several design patterns as part of the language components. Although many new features, such as modern design patterns, can still be implemented in languages such as Delphi and C++, due to their object-oriented nature, they are pre-built in Java language. These features are outlined here to provide a ready reference of the language for new programmers.

One of the problems faced by many of the developers using C++ language is the improper management of memory pointers that could lead to either access violations or memory leaks. Although this is most likely not a problem for most veteran C++ developers or for those programmers who love power programming in C++, many non-C++ programmers and newcomers complain about this aspect because—in the current development scenario—most people desire to learn programming languages on the fly and become proficient without struggling with complex situations such as dynamic memory management. They want everything to be handled by the programming language itself. In simple terms, they expect programming languages to behave more intelligently—like operating systems. Although this is not practical, it is the current trend. Java language has emerged with the objective of helping programmers in this regard. Although C++ language is a perfect language for people with mathematics and computer science backgrounds (and aptitude), Java makes a good fit for people who do not have a background in these technologies. Applications written in Java do not require the programmers to handle memory pointers, as they run in a Java Virtual Machine (JVM) environment that automatically performs the garbage collection needed to release resources when they are no longer necessary. This means that programmers only create objects, they do not (have to) release them.

The designers of Java language components provide a library of routines and tools—collectively called the Software Developer Kit (SDK) or Java Development Kit (JDK)—on a number of operating system and hardware platform combinations and ensure portability of the applications across these platforms. Although the programmers are relieved of the burden of dynamic memory management, a major task involved in Java programming is to become familiar with the library of routines shipped with the platform. It is not mandatory for a Java programmer to master these libraries, but typical employers expect Java programmers to know the library thoroughly. In fact, a Java programmer who is familiar with the maximum number of packages in JDK library is considered more experienced than one who knows fewer of these packages but is more knowledgeable in the principles and language features. Although this may not be an appropriate way to measure an individual’s capabilities, nonetheless it is the way the industry benchmarks between professionals. If a typical C++ programmer takes about two to three years to become proficient in aspects such as dynamic memory management, a typical Java programmer takes a similar amount of time to become proficient in using the Java library of routines. Although Java programmers have to use the Java 2 SDK library, the C++-based application designers have the freedom of designing their own libraries—as a typical C++ implementation provides only the basic compiler and the associated standard template library—or they can use third-party libraries. Further, although Java is capable of interfacing with applications developed in other environments, there are limitations in implementing these interfaces, and Java designers and pro-Java professionals recommend using Java language SDK for all the components of the application.

Because Java programs are executed within the protected JVM environment and the JVM on different platforms inherently takes care of handling platform-specific calls, Java programs are portable across multiple platforms. This is really a very big advantage to Enterprises, enabling them to create platform-independent applications. For example, creating GUI applications on the Microsoft Windows platform is based on the Win32 library, while it is based on the X-Windows library on a typical UNIX platform. Because the Win32 and X-Windows libraries are entirely different, developing portable applications across these platforms was quite a challenge. Prior to the invention of Java technology, portable GUI application development was an almost impossible task. Now that Java has pierced deeply into the technology market, many vendors are creating portable GUI applications using Java GUI SDK (also known as the Abstract Windowing Toolkit and the Swing component set). For example, the Oracle Universal Installer™, Oracle Enterprise Manager™, Oracle Jdeveloper9™, and other Oracle client tools are all converted (from Win32) to Java technology, so that they run on Windows, UNIX, Linux, and other Java-supported platforms. Similarly Borland JBuilder, Borland JDataStore™, Borland Enterprise Manager™, and so on were all developed using the Java technology, so all these applications run on a host of multiple platforms. Other vendors, such as IBM and (naturally) Sun Microsystems, also built many of their applications in Java technology.

Although some object-oriented languages such as C++ support inheriting a derived class from multiple-base classes (also known as multiple inheritance), Java provides indirect support of this feature through the concept of interfaces similar to Delphi. Even though a Java-derived class can inherit from only one base class, it can implement any number of interfaces.

Due to the fact that Java programs run within the protected JVM environment, it appears that there is some performance hit on some time-critical and mission-critical applications compared to C++. This is very noticeable in older systems running on slow processors with low memory. Also, Java Swing-based applications consume more resources than backend applications. If you are using one of the latest and greatest computers with abundant resources, these problems may be ignored. However, on the same hardware, undoubtedly C++ programs run faster and more efficiently than Java programs. Particularly when resources are very critical on the hardware, efficient C++ programmers can easily build high-performance applications in C++ compared to Java. This is probably the main downside of this language. In simple terms, while C++ is for the power programmers who develop operating systems and its routines, compilers, virtual machines, device drivers, and every low-level application that directly talks to the hardware, Java is for the more generic developer community because C++ language gives complete power (like C language) and provides it in perfect object-oriented design (the main feature of C++), and Java hides these details from the programmers and provides a very sophisticated approach. In addition to low-level programming, C++ can be used to build any type of application from ground up. One knows what happens if a powerful tool is given to someone who does not know how to operate it; he can harm himself or harm others. Such is the case with C++.

Java directly supports CORBA (Common Object Request Broker Architecture) technology through the IIOP (Internet Inter-ORB Protocol) and messaging services through JMS (Java Messaging Service). Therefore, Java programs can participate in distributed applications written in other technologies. These technologies are discussed in detail in Chapter 7, Bulding Distributed Java Applications.

The Programming Model

As mentioned earlier, the Java programming model is different from other traditional object-oriented languages such as C++ or Delphi and so on. How is it different? Normally, when a program is written in a traditional language such as C++, the compiler creates an intermediate object code, and the linker creates the final machine code that the computer can understand and execute. This executable machine code is different for each platform, so programs built to run on one platform (such as Linux) cannot be run on a different platform (such as Windows). When a Java program (with .java file extension) is compiled, the Java compiler (which is the javac program) creates an intermediate class file that contains byte codes (and not the object code or machine code produced by traditional compiler and linker components, respectively). The byte codes are written in a particular format that another program in the Java technology (which is the Java program also known as the Java Virtual Machine or JVM) can interpret and convert to native machine code and execute within its own (or JVM) environment. The class file that contains the byte codes has the .class extension and is portable across platforms. This means that a .class file built in Linux can be ported and executed on the Windows platform. However, the JVM is platform-dependent, as it has to interact with the underlying processor and execute the byte codes. Even the Java compiler is also platform dependent as it has to run on the specific platform. Typically, the Java compiler and the JVM are written in a power language (such as C or C++). Every Java class file is executed through the JVM. Sample command lines are given here to compile and execute Java programs.

$ javac firstsample.java
$ java firstsample

The first line compiles the firstsample.java program, and the second line executes it. Notice that the .class extension is not specified in the second line while running the program.

Java programs can be developed using the Java 2 SDK and SDKs of other Java technologies (from Sun Microsystems) and simple text editors such as vi and Emacs, and therefore do not need any development tool. However, vendor IDEs such as Borland JBuilder, Oracle JDeveloper, and IBM WebSphere Application Developer enhance the developer productivity to a great extent by offering a very comprehensive development environment that does a lot of mundane work in the background. In this chapter we will demonstrate the Java development using Borland JBuilder8 and Oracle JDeveloper9i IDEs, in addition to the discussion on using command-line Java 2 SDK tools. Most of the applications demonstrated in this chapter can be compiled and tested using the Java 2 SDK downloaded from the java.sun.com Web site free of charge. As of writing this book, the most current edition is Java 2 SDK 1.4.1, which is very comprehensive and stable compared with all the previous releases. The Java 2 SDK is also known as J2SE (Java 2 Standard Edition). Most Linux installations might come with an older edition of Java 2 SDK. However, it is always best to install a newer edition. You may install the rpm edition using the Red Hat Package Manager™, or you may also install the tarred and gzipped edition (identified by the .tgz file extension). Multiple versions of Java SDKs may coexist on the same computer, so you may install the newer SDK in a different directory. To make the newly installed Java2 SDK (or any package) recognized by the system, create a separate shell script with .sh extension and place it in the /etc/profile.d directory. In Chapter 4, we learned that every time a user logs into the system, the shell scripts placed in this directory are executed. Therefore, scripts placed in this directory usually contain commonly used environment variables. In the newly created script, write lines similar to the following.

export JAVA_HOME=/opt/java/j2sdk1.4.1
export JRE_HOME=$JAVA_HOME/jre
export PATH=$PATH:$JAVA_HOME/bin:
export CLASSPATH=$JAVA_HOME/lib:.:$JRE_HOME/lib

When you next log in to a new shell window, these settings become effective. Note that the new environment variable CLASSPATH is created. This variable contains all the directories where the JAR (Java archive) files containing the Java routines shipped with the SDK are located. A JAR file is simply the Java version of an archive file (similar to the tar files, zipped files, and so on). JAR files may contain any kind of files related to Java applications. However, the CLASSPATH environment variable points to the locations where the JAR files contain compiled Java classes that are part of the SDK. The CLASSPATH variable may also contain specific JAR files with fully qualified path name. When the JVM executes a Java application, it needs to know where the Java runtime environment and any other third party libraries are located. We may specify this information through the –classpath command-line option for the java command or through the CLASSPATH environment variable, or both.

Java applications are typically interactions between different Java objects within the same JVM or across different JVMs. If more than one JVM (running different Java applications) is involved in the application, then it is considered a distributed application, as each JVM runs in its own address space. Although this chapter will focus on applications running in a single JVM, Chapters 7 and 8 provide a detailed discussion on distributed and Web applications. We have noted earlier that the JVM takes responsibility for running the application within a protected environment. While doing so, there are a number of tasks that it performs without the knowledge of the end user, such as running the class loader to load the class file containing the byte codes into the memory, running the verifier that verifies the validity of the byte codes and ensures security is not violated by the program, and finally running the interpreter that interprets and executes the byte codes. Thus, the JVM environment comprises all these three components necessary to successfully run byte codes from the class files.

A class constitutes a special programming construct that can be instantiated to an object. Conceptually, it is similar to a class in the C++ and Delphi languages discussed earlier, as all the object-oriented languages are developed on the same basic principles. Each Java program can contain only one public class definition, and the name of the public class should match the source file name. The source file may contain additional private classes. However, private classes in a Java program are only accessible to the public class in the same source file. In the previous example, the firstsample.java file should contain a public class named as firstsample. It should be noted that the names are case sensitive. A simple example program is provided here. You may create this program using the vi editor and save it with the file name HelloWorld.java.

public class HelloWorld {
        public static void main(String args[])
        {
            System.out.println("Hello, world!");
        }
    }

Compile this program with the following command line.

$ javac HelloWorld.java

The program should be compiled without any problem. Now the program may be executed with the following command line.

$ java HelloWorld

Typically, related Java programs are grouped together in a package. A package consists of a hierarchy of directory tree structure, and all the programs within a package should be located in the last subdirectory of the package name, and each Java program belonging to a package should specify the package name with the package statement as the first statement in the program. It has been a traditional practice to start the package name with the reverse Web site address of the organization excluding the www string, even though there is absolutely no relation to the Web site address and the package we are building. For example, if the Web site address of a company is www.mycompany.com, names of the Java packages built within that organization could begin like com.mycompany and may continue to have further names in the hierarchy that may represent the application-specific details. Following are some examples of typical package names.

    com.mycompany.insurance.brokerage
    com.mycompany.employee.benefits
    com.mycompany.admin.networking

Taking the first example, all the program files belonging to the com.mycompany.insurance.brokerage package should be placed in the last directory of the com®mycompany®insurance®brokerage directory tree, which is the brokerage subdirectory in this case. Also, each of the programs should have the following statement in the beginning, before the class declaration.

    package com.mycompany.insurance.brokerage;

If we place our HelloWorld.java application in this package, then the program looks similar to the following.

    package com.mycompany.insurance.brokerage;

    public class HelloWorld {
        public static void main(String args[])
        {
            System.out.println("Hello, world!");
        }
    }

The program is compiled from the parent directory of the starting point of the package directory hierarchy (which is the parent directory of the com subdirectory) with a command similar to the following.

    javac com/mycompany/insurance/brokerage/HelloWorld.java;

The program is executed from the parent directory of the starting point of the package directory hierarchy (which is again the parent directory of the com subdirectory), with a command similar to the following.

    java com.mycompany.insurance.brokerage.HelloWorld;

Notice the difference in the notation used while compiling and while executing. The compiler sees the individual parts of the package name as individual directory/subdirectory names, whereas the JVM considers the package in its entirety. The program file extension .java is specified in the compile command line, whereas the byte code file extension .class is omitted in the JVM command line.

When the Java programs are archived into a JAR file, then the archive file maintains the directory tree hierarchy that is not visible to the outside world. Therefore, JAR files can be placed in any directory we desire; specify that directory name (or JAR file name) in the CLASSPATH environment variable or in the –classpath command-line option to the JVM, as mentioned earlier.

Every Java program references one ore more other Java classes, which might belong to the Java 2 SDK or might have been developed by us or by some other group or company. Therefore, we may be using compiled Java classes from several packages. Within a program, a class from a package is referenced with a fully qualified package name, as shown in the following statement.

    java.util.Vector names = new java.util.Vector();

In this statement, we are creating an object of the java.util.Vector class, and the object is named names.

Using the fully qualified class name every time would make life very difficult for programmers. Therefore, the Java technology has provided an alternate way to achieve the same result. We can import a class or a package into our program using the import statement at the beginning of the program (after the package statement) with the specific package or class name. If we import a single class into the program, we can simply refer that class with the class name and without the need of qualifying it with the package name, as shown in the following program segment.

    package com.mycompany.insurance.brokerage;
    import java.util.Vector;

    public class ImportClass {
        public static void main(String args[])
        {
            Vector names = new Vector();
            System.out.println("I imported the Vector class ");
            System.out.println("and created an object ");
        }
    }

If we import the complete package (with the * notation), all the classes, interfaces, and exceptions defined in that package can be used without qualifying them with the package name, as shown in the following code segment.

    package com.mycompany.insurance.brokerage;
    import java.util.*;

    public class ImportPackage {
        public static void main(String args[])
        {
            Vector names = new Vector();
            LinkedList list = new LinkedList();
            System.out.println("I imported the java.util class ");
            System.out.println("and created two different objects ");
        }
    }

There are different types of Java programs to serve different types of needs. Applets are Java programs that are typically embedded in Web applications and are loaded by a Web browser. Although applets provide a great way to embed external objects in Web applications (very similar to the ActiveX objects loaded by Web applications developed by Microsoft Active Server Pages™ technology), there are limitations imposed by different Web browsers, and many organizations are now considering discontinuing the use of applets in their Web applications. The second type of Java applications are the standalone applications, such as desktop applications or custom-built standalone servers. These are the type of applications that are in maximum use and development in the industry currently, apart from the next type, which are the servlets. While applets are designed to be client-side objects (executed on the client-side), the servlets are designed to be the server-side equivalent of the applets. However, servlets are more powerful than applets; a servlet is capable of running (and managing) an entire Web application. Although this chapter focuses mainly on plain Java applications, chapters 7 and 8 are completely devoted to server-side Java development.

Even though every Java program will inevitably have a class definition, the main() function only exists in those class files which are standalone executables. Note that standalone execution does not mean that it can run without a JVM. All Java programs execute within the context of a JVM. However, only those class files that contain the main() function are capable of being launched (by JVM) as an individual application, and every other class file should be loaded by another application that runs within a JVM. We have seen the signature of the main() function in the previous example. It is provided here for ready reference.

    public static void main(String args[])

The public specifier indicates that the function is accessible by external applications such as the JVM in this case. The static specifier indicates that it is a class function and is executable before the class is instantiated to an object. The input argument to this function is an array of strings known as args. These are the arguments passed to the command-line execution of the program. The void return type indicates that the function return void. If this function returns void, how does it return an exit code to the caller? We will discuss this in more detail in the following sections of the chapter.

Language Fundamentals

Several syntactical features of the language are modeled after C++; therefore C++ programmers find it really easy to learn (or migrate to) Java. However, the fact that they are not going to work with memory pointers in Java might seem a bit strange to them. Although a single C++ program can contain more than one public class, Java does not permit this. In addition, a typical C++ program traditionally (although this is not mandatory) contains two files, one header file (with .h or .hpp extension) containing the class definition, and one implementation file (with .cpp extension) where the class’s methods are implemented. Java programs do not have header files, and therefore no include directives. Also, a Java class does not separate definition and implementation as two entities. The methods are implemented as they are defined.

In addition, a C++ program’s main function resides outside of a class’s definition, while it becomes part of a Java class implementation through the static keyword. For this reason, some Java professionals are of the opinion that Java is a true object-oriented language while C++ is not. However, this is not true. C++ is a perfect object-oriented language (through which Java has emerged), and it does not matter where the main function resides; after all, it is there and is unavoidable. In fact, many professionals are of the opposite opinion; they believe that keeping the main function outside the class’s implementation makes the class really object-oriented, as it does not differentiate between an executable class and a non-executable class and also is a very elegant design. It should be understood that such matters are implementation-specific and depend on the preferences of the designers of the language.

The data elements in Java are primarily of two types: primitive data types and objects. The primitive data types are useful for storing the basic data types supported by the language, such as short, int, long, char, byte, float, double, and boolean data types, and the objects are instances of classes that support complex data structures and their access mechanisms. Objects can hold other objects or intrinsic data types. For convenience, the intrinsic data types are wrapped into classes of their own in the java.lang package, such as the char data type is wrapped within the Character class, int data type is wrapped within the Integer class, and so on. Java provides the String class, which is an implementation of character strings, unlike C++ where character strings are represented by either fixed-size character arrays or variable-size character pointer-based objects. The Java String class can grow dynamically as required by the size of the stored string.

Although variables of the primitive data types are created on the stack and are accessed directly, the objects are created on the heap and are accessed through their references. As discussed in an earlier chapter on C++ and Delphi programming, a class is a blueprint (or design) of how an object should look and behave, while an object is the concrete representation of an instance of the class. Throughout this book, a class instance and an object are interchangeably used, as they mean the same thing. An object can have any number of references, and an object reference points to the object. Object references should not be mistakenly considered as similar to C++ pointers. The C++ pointers support pointer-arithmetic, Java references do not. In fact, Java object references are similar to C++ object references. Because we do not directly interact with the object’s memory, we do not use the operators—such as * and &—that are typically found in a C++ program. Java object references are straightforward declarations. For example, a reference to an object of the MyClass class is declared with the following syntax.

    MyClass myClassObject;

As seen in this example, MyClass is the class name and myClassObject is the object reference. It is a convention to begin the class name with an uppercase letter while the corresponding object reference name starts with a lowercase letter. Declaring an object’s reference is not the same as creating the object. Until an object is created and assigned to the reference, the reference simply points to a null value. The object is created using the new operator, as shown below.

    myClassObject = new MyClass();

At this time, the object is allocated memory on the heap, the JVM takes control of the object’s lifetime management, and the object is accessible through the reference and only through the reference. At any point of time, objects that lack active references will become the target of the garbage collector and will be released automatically when the garbage collector next executes to release all unused objects. The two separate statements shown above may also be combined together as a single statement, where the object reference is declared and assigned to a newly created object.

    MyClass myClassObject = new MyClass();

Because an object can be referenced through any number of individual references, a second reference (or any number of references) may be created to point to the same object, as shown below.

    MyClass myClassSecondObject = myClassObject;

To create more than one reference to the same object, we just need to create another reference and assign the previously created reference to it. An important distinction should be made here. When multiple references are created to point to the same object, the object should be created only once, using the new operator. Each time a new reference is declared, it should be assigned one of the previously created references. On the other hand, if we need to create two references, each of which points to a different instance of the same class, then the two objects should be created with the new operator, as shown below.

    MyClass myClassObject = new MyClass();
    MyClass myClassSecondObject = new MyClass();

Now the two references point to two different objects of the same class. Object references are used to access the object’s public data elements and execute its public access methods. Object references may also be used as method return values and may be passed as method arguments. In fact, objects created in one specific method of a class are typically accessed in another method of another class. How does it work? Simply by passing the object references as method arguments. As long as the object is accessed somewhere, through this mechanism, the JVM considers that the object is being used and therefore does not make it a target for garbage collection. Typically, objects become obsolete when their references go out of scope. All object references declared within the scope of a method are local to the method and go out of scope at the exit point of the method. Object references declared within the scope of the class are global to all the methods and they do not go out of scope until the object is destroyed.

Typically, when object references go out of scope, there is no way to access the object. As long as the object reference is passed from one place to the other through method arguments, the object is in active use. Whenever the last reference goes out of scope, it becomes an orphan object. Orphan objects still occupy memory on heap. This is where the C++ programmer is expected to release the object through the delete operator, whereas in Java, the object becomes a target for garbage collection without the intervention of the programmer. Thus, in C++, the object is immediately destroyed and its allocated memory is immediately released when the object is deleted explicitly by the programmer. On the other hand, the Java programmer cannot destroy individual objects explicitly as there is no delete operator and because the language is not designed to support this feature. However, the programmer may explicitly call the System.gc() method to suggest to the JVM that the garbage collector may be run to release orphan (or unused) objects. As mentioned, this is only a suggestion to the JVM. The JVM does not guarantee that it will release all the objects; it only makes its best efforts to do so. This is one of the reasons that typically the Java programs need more memory for better performance (or run slower on a computer with less memory) than C++ (or other similar language) programs, as noted by many of the Java-based IDEs compared to C++ or Delphi or Visual Basic-based IDEs. The second reason for the slow performance of Java programs is the intervention of the JVM middle layer that is typically an interpreter.

The java.lang package is the core fundamental package for Java programs to run and therefore is automatically imported in every Java program and is not required to be explicitly imported. The Object class in this package forms the root class from which any Java class is derived. When we declare classes without specifying a super class (to derive from), the compiler automatically assumes the java.lang.Object class as the super class. The Object class implements basic methods required for all Java objects to enable it to be run within the JVM. If a class does not implement a constructor, the basic constructor provided by the java.lang.Object class is automatically used. However, our custom classes may require implementing one or more constructors, depending on the custom initialization requirement of our class.

As mentioned earlier, objects are instances and classes are design blueprints for the object’s behavior with the external world. The internal data variables of the class are called fields, and the functions through which the fields are accessed are called methods. The fields of a class can be primitive data types or object references. There are different types of access specifiers that control the member fields and methods. The private access specifier to a class member indicates that the member is accessible only to other members within the class. An attempt to access private members by external objects will raise a compilation error. The public access specifier to a member will make it accessible to the external world in addition to being accessible to other members of the class. The protected access specifier indicates that the member is accessible to all the classes within the package that the current class belongs to and within any class that inherits the current class, in addition to being accessible to its own members. It is suggested that the class member fields be declared private for better encapsulation of the object’s data, while the fields are accessed through public methods known as getter and setter methods. The getter method of a field is used to retrieve the field’s value, and the setter method is used to set the value. The preferred convention for the name of the field indicates that it should start with a lowercase letter; appending the field’s name to the word get, after converting the first letter of the field name to uppercase, makes the getter method name. The setter method name is made in a similar way. In the example program shown in Listing 5.18, the field name color has access methods named getColor and setColor. Even though we do not follow the convention, the program is syntactically correct and works well in normal situations such as standalone programs; however, when working with advanced technologies such as JavaBeans™ and Enterprise JavaBeans, the conventions should be followed because external components such as the application servers (or containers) are designed to follow the conventions. In such conditions, if we don’t follow the conventions, our components may not work as expected with these external components.

The java.lang.Class class represents running instances of objects and their interfaces within the context of a JVM. Instances of this class are not constructed explicitly, as the JVM automatically constructs the Class objects when instantiating specific classes while running an application and loading them through the class loader. In a way, this class provides runtime type information of the classes. Listing 5.18 displays a simple Java class demonstrating some of the concepts learned so far. More and more of these concepts can be visualized through the example programs as we progress through the chapter.

Listing 5.18: car.java
Start example
/* 
Multiple lines of comments can be written
by enclosing the lines as shown in this example.
This is a simple Java class example
*/
public class Car {
    private int cylinders;
    private String color;
    private String make;
    private String model;

    public int getCylinders() {
        return cylinders;
    }
    public void setCylinders(int cylinders) {
        this.cylinders = cylinders;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public String getMake() {
        return make;
    }
    public void setMake(String make) {
        this.make = make;
    }
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
}
End example

An object’s members are accessed through the dot (.) notation; for example if myCar is an instance of the Car class, the methods are accessed through code similar to myCar.getModel(), myCar.getCylinders(), and so on. From the example, it can be noted that the current object instance is referred through the this keyword, which acts as a reference to itself.

Because Java is an object-oriented language, one would expect to derive new classes from existing classes. The class from where a new class is derived is known as the base class or the super class. The new class that is derived from the base class is known as the derived class or the sub class. The process of deriving a sub class from a super class is known as inheritance, and the sub class is known to extend the super class. It is very important to understand the rules (or features) of inheritance and the relationship between sub classes and super classes in order to better design our applications and better understand third-party programs. A sub class can have access to the public and protected members of the super class, while the private members of the super class remain private to itself. Thus, the protected members of the super class provide an intermediate-level access compared to the public and private members. When a sub class extends the super class, all the public and protected members of the super class are automatically available through the dot (.) notation. Because typically the super class’s data members are private (by convention), they are not accessible in the sub class directly; however, they are accessible through the protected and public methods only. If the super class defines some of its data members as protected, then they can also be accessed through the sub class. However, if the sub class reimplements a super class’s method (with the same signature), then that method is automatically overridden in the sub class. Therefore, when we attempt to access the method through a pointer to the sub class object, by default the sub class’s implementation is invoked. If we wish to invoke the super class’s implementation of the method within the sub class’s methods, then we should use the keyword super and then the dot (.) notation to access the members. In simple words, the super keyword refers to the super class while the this keyword refers to the current class.

With respect to inheritance, the following scenarios are important to keep in mind.

  • A sub class also contains the super class implicitly as it is derived from the latter. However, the object types of sub class and super class are always different. Accessing the super class members through super class object reference and sub class members through sub class object reference is usually the most direct approach.

  • Because a sub class also contains the super class by default, through the inheritance mechanism, accessing the sub class members through the super class object reference is safe. However, in this way only the members as declared by the super class are visible because it is the super class object reference. The sub class’s members are not visible through the super class’s object reference. It is illegal from the point of view of the compiler to attempt to access sub class–only members through the super class object reference.

  • Attempting to access a super class object through the sub class reference is also illegal and will raise a compilation error because the sub class reference expects the sub–class only members also to be available (or visible) in the object in addition to the members inherited from the super class, and as the object is an instance of the super class, it does not contain sub class–only members. In this case, the object should be cast to the appropriate type (or the object cast type and object reference type should match) before attempting to access the object. Consider the following method.

        public void processEngineHorsePower(java.lang.Object vehicle) {
        Car car = (Car)vehicle;
        }
  • In the above method signature, vehicle is an instance of java.lang.Object class or its sub class. However, in the context of the method, it is expected that the object represents a Car sub class and therefore the local object reference car is assigned to the properly cast object as shown on the right hand side. However, if the input argument does not represent a Car sub class, then the cast operation would result in a runtime error. This situation does not arise a compile time error because it cannot be determined whether the cast would succeed or fail until the program is executed. The process of determining (or casting) an object type at run time is also known as late binding.

  • If a class is declared as final, it means that it should be the last class in the class hierarchy and should not be extended. This means that a final class can never be a super class.

  • Variables declared in a class outside of all the method declarations are global and available to all the methods. Similar to the classes, global variables and methods of a class may also be declared as final. In such a case, final variables must be assigned values at the time of declaration because they cannot be changed later. An attempt to extend a final class or change the final members or an object would result in compilation error. In a way, this is similar to the const keyword in C++. If the final attribute is used with a method, it means that it cannot be overridden in a sub class.

  • By default all the methods and global variables are nonstatic, which means that every individual instance of the class will have its own copy of these methods and global variables. Sometimes it requires that only one copy of the variable should be maintained for all the objects of the class. This may be the case when it does not make sense in the application to create multiple copies of the method or global variable, or we may have to execute a method without instantiating an object of the class. Such methods and variables should be explicitly declared as static. As mentioned here, only static methods can be called from a class (using the dot notation with the class name) without instantiating an object of the class. Using static methods and global variables in appropriate places will certainly help improve performance, as unnecessary duplication is avoided. Static variables are also known as class variables because their scope is at class level as against object level. On the other hand, the non-static variables are known as instance variables because their scope is at instance level. Static variables and methods are implicitly final because they also do not change after they are created. It is important to note that a static method cannot invoke a nonstatic method in its code, as the nonstatic methods can only be executed on instances and not on classes. For example, the System.gc() method that we invoke to request execution of the garbage collector is a public static method. A sample declaration of a static variable and a static method is given here.

        static myName = "Satya Sai Kolachina";
        public static void updateStatus(java.lang.String status) {
        . . . .
        . . . .
        . . . .
        }
  • Although the private methods of a class are also implicitly final because they cannot be overridden in a sub class, the private variables are not implicitly final, as they can be changed anywhere in the class.

  • Classes declared as abstract cannot be instantiated and are best suited as super classes, and classes at several levels in a class hierarchy may be abstract. On the other hand, classes that can be instantiated into objects are known as concrete classes. It is not necessary to have abstract classes in the class hierarchy. However, it is very helpful to implement polymorphic behavior by employing abstract classes. Though abstract classes cannot be instantiated, we can create references to these classes in order to generalize the more concrete classes in the hierarchy. For example, if Vechicle is an abstract class with two concrete sub classes Car and Truck, then a reference to the Vechicle class may be used to access either a Car object or a Truck object. A method of a class may be written to accept a reference to the Vehicle object as input argument, whereas an invocation of that method might actually receive a Car object or a Truck object. This is an example of utilizing the polymorphic behavior of Java language. Similarly, the method may return a Vehicle object to the calling program. The program that needs to process this object must cast it to the appropriate type (Car or Truck) as discussed earlier, before attempting to access the object.

  • Abstract classes contain one or more abstract methods. The presence of at least one abstract method means that the class must be declared abstract. An abstract class can also have one or more non-abstract methods, instance variables, and constructors. The sub class that extends the abstract class must override the abstract methods of the super class, while overriding the non-abstract methods is not required. Those non-abstract methods of the abstract super class that are not extended by the sub class will provide a default implementation to the sub class. Because abstract classes must be extended by sub classes, they cannot be declared as final.

  • When a method is declared as abstract in an abstract super class, it leaves flexibility for the sub classes to provide their own implementation, thus supporting polymorphic behavior within the class hierarchy. The declaration of a method as abstract indicates that its implementation is only available in the sub classes. Because abstract classes and late binding go hand-in-hand, it simplifies the process of adding new members in the class hierarchy.

  • It has been mentioned earlier that Java does not support multiple inheritance, which is supported by C++. Then what is the mechanism to inherit the behavior of multiple classes to a sub class? The solution provided by Java is the use of interfaces. An interface is a definition of how an object should behave. Because an object’s behavior is identified through its methods, an interface definition contains one or more method signatures without an implementation. While a sub class extends a super class (or one and only one super class) in order to inherit its characteristics, it implements one or more interfaces. Note the keywords extends and implements. When a class implements an interface, it means that it agrees to provide the behavior promised by the interface to the rest of the world. This means that the implementing class must implement every method declared in the interface definition, otherwise the class cannot be compiled. Every class that implements an interface can have its own implementation of the methods inherited from the interface definition. Consider the following interface definition with respect to our classic example.

        public interface PowerTransmission {
        public void transmitPower();
        }
    
        public class Car extends Vehicle implements PowerTransmission {
        public void transmitPower() {
        . . . 
        . . . 
        }
        }
        public class Truck extends Vehicle implements PowerTransmission {
        public void transmitPower() {
        . . . 
        . . . 
        }
        }
        public class Ship extends Vehicle implements PowerTransmission {
        public void transmitPower() {
        . . . 
        . . . 
        }
        }
        public class Plane extends Vehicle implements PowerTransmission {
        public void transmitPower() {
        . . . 
        . . . 
        }
        }
        public class SewingMachine implements PowerTransmission {
        public void transmitPower() {
        . . . 
        . . . 
        }
        }
    
  • In this example, the PowerTransmission interface defines a method named transmitPower(), which does not take any input arguments and returns a void type. This is a generic method that can be implemented by any equipment that has to transmit power from one component (where the power is generated) to another (where the power is consumed). Notice that a car, truck, ship, plane, and sewing machine all need to transmit power from the point of generation to the point of consumption. While the first four classes inherited from the Vehicle super class, the last class only implemented the PowerTransmission interface as it is not a vehicle by its design. In addition, each of the sub classes implement the transmitPower() method differently because the method of power transmission is different among these types. When a class implements more than one interface, each additional interface name is appended to the previous interface name separated by a comma (,) as shown here.

        Public MyClass extends MySuperClass implements interface1,
        interface2, interface3 {
        }
    
  • While abstract classes and interfaces have similarities between themselves, there are some differences that should be noted. Because abstract classes are classes anyway, the sub class cannot extend more than one abstract class, whereas interfaces should be used when a class is expected to show different behavior patterns, and therefore multiple interfaces can be implemented by a sub class. The other difference is that abstract classes can have their own non-abstract methods that provide default implementation when the sub class does not override these methods, whereas interfaces do not (and should not) provide any default implementation. Although designing interfaces is done usually by system designers and architects, they are used by the developers in their class implementations. Another use of interfaces is to implement the callback mechanism. The interface that supports a callback function simply declares the method signature in its definition. A callback function is a method of an object that is used to notify the object of any external events. The object that implements a callback method simply registers itself with another object (also known as notifying object) by way of passing its own reference. When an event (of interest to the callback object) occurs, the calling object executes the callback function of the callback object. If the callback method signature is declared in an interface, then any object that implements this interface behaves like a callback object. This is similar to the way Java implemented its event model, as discussed in the Multiple Document Interface example in Chapter 6. Because the core C++ language does not support interfaces, it implements callback functions through function pointers.

  • Because interfaces are only method declarations, instances of interfaces cannot be created using the new operator, as we do with the concrete classes. However, an interface reference may be declared, and an object of the class that implements the interface can be assigned to this reference, as shown here.

        PowerTransmission car = new Car(..);
        PowerTransmission truck = new Truck(..);
        PowerTransmission sewMachine = new SewingMachine(..);
  • In this example, the objects created on the righthand side of the statement are assigned to the respective interface references on the lefthand side. This is perfectly legal to the compiler.

  • Whether an object is an instance of a specific class or an instance of one of its sub classes, or it implements a specific interface, or one of its sub interfaces, may be checked by the interfaceof keyword in an if construct, as shown here.

        if (myObject instanceof MyClass) { . . . }
        if (myObject instanceof MySuperClass) { . . . }
        if (myObject instanceof Interface1) { . . . }
        if (myObject instanceof Interface2) { . . . }
    

Program Control Constructs

The program control constructs in Java are very similar to those in C++, partly because it is modeled after C++. These constructs are presented in this sub-section. There are multiple variations of the if, if / else, and if / else if / else constructs, as shown here.

    if (<conditional expression1>)
        <execution statement>

    if (<conditional expression1>) {
        <execution statement1>
        <execution statement2>
        <more execution statements >
    }

    if (<conditional expression1>)
        <execution statement1>
    else
        <execution statement2>

    if (<conditional expression1>) {
        <execution statement1>
        <execution statement2>
        <more execution statements >
    }
    else {
        <execution statement3>
        <execution statement4>
        <more execution statements >
    }

    if (<conditional expression1>) {
        <execution statement1>
        <execution statement2>
        <more execution statements >
}
    else if {<conditional expression2) {
        <execution statement3>
        <execution statement4>
        <more execution statements >
    }
    else if (<conditional expression3) {
        <execution statement5>
        <execution statement6>
        <more execution statements >
    }
    < . . . >
    < . . . >
    < . . . >
    else {
        <execution statement7>
        <execution statement8>
        <more execution statements >
    }

As noticed here, the simple if construct is used when a conditional statement is evaluated to be true. If the number of statements to be executed is more than one, then these statements must be enclosed within curly braces {. . .} as shown in the second type. On the other hand, if we need to specify statements to be executed for both situations where the conditional expression evaluates to true as well as false, the if / else type construct should be used. The else condition is applicable for all situations where the if condition fails; for example, the statement if (x == 5) <statement1> else <statement2> means that the statement1 is executed when the variable x contains the value 5 and the statement2 is executed for all other conditions where x is less than 5 and more than 5. This construct may be extended further for testing multiple conditional expressions with the if / else if / else construct. In this construct, any number of else if conditions can be specified. Any specific else if condition actually specifies a new conditional expression that is different from all the previous if and else if conditions. As mentioned above, it should be kept in mind that multiple execution statements must always be enclosed in curly braces. Otherwise, the program often fails compilation. However, there may be situations where syntactically the program looks correct but fails in execution. Consider the following situation. A company pays $100 per week as bonus and $150 per week as additional allowance for its sales employees if the base salary is less than $1500 per week and pays $200 as bonus and $250 as additional allowance if the base salary is $1500 or more.

    int bonus = 100;
    int additionalAllowance = 150;
    if (baseSalary >= 1500)
        bonus = 200;
        additionalAllowance = 250;

Experienced programmers can easily figure out the error in this code. However, new programmers may fail to do so because they will usually look for syntactical errors while overlooking the errors in the logic. Although this program is syntactically correct and compiles error free, it will not perform what is intended. As there are no curly braces enclosing the two statements after the if construct, only the first statement bonus = 200 is executed when the conditional expression is evaluated true, and the second statement additionalAllowance = 250 is executed always whether the conditional expression succeeds or fails because it is out of the conditional construct. In this kind of situation, when the curly braces are missing, the compiler considers only the first statement to be executed after the conditional expression. However, if the construct has an else or else if condition after the execution statements, then the compiler raises a compile time error because it expects an else or else if condition after the first execution statement itself. More than one conditional expression can be joined logically; the logical or is specified by two vertical bars || and the logical and is specified by two ampersands &&, as shown here.

    if ((x > 10) || (y < 20)) {
        <execution statements>

    }
    if ((x > 10) && (y < 20) && (z == 5)) {
        <execution statements>
    }

It should be noted that each conditional expression is individually enclosed in parentheses as in (x > 10). By doing so, the compiler groups the conditional expressions by prioritizing the expressions in parentheses. It is strongly suggested that parentheses be used to explicitly tell the compiler your grouping of expressions instead of making assumptions about what the compiler would do by default. The negation of a conditional expression is specified by a preceding exclamation mark ‘!’ before the conditional expression, as shown here.

    if (!(x > 10)) {
        <execution statements>
    }

The logical operators are >, >=, <, <=, ==, and != used for greater than, greater than or equal to, less than, less than or equal to, equal to and not equal to, respectively. Another common mistake made by new programmers is to use single = symbol for comparing equality of operands in a conditional expression. It should be remembered that a single = symbol is used for assignment operation, and a double == symbol should be used in conditional expressions. If the single = symbol is used in a conditional expression, the compiler will not generate an error; rather, the program will not perform what it is supposed to.

In a majority of situations, the multiple conditional constructs can be conveniently replaced by the switch . . . case construct, whose syntax is provided here.

    switch (selection)    {
        case <value1>:
            <execution statements1>;
            break;
        case <value2>:
            <execution statements2>;
            break;
        case <value3>:
            <execution statements3>;
            break;
        . . . 
        . . .
        default:
            <default execution statements>;
    }

In this construct, switch, case, break, and default are Java language keywords and should be used exactly like that. All others are user defined. The way it works is that selection is an int data type variable whose value is checked against each of the case statements having specific value to be compared with the selection variable. If the value of selection matches one of the case statement’s values then the execution statements specified for that case statement are executed. Each case statement can have any number of execution statements. The values specified for the case statements must also be of int data type. However the individual case statements should be separated by the break statement; otherwise, the control automatically falls to the default statement and executes all the default execution statements even though one of the case statements was successfully executed. An important point to keep in mind is that the operands of the conditional expression, selection and values are of int data type. Therefore if we have a different type of conditional expression, then we should properly convert that condition to an integer type before using this construct. If your programming situation does not permit such a conversion, then you are free to use the multiple if . . . else if . . . else construct.

In addition to the conditional constructs, Java language also supports a multitude of repetitive loop constructs as discussed here. The for loop is very popular and safe to use as it always provides a nice exit criteria. The for loop executes for a specified number of iterations as shown in the syntax here.

    for (<initialization>; <condition>; <increment counter>) {
        <execution statements>;
    }

    for (int i=0; i < 100; i++) {
        <execution statements>;
    }

The first construct shows the general form of the for loop, and the second construct is an example. The for loop conditional expression consists of three parts; the first part initializes a loop counter to an appropriate value, the second part evaluates the conditional expression, and the third part increments the loop counter. The loop counter may be an integer or a floating point value, and the increment may be any fixed value. The conditional expression evaluates the loop counter against a maximum value for the loop counter. If the for loop is for a decrementing counter, then the conditional expression evaluates the loop counter against a minimum value. Because the number of iterations of the for loop is controlled through a loop counter against a maximum (or minimum) permitted value, the number of iterations is always finite, and the loop guarantees an exit criterion. Typically, the value of the loop counter is not expected to be updated within the execution statements of the loop. Otherwise, it is a common programming mistake to decrement the loop counter within the loop making the for loop infinite. If it is necessary to use the loop counter in an update mode, then it is strongly suggested that a copy of the loop counter be created and used instead of the counter itself, as shown here.

    int j=0;
    for (int i=0; i < 100; i++) {
        j=i;
        <execution statements>;
    }

Another looping construct available in Java is the while construct, whose syntax is shown here.

    while (conditional expression) {
        <execution statements>;
    }

The conditional expression in this construct is similar to the one used in an if construct. The while loop is executed as long as the conditional expression evaluates to true and exits only when the conditional expression evaluates to false. Therefore it is the responsibility of the programmer to provide definite exit criteria for the loop; otherwise the program would fall into an indefinite loop. The while loop does not contain any built-in mechanism to exit after a specific number of iterations, so it should be used carefully.

A variation of the while loop is the do while loop, whose syntax is shown here.

    do {
        <execution statements>;
    } while (conditional expression)

In the case of the do while loop, the conditional expression is not evaluated until after the execution of the first iteration, so at least one iteration of the loop is guaranteed always, whereas in the while loop, the conditional expression is evaluated before executing the first iteration and may not guarantee the execution of the first iteration.

In order to have a better control and customized exit criteria of the looping constructs, the break and continue statements are provided. These statements are very useful particularly as the looping constructs are very powerful when nested loops (or multiple levels of loops) are designed. The break statement immediately breaks the innermost loop in which it is executed and program control is transferred to the statement that follows after the loop that it exits, and may be used in all the looping constructs. The break statement is also used in the switch construct, as seen earlier, in order to skip the remaining statements (particularly skipping the default statement) after completing one case statement. The continue statement is used to skip the rest of the current iteration and start the next iteration in the loop, and should only be used in the looping constructs. We have noted previously that the break and continue statements operate on the innermost loop by default. However, either of these statements may be used with a label that identifies a specific loop instead of operating on the innermost loop, as shown here.

    outer:
    while (conditional expression) {
        <execution statements>;
        inner:
        while (conditional expression2) {

            if (conditional expression3)
                break outer;
        }
    }

In this example, the labels outer and inner are provided to identify the loops and the statement break outer: specifies that the break statement should break the outer loop instead of the inner loop. If the label is not used, by default, the break statement breaks the inner loop. It should be noted that the loops are not necessarily labeled always; they are usually labeled only as required by the break or continue statement.

The Arithmetic Operators

As one could expect, the basic arithmetic operators +, -, *, and / are used to perform addition, subtraction, multiplication, and division operations, respectively. The = operator is used as an assignment operator in statements to assign the value of the variable or object reference on the righthand side of the statement to the operand on the lefthand side of the statement. For the assignment operator to succeed, the data types (or object references) on both sides of the statement must match (or should be convertible) for all the primitive types and object references. In case of object references, the assignment operation should match the rules of inheritance discussed earlier in this section; otherwise the operation would result in compilation error in a majority of situations or a runtime error in some instances.

The ++ and -- operators are used to increment or decrement, respectively, a numeric variable value by one. The += operator is used to combine the addition and assignment operations into one operation as shown in the following example.

    int k = 10;
    k += 15;

In this example, the second statement adds a value 15 to the current value of the variable k, and is equivalent to the following statement.

    k = k + 15;

Similarly, the = operator is used to decrement and assign in a single operation. The exponentiation operation is performed through the pow() method of the java.lang.Math class, as shown here.

    double result = java.lang.Math.pow(base, exp)

The base, exp, and result are all double-precision floating point variables. The java.lang.Math class has many more mathematical functions that the reader is encouraged to refer to. In addition, there are other bitwise operators (similar to C++) supported by Java that are not discussed here.

Exception Handling

Almost all the modern object-oriented languages are capable of efficient exception handling compared to many of the previous generation simple procedural languages, and Java is no exception. Because Java is bestowed with a complete suite of classes and interfaces, exception handling is pre-built within these packages. In addition, Java allows every method of a class to handle the exception condition in a special way by throwing an object that contains the information about the error occurred. The object that is thrown is known as an exception object and should extend the java.lang.Throwable class, which provides the basic exception handling features, or one of its sub classes. The Throwable class is further derived by two sub classes, java.lang.Error and java.lang.Exception; although the first type is used to identify internal errors and therefore is not much useful for the programmers, the second type is used to provide extensive support for the programmers. There are many more exception classes derived from the Exception class, which can be directly used or extended to suit our needs. One important sub class is java.lang.RuntimeException, which may be confusing, as typically any exception occurs only during runtime of an application. An instance of any exception class that extends the RuntimeException is known as a runtime exception object. The term runtime exception is used to identify exceptions that occur while running a Java application during the normal operation of a JVM, and is automatically thrown when such an exception condition occurs. This means that a method (of a class) does not have to explicitly specify a runtime exception in its throws clause. Examples of runtime exceptions are instances of java.lang.ArithmeticException, java.lang.ClassCastException, java.lang.SecurityException, java.lang.NullPointerException, java.lang.IndexOutOfBoundsException, and so on. The meanings of these exceptions are self-explanatory from their names.

Examples of exceptions that are not runtime exception objects are typically related to the specific business functionality supported by a specific package, and are instances of java.io.IOException, java.lang.NoSuchMethodException (and their sub classes), and so on. Although these exceptions also occur during runtime, these are considered to be more of non-runtime exceptions because they are specific to their business objects. There are many exception classes provided in Java 2 SDK that the readers are encouraged to refer to. When an object’s method is prone to an exception condition that is not of java.lang.RuntimeException and java.lang.Error type then that method should declare to the external world that it is prone to throw an exception when it is used in other objects. This is done through the throws clause in the method’s declaration (or header), as shown in the example declaration.

    public void processSpecialFile(String filename)
        throws EOFException, FileFormatException {

    }

As seen in the example, any number of exceptions can be thrown by a method, as required by the business functionality. When an exception is thrown, it should be handled within a try . . . catch block, or rethrown such that the user of the current object will be able to catch it in its try . . . catch block. The typical syntax of a try . . . catch block is shown here.

    try {
        <execution statements>;
    }
    catch (EOFException ex) {
    }
    catch (IOException ex) {
    }
    catch (Exception ex) {
    }
    finally {
    }

A try . . . catch block may code one try block and at least one catch block; we may code any number of additional catch blocks as required. Code executed within the try {. . .} block is known as protected code, as an exception occurring in this block is caught in the appropriate catch {. . .} block, if one is coded. Each of the catch blocks is used to catch one exception object, and therefore is known as an exception handler. If a catch clause catches a higher level exception object, then it will not search further for more appropriate catch clauses. Therefore, the first catch clause should attempt to catch the lowest level exception in the hierarchy, followed by the next lowest level exception, and so on. If there is no hierarchical relation between the different exceptions being caught, then they can be specified in any order. In the example shown before, the EOFException is a sub class of IOException, which in turn is a sub class of the Exception class. Therefore we attempt to catch the EOFException first. If the exception that occurs in the program is of EOFException type, then it is executed first and others are ignored; on the other hand, if it is some other type of IOException, then it is caught in the second catch block, and so on. The more catch blocks you code, the better control on individual exceptions can be handled; however, the code may become complex. To simplify the process of catching exceptions, we may just code a single catch block for the Exception class, which is the ancestor of all the exceptions that we intend to catch. When an exception of a specific type is caught and a corresponding handling block is found (either for the same exception object type or for an object of higher level exception class), then the exception object is passed to the handling catch block where it may handled as desired. There are different ways of handling exceptions within a catch block. We may log the message and continue execution, we may display the message to the console and terminate the program gracefully, or we may throw another (custom) exception to a higher level object that can catch and process the exception. The finally {. . .} block is optional and may be used to release any resources such as database connections or close socket connections and so on. However, if coded, the finally block is executed in all circumstances, whether an exception occurs or not.



Previous Section
 < Day Day Up > 
Next Section