Home Company Services Experience Process Articles FAQs Contact Us

Java Language Primer


1.      A Java Application Example

 

The best way to get familiar with a language is to see it at work. Here is an example of Java application, the first form of Java executable.

 
/** The EchoDemo class defines a Java application that takes words from standard input and print them to standard output. */
 
class EchoDemo {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++)
             System.out.println(args[i]); // print each word.
    }
}
 

To run the application,

 

        >>java EchoDemo Hello World!
        >>Hello
        >>World!
 

In Java, there are no “global” variables or methods as in C and C++; everything must be defined within classes. The main function must be public so that it can be invoked by Java interpreter. It must be static because when it is invoked by interpreter, class EchoDemo has not been instantiated yet. String[] is the way interpreter passes command line arguments to the Java application. The main function must return void, because no action can be taken on its return value, but System.exit() can be used to return value from the main function to Java Interpreter.

 

Java interpreter starts one thread for the main function. After main exits, Java interpreter continues executing other threads of the application until all of them exit.

 

Since main itself is static, we can be pretty sure that System.out is a static variable. What is less obvious is that System.out refers to an instance of PrintStream class, which is instantiated when System class is loaded. Therefore System.out.println is actually an instance method.

 

If the interpreter cannot find a main function, it displays the error message: In class NoMain: void main(String argv[]) is not defined.

2.      A Java Applet Example

 

Applet is another form of Java executable, which shall be started from a Web browser.

 

import java.applet.Applet;
import java.awt.Graphics;
 
public class AppletDemo extends Applet {
        String title;
        public void start() {
               title = getParameter(“DemoTitle”);
        }
        public void paint (Graphics g) {
               g.drawString (title, 100, 100);
        }
}

 

To run this applet, embed it in an HTML file which locates at the same level as directory “Demo”. The “Demo” directory contains the AppletDemo.java and AppletDemo.class files.

 

<APPLET CODE=”AppletDemo.class” CODEBASE=”Demo/” WIDTH=50 HEIGHT=75>
        <PARAM NAME=”DemoTitle” VALUE=”Hello world!”>
</APPLET>

3.      Object-oriented Programming

 

Object is a runtime software entity that bundles related variables and methods. The idea is to maximize external extensibility and minimize internal modification – the so-called open-closed principle. Class is the static blueprint that defines the states and behaviors of all the objects of a certain kind. The action that Java runtime creates an object from its class is called class instantiation. An interface is a contract between an object and its users in terms of the object’s external behaviors, that is, interface defines a set of methods that the object’s uses can invoke to access the object’s states. Interface does not define the internal states or implementation of an object. In Java, interface cannot be instantiated.

 

Java is an object-oriented language. Class and interface are the two basic building blocks of Java program. Every variables and methods are declared and defined within classes or interfaces. Java program is built upon the inheritance and association among classes and interfaces. Inheritance lets one object reuse another object’s states and behaviors (extends) or honor another object’s external behaviors only (implements). Association is a general term to describe that one object knows about another object, can access its states and invoke its methods. Inheritance is built as part of the declaration of the object’s class or interface. Association is through the reference to the designated object.

4.      Class Declaration

 

Following is the syntax of declaring a Java class.

 

[public, abstract, final] class ClassName 
               [extends] BaseClassName
               [implements] InterfaceName1, . . ., InterfaceNameN
 

By default, a class can be used only by classes in the same package. Modifier public makes the class usable by any classes.

 

Modifier abstract indicates that the class cannot be instantiated; therefore its only purpose is for subclass. Abstract methods are the ones that have no implementation. An abstract class may or may not have abstract methods, but only abstract class can have abstract methods. A non-abstract class must provide implementation for all its methods and the methods it inherits from its abstract base class.

 

Modifier final indicates that the class cannot have any subclasses. Non-final class can have subclasses, but if it defines any final methods, the implementation of the final methods cannot be changed in the subclass.

 

A class can inherit from only one base class. The rule of inheritance is: A subclass inherits all of the members in its base class that are accessible to that subclass unless the subclass explicitly hides the member variables or overrides the member methods. By using the same name, the member variable in the subclass hides the one in the base class; by using the same signature, the method in the subclass overrides the one in the base class. Abstract methods must be overridden in all the subclasses. Final methods cannot be overridden in any subclasses. Note that constructors are not members and therefore not inherited by subclasses.

 

A class can implements any number of interfaces. Since all interface methods are public and abstract, classes that implement an interface must provide implementation for all its methods.

5.      Interface Declaration

 

Following is the syntax of declaring a Java interface.

 

[public] interface InterfaceName
               [extends] BaseInterfaceName1,. . ., BaseInterfaceNameN

 

By default, an interface can be used only by interfaces and classes in the same package. Modifier public makes the interface usable by any interfaces or classes.

 

All methods of interfaces are implicitly public and abstract. This is different from abstract class, since abstract class can have non-abstract method and methods with other types of accessibility (private, protected and package). All variables of interfaces are implicitly public, static and final.

 

Since all classes implement an interface must provide implementation for all the methods of the interface, adding new methods to an existing interface would break all the inherited classes. One way to solve this is making the new interface a sub interface of the old interface instead of changing the old interface directly. The other way is to provide an adapter class for each interface and the adapter class provides the default implementation for all the methods. Instead of implementing the interface directly, subclasses shall extend the adapter class. When interface changes, changes can be confined at the adapter class level. The draw back is that only one adapter class can be extended, thus only one interface can be indirectly implemented.

6.      The “this” and “super” keywords

 

Keyword this is used to explicitly refer to a member of the same object. For example,

 
class Shape {
    int x, y;
    void Drop (int x, int new_y) {
        this.x = x;
        y = new_y;
    }
}

 

Keyword this can be ignored and become implicit if there is no ambiguity, even though some practices prefer to use it all the time.

 

Keyword super is used to refer to a member of the base class. For example,

 
class Circle extends Shape {
    int x, y;                         // hide x and y of Shape
    void Drop (int x, int new_y) {    // override Drop() of Shape
        super.Drop(x, y);              // update x, y in Shape
        this.x = x;                    // update x in Circle
        y = new_y;                     // update y in Circle
    }
}

 

In the case that subclass members hide or override base class members, keyword super must be used to refer to the base class members.

7.      Class Members and Instance Members

 

Members are variables and methods defined within classes and interfaces. In Java, every variables and methods are members. Class members are those owned by class definition itself. They can be accessed through either classes (including interfaces) directly or instances of class (objects). Class members are declared with the static keyword. Instance members are those owned by instances of class (objects) and can be accessed only through objects. The restriction of class member and instance member cross reference is that class methods cannot contain instance members.

 

Class variables can be initialized at its definition or in the “static block”. For example,

 

Class MySystem 
    static int init() {
        return 0;
    }
}
 
class ErrorInfo {
    static final int MAX_NUMBER = 10;
    static int systemInitError;
    static {
        systemInitError = MySystem.init();
    }
}

There can be any number of such static definitions or blocks scattered in the class definition. Java runtime allocates memory for class variables per class when it first encounters the variable definitions in the class, in the order that the variable definitions or blocks are positioned. Note that a typical way of defining constants is to use static final.

 

Instance variables can be initialized at its definition or in the constructor. For example,

 

class Shape {
    int x = 0;
    int y = 0;
    void Shape (int x, int y) {
        this.x = x;    // “this” is used to avoid ambiguity.
        this.y = y;
    }
}
 

Java runtime allocates memory for instance variables per class instance (object) when that class is instantiated.

8.      Member Accessibility

 

Because all variables and methods are class members, member accessibility in Java is based on types (classes or interfaces), not on instances (objects). For example, object A can access the private members of object B, if A and B are instances of the same class; object A can access the protected members of object C, if class of A is the subtype of class of C, and they are in the same package. Within one object, the subclass portion can access the base class portion that is not declared as private.

 

Member accessibility is declared by modifiers: private, package, protected and public. Among them, package is implied when there is no modifier used. The following table summarizes the meaning of each modifier.

 

Members with Modifier

Same Class

Classes in Same Package

Subclasses

All Classes

private

X

 

 

 

package (default)

X

X

 

 

protected

X

X

X*

 

public

X

X

X

X

* The subclass must be in the same package as the base class to be able to access protected members of the base class. If you move the subclass from the base class package to a different package, suddenly, the subclass cannot access protected members of the base class any more. This implies that a subclass cannot get too much from its base class unless they are in the same package.

 

Java language does not provide a modifier so that a member can be accessed by subclasses only. One way to achieve that is to put only the class and its subclasses in one package. The drawback is that this package may not server the purpose of functional grouping.

 

Error message for accessing non-accessible variable is “Variable vvv in class aaa not accessible from class bbb”. Error message for accessing non-accessible method is “No method matching mmm() found in class ccc ”.

 

A special member is the nested class, which by definition, is the member of the enclosing class. Nested classes follow the same rules as variable and method members.

 

Even though constructors are not considered members, they can be guided by the same modifiers, where accessing means instantiating the class.

9.      Member Method Declaration

 

Following is the syntax of declaring a member method.

 
[public | protected | package | private, abstract, final, static, synchronized, native] methodName (argument1, . . ., argumentM)
               throws Exception1, . . ., ExceptionN

 

Modifier public, protected, package or private defines the accessibility of the method.

 

Modifier abstract indicates that the method has no implementation in this class.

 

Modifier final indicates that the method implementation cannot be changed in the subclasses.

 

Modifier static indicates that the method is a class method.

 

Modifier synchronized directs Java runtime to allocate a lock for the object that has at least one synchronized method. A thread must get the object lock in order to run any synchronized method of the object, therefore for a given object at any given moment there can be no more than one synchronized method running. Synchronized method is executed in this way: (1) The thread acquires the object lock; (2) The thread updates all the object variables with the values read from the “main” memory copy; (3) The thread runs this method; (4) The thread reflects any changes in variables back to the “main” memory; (5) The thread releases the object lock.

 

Modifier native indicates that the method is implemented in a platform-dependent fashion, such as in C.

 

In Java methods, arguments are always passed by value. If the arguments are primitive types, the original values of the arguments cannot be changed by the method. If the arguments are reference types, the method cannot change the values of arguments themselves, but can change the contents of the objects referred to by the arguments.

 

A method may encounter an exception anywhere within its code, or may use the throw statement to throw an exception explicitly. Runtime exceptions are those occur within the Java runtime. Checked exceptions are all other types of exception.

 

When an exception happens within a particular method, the method can either catch it, or not catch it but specify it hoping that its caller may catch it. It is an option to catch or specify runtime exceptions, but checked exceptions must be either caught or specified.

 

The throws clause is used to specify a list of uncaught exceptions. Specified exceptions will be checked by the compiler (where the name “checked” comes).  Java emphasizes the point that the exceptions a method can throw are really part of the method’s public interface. It is through the throws clauses of contained methods that a containing method knows about the possible exceptions so that it can decide which to catch and which to list in its throws clause.

10. Member Variables

 

There are two types of variables: primitive and reference. Primitive types are boolean, byte, char, double, float, int, long, short and void. Reference types are array, class and interface. An array is a fixed-length data structure that can contain multiple objects of the same type, any type. Array elements are indexed from 0 to length – 1. Each primitive type has a corresponding wrapper class, namely, Boolean, Byte, Character, Double, Float, Integer, Long, Short and Void. This is useful in Java reflection.

 

To achieve Java program’s portability, the format and size of primitive types are system independent. One table in Sun’s online Java Tutorial lists the definitions of primitive types. The table is copies here fore reader’s convenience.

 

Keyword

Description

Size/Format

(integers)

byte

Byte-length integer

8-bit two's complement

short

Short integer

16-bit two's complement

int

Integer

32-bit two's complement

long

Long integer

64-bit two's complement

(real numbers)

float

Single-precision floating point

32-bit IEEE 754

double

Double-precision floating point

64-bit IEEE 754

(other types)

char

A single character

16-bit Unicode character

boolean

A boolean value (true or false)

true or false

 

11. Scope of Member Variables

 

A variable's scope is a block of program within which the variable lives and can be referred to. Java runtime destroys a variable and releases the memory when the variable is out of scope. There are four types of scope: member variable, local variable, method parameter and exception-handling parameter, as illustrated in the following picture, copied from Sun’s online Java Tutorial for reader’s convenience.

 

Note that there is no global variable in Java as in C and C++. Every variable is declared and defined in some classes or interfaces.

12. Member Variable Declaration

 

Following is the syntax of declaring a member variable.

 

[public | protected | package | private, final, static, transient, volatile] variableName

 

Modifier public, protected, package, or private defines the accessibility of the member variable. Note that visibility and scope are two distinctive things.

 

Modifier final indicates that value of the variable cannot be changed after it is initialized. A final variable can be declared without initialization, the so-called blank final. Any attempt to modify a blank final will also result in a compilation error.

 

Modifier transient indicates that the variable shall not be serialized during object serialization.

 

Modifier volatile indicates that Java runtime will always kept the local copy of the variable in the running thread in sync with the copy in the “main” memory. That means, whenever the running thread updates the local copy, changes will be reflected in the “main” copy and flushed to all other running threads that have this variable.

13. Special Operators

 

To provide a handy reference, this section lists several special operators defined in Java. The following table is copied from Sun’s online Java Tutorial for reader’s convenience.

 

Operator

Use

Description

?:

op1 ? op2 : op3

If op1 is true, returns op2. Otherwise, returns op3.

[]

type []

Declares an array of unknown length, which contains type elements.

[]

type[ op1 ]

Creates and array with op1 elements. Must be used with the new operator.

[]

op1[ op2 ]

Accesses the element at op2 index within the array op1. Indices begin at 0 and extend through the length of the array minus one.

.

op1.op2

Is a reference to the op2 member of op1.

()

op1(params)

Declares or calls the method named op1 with the specified parameters. The list of parameters can be an empty list. The list is comma-separated.

(type)

(type) op1

Casts (converts) op1 to type. An exception will be thrown if the type of op1 is incompatible with type.

new

new op1

Creates a new object or array. op1 is either a call to a constructor, or an array specification.

instanceof

op1 instanceof op2

Returns true if op1 is an instance of op2

14. Operator Precedence

 

Precedence defines the rules of associating a list of operators. The following table is copied from Sun’s online Java Tutorial for easy reference. Operators listed in the same block have the same precedence; operators listed in the higher block have the higher precedence. As you are familiar with, a + b * c means a + (b * c). However, as with any languages, precedence is the last thing you need to remember 100%. For the sake of readers, or whenever you feel unsure, use “(” and “)” explicitly.

 

postfix operators

[] . (params) expr++ expr--

unary operators

++expr --expr +expr -expr ~ !

creation or cast

new (type)expr

multiplicative

* / %

additive

+ -

shift

<< >> >>>

relational

< > <= >= instanceof

equality

== !=

bitwise AND

&

bitwise exclusive OR

^

bitwise inclusive OR

|

logical AND

&&

logical OR

||

conditional

? :

assignment

= += -= *= /= %= &= ^= |= <<= >>= >>>=

 

15. Control Flow Statements

 

The following table summarizes all the flow control statements used in Java.

 

Looping

while loop

while (true | false) { . . . }

 

while-do loop

do { . . . } while (true | false)

 

for loop

for (begin; end; increment) { . . . }

 

Logical Branching

if-else if-else

if (true | false) { . . . }

else if (true | false) { . . . }

else (true | false) { . . . }

 

switch

switch (int) {

  case 0: . . .; break;

  default: . . .; break;

}

All the cases belong to one switch scope.

Other Branching

break

break [label];

Terminate the innermost loop

continue

continue [label];

Terminate the current iteration

return

return [value];

 

Exception Handling

try-catch-finally

try { . . . }

catch (name) { . . . }

finally { . . . }

The try, catch and finally blocks are distinctive scopes.

Thread Synchronization

exclusive access

synchronized (object) { . . . }

Mutually exclusive access to object

 

16. The Object Class

 

The Object class is the root of all Java classes. It defines the common states and behaviors that all Java objects shall have.

 

The Object class has several final methods that cannot be overridden by any descendent classes: getClass, notify, notifyAll and wait. The getClass method returns a Class object which represents the class of the object on which getClass is invoked. The notify, notifyAll and wait methods are used for thread synchronization purposes. The wait method halts the thread execution and relinquishes the object lock held by the thread so that other threads blocked by the lock can get it and move on. The notifyAll method wakes up all the threads that are waiting on the same object. The awaken threads compete for the object lock. Whoever wins gets chance to execute; others go back to waiting. The notify method arbitrarily wakes up one of the threads that are waiting on the same object.

 

The Object class also contains several methods that can be overridden by the descendent classes: clone, equals (and hashCode), finalize, toString. The clone method creates an object from the existing object on which clone is invoked. To make the existing object cloneable,  it must implement the Cloneable interface. The equals method is used to compare whether two objects are equal. The Object class’s implementation of equals returns true if the two objects are actually the same entity. The descendent class can override equals method to implement other criteria of equality, e.g. the two objects have the same contents. Note that the equals and hashCode methods must be overridden together. The finalize method is used to clean up the object before it is garbage collected. The toString method is used to obtain the String representation of the object.

17. The String and StringBuffer Classes

 

They both hold strings, that is, one or more character data, without the C/C++ style ending character. The number of characters can be obtained through method length. Characters of String or StringBuffer are indexed from 0 to length() – 1.

 

String stores characters statically, that is, it cannot be changed; therefore it is optimized for reading and sharing. On the other hand, StringBuffer stores characters dynamically, that is, it can be changed. StringBuffer has an extra method capacity, which returns number of characters being allocated. Whenever possible, prefer String to StringBuffer.

 

All classes inherit the toString method from the Object class, and can use the method to convert an object (including the StringBuffer object) to a String. Another way to convert an object to String is to use the valueOf method of String class. All primitive types can be converted to String using the valueOf method too.

18. The Applet Class

 

public class OneApplet extends Applet {
    public void init() { . . . }
    public void start() { . . . }
    public void stop() { . . . }
    public void destroy() { . . . }
    public void paint (Graphics g) { . . . }
}

 

The init method initializes the applet each time it is loaded or reloaded. Since applet constructor cannot do much before the applet is loaded, this method performs the actual one-time initialization for an applet, as usually done by constructor for normal object.

 

The start method starts the applet’s execution, such as launching threads and handling events. It is called when the applet is loaded or the page containing the applet is revisited.

 

The stop method stops the applet’s execution, such as suspending all the threads and releasing all the resources. It is called when the use leaves the page containing the applet or closes the browser. Overriding start and stop methods usually go hand-in-hand.

 

The destroy method usually does nothing since the stop method has done most of the job. However, it can perform some final cleanup before the applet is unloaded.

 

The paint method is the main way of displaying contents in a page. The other less used method is update, which supposedly improves drawing performance.

 

None of these five methods have to be overridden by user defined applet.

19. The Thread Class

 

In Java a new thread is launched by instantiating a Thread class. When the Thread class is instantiated, no system resources are allocated for it, and the only thing can be done to the Thread object is to invoke its start method. The start method allocates the system resources, schedules the thread, triggers the run method, and then returns. After start method returns, the thread is in the runnable state and will be running when it gets its share of CPU time. The run method of Thread class (which inherits the method from Runnable interface) defines the code to execute when the thread is running.

 

There are two ways to provide the run method. The first is to subclass the Thread class and override its run method directly.

 

public class MyThread extends Thread {
    public MyThread(String name) {
        super(name);
    }
    public void run() {
        System.out.println("Thread " + getName() + “ executed.”);
    }
}

 

public class TestMyThread {
    public static void main (String[] args) {
        new MyThread("MyThread").start();
    }
}

 

The second is to implement the Runnable interface and give itself as the runnable object to the new thread object. Programmer must use this way when the new class must be subtype of a class that is not the Thread class.

 

public abstract class YourClass {
    public abstract void start();
}
 
public class MyClass extends YourClass implements Runnable {
    public void start() {
        new Thread(this, "MyThread").start();
    }
    public void run() {
        System.out.println(“Thread “ + getName() + “ executed.”);
    }
}

 

public class TestMyClass {
    public static void main (String[] args) {
        new MyClass().start();
    }
}
 

A runnable thread becomes non-runnable under one of the three situations: (1) The sleep method is invoked; (2) The thread calls the wait method; (3) The thread is blocked (by I/O, object lock, etc). A running thread stops when the run method terminates naturally.

20. The ThreadGroup Class

 

Every thread belongs to a thread group, an object that collects multiple threads and manipulates them all at once. A thread is assigned to a group when it is created and can never change its group thereafter. If no group is specified when the thread is created, the thread is assigned to the same group as the thread that creates it.

 

When a Java application starts, Java runtime creates a ThreadGroup object named “main”. If the Java program never specifies any thread group, all the created threads would belong to the “main” thread group. For programs with small number of threads, this is usually the case. However for programs with large number of threads, it is better to create some new thread groups and carefully assign the proper group to each created thread.

21. The ThreadLocal Class

 

A ThreadLocal object provides a way of storing a separate set of variables just for a particular thread. The get()method retrieves the current value of the variable associated with the running thread. The set()method gives a new value to the variable associated with the running thread. The initialValue()method returns the initial value of the variable for the thread. It is defined as protected so that subclass can modify its behavior (return null by default). Each thread holds an implicit reference to its ThreadLocal object. After the thread goes away and there are no other references to the ThreadLocal object, the object is subject to garbage collection. For non-thread-safe resources such as JDBC connection, we can use ThreadLocal object to store the resource object on a per thread basis.

 

public class DbConnPerThread {
  private static class DbConn extends ThreadLocal {
    public Object initialValue() {
      return DriverManager.getConnection (url, userName, password);
    }
  }
 
  private DbConn conn = new DbConn();
 
  public static Connection getConn() {
    return (Connection) conn.get();
  }
}

22. Thread Scheduling

 

On a single CPU system, only one thread can run at any given time. The Java runtime allocates CPU time among all the runnable threads and makes them appear to be running concurrently. Java uses the preemptive, priority-based scheduling algorithm to allocate CPU time.

 

Every thread is associated with an integer, between MIN_PRIORITY and MAX_PRIORITY (defined in the Thread class), that represents its priority. Be default, a thread inherits its priority from the thread that creates it, and later on can change it using the setPriority method. The higher the integer is, the higher the priority. Java scheduling algorithms can be summarized as:

 

(1) Thread with higher priority gets the CPU time to run while thread with lower priority waits. However, the runtime may not guarantee that the highest priority thread is running all the time, e.g. to avoid starving the lower priority thread. Therefore don’t rely on thread priority for algorithm correctness.

(2) Among threads with the same priorities, each thread is scheduled to run in a round-robin fashion without preemptive. The chosen thread runs until it gives up its share of CPU time.

(3) A running thread can always give up its share of CPU time by exiting (run method terminates), or becoming non-runnable.

(4) A running thread can give up its share of CPU time to threads with the same priority by calling the yield method, but high priority thread cannot give up its share of CPU time to low priority thread by calling the yield method.

(5) When a thread with higher priority than the currently running thread becomes runnable, the runtime schedules its running right away (preemptive).

(6) Java runtime does not provide time-slicing, which means a running thread cannot be preempted by a thread with the same priority. However, some native threading systems under JVM such as Windows may support time-slicing. Time-slicing makes Java program less portable.

23. The Exception Classes

 

An exception usually means an event indicating that something needs special attention. In Java, an exception is (1) an object of Throwable class or its descendent classes that is created and handed over to Java runtime by a method using the throw statement, or (2) an object of Exception class that is thrown by one of the contained methods, or (3) an object of RuntimeException class that is generated by Java runtime itself.

 

Throwable object and its descendents are used by the throw statement. Among them, Error objects and descendents are for linking failure or some other system failure within JVM (usually Java program shall not throw or catch Error objects), and Exception objects and descendents are for problems that are Java program related.

 

Among Exception descendent objects, RuntimeException objects represent exceptions that occur within the JVM but somehow program related, such as dividing by zero, access through null reference, and array out-of-boundary indexing. All other Exception objects represent so-called checked exceptions, because they must be either caught by the method (in catch block) or specified by the method (through throws clause), therefore can be checked by Java compiler. On the contrary, RuntimeExceptions need not to be caught or specified (because they can be anywhere and too many), therefore cannot be checked at compilation time. As rules of thumb, Java programmer shall not mess up with the RuntimeException – don’t specify, throw and inherit from it, unless s/he really means to deal with runtime errors. In short, don’t use RuntimeException, use Exception instead.

 

 

24. Exception Handling

 

Within a method, exception handling uses the try-catch-finally structure, as in the following example.

 
public void writeList() {
    PrintWriter out = null;
 
    try {
        out = new PrintWriter(new FileWriter("OutFile.txt"));
    }
    catch (IOException e) {
        System.err.println("Caught IOException: " + e.getMessage());
    }
    catch (Exception e) {
        System.err.println("Caught Exception: " + e.getMessage());
    }
    finally {
        if (out != null) out.close();
    }
}  

 

Any code that wants to take the advantage of exception handling must be put in the try block. When exception is encountered, Java runtime gives up the remaining code in the block and searches through the associated catch blocks for the proper handler. A try block must be accompanied by at least one catch or finally block, and there can be no code between the try block and the first catch block. If runtime cannot find the proper handler, it goes up to the nesting try blocks, if there is one, and search for proper handlers among its catch or finally blocks. If runtime has exhausted all the nesting try blocks and found no handler, the exception is propagated to the caller of the method.

 

Java runtime then traverses up the call stack from the exception-throwing method to locate an appropriate handler for the exception. If runtime has exhausted the call stack and no proper handler has been found, the runtime system, thus the Java program, terminates.

 

The catch statement requires only one argument, the exception type. Exception handler is searched following the listing order of catch statements and based on the matching of exception type. It is a syntax error to list the catch statement whose argument is the base class before the one whose argument is the subclass. If one catch argument matches the type of the exception object, or is the base type of the exception’s type, the associated catch block is executed as the exception handler.

 

Besides trying to deal with the situation, typical actions include getting the detailed description using the getMessage method, printing out the back trace using the printStackTrace method, and simply re-throwing the exception using the throw statement. The lower in the exception class hierarchy, the less exception the catch statement will match, thus more specific. Catch statement shall be as specific as possible, and the more specific one must be listed first. If not sure what exception could be and don’t want to miss any, a general type such as Exception can be used. It is a syntax error to use catch (Exception e) in front of other exception types.

 

The finally block provides the clean up for the above try block and catch block. No matter what happened in the try or catch block, the finally block is always executed. This is especially valuable when one exception did not get caught and passed out of the method, in which case, the finally block can guarantee to prepare (or clean up) something before handing the exception up the call stack.

 

Note that try, catch and finally blocks are different scopes; therefore they cannot refer to the entities defined in other blocks. When catch or finally block is executed, the try block has already gone out of scope.

25. Clean Up Objects

 

When a reference variable goes out of scope or is assigned to null, the reference is removed from the object being referred to. When an object has no more reference associated with it, it is eligible for garbage collection. Garbage collection is running as a low priority thread and periodically frees the memory used by those collectable objects. Garbage collection can be explicitly triggered by calling System.gc() or Runtime.getRuntime().gc() methods. Before garbage collector deleted the objects, it calls the finalize method of the objects, therefore finalize method can be used to do the last minute clean-up work.

26. Object Serialization

 

Object serialization includes writing an object to a stream and then reconstructing the object from the stream. To write an object to a stream, the object must be serializable, that is, its class must implement the Serializable interface.

 

FileOutputStream file_out = new FileOutputStream("Date");
ObjectOutputStream s = new ObjectOutputStream(file_out);
s.writeObject("Today is ");
s.writeObject(new Date());

s.flush();

 

The code above writes a String object and a Date object to the file named “Date”. The code below reconstructs the String and Date objects from the “Date” file. It reads the file, de-serializes the streams and reconstructs the objects in the reverse order.

 

FileInputStream file_in = new FileInputStream("Date");
ObjectInputStream s = new ObjectInputStream(file_in);
String title = (String)s.readObject();
Date today = (Date)s.readObject();

 

Class member variables that are transient or static fields will not be serialized or de-serialized.

27. Collection Framework

 

In Java, a collection object groups a set of other objects, the elements, and manages the organizational structure among the elements. Java framework defines a unified architecture to represent and manipulate collection objects. It includes three major topics: interface, implementation and algorithm.

 

The following picture (a copy from Sun’s online Java Tutorial) illustrates the collection interface hierarchy.

 

 

The following table summarizes the general purpose implementations. All implementations provide unified behaviors: permit null elements, fail graceful under illegal concurrent modification, grow dynamically in size, and are unsynchronized, serializable, and cloneable.

 

 

Hash Function

Resizable

Balanced Tree

Linked

Set

HashSet

(fast indexing, no order, load factor)

 

 

 

List

 

ArrayList

(fast read, slow write except the end, initial capacity)

 

LinkedList

(fast random access)

SortedSet

 

 

TreeSet

(slow indexing, ordered)

 

Map

HashMap

(fast indexing, no order, load factor)

 

 

 

SortedMap

 

 

TreeMap

(slow indexing, ordered)

 

 

Vector is the synchronized counter part of ArrayList, and it is a little faster than ArrayList if synchronization is needed. In a similar way, HashTable is the synchronized counter part of HashMap, and it is a little faster than HashMap if synchronization is needed.

 

Actually Vector and HashTable are implemented using the synchronization wrapper methods.

 

public static Collection synchronizedCollection(Collection c);
public static Set synchronizedSet(Set s);
public static List synchronizedList(List list);
public static Map synchronizedMap(Map m);
public static SortedSet synchronizedSortedSet(SortedSet s);
public static SortedMap synchronizedSortedMap(SortedMap m);

 

For example, Vector is implemented as,

 

List list = Collections.synchronizedList(new ArrayList());

 

HashTable is implemented as,

 

Map m = Collections.synchronizedMap(new HashMap());

 

Class Collection provides a set of common algorithms in the form of static method: sort, shuffle, reverse, max, min, etc. They all take a collection object as the first argument, and provide some computation on the object.


Jerry Zhong, May 2000.