Bristle Software Java Tips

This page is offered as a service of Bristle Software, Inc.  New tips are sent to an associated mailing list when they are posted here.  Please send comments, corrections, any tips you'd like to contribute, or requests to be added to the mailing list, to tips@bristle.com.

Table of Contents:

  1. Differences from C++
  2. Java Versions
    1. Java SE 6.0
  3. Getting Started
    1. Applications
    2. Applets
    3. Servlets
    4. JSP - JavaServer Pages
    5. Java Beans
    6. JDBC - Java Database Connectivity
    7. Javadoc - Generating documentation automatically
    8. EJB - Enterprise Java Beans
  4. Quick Reference
    1. Operators
    2. Literals
    3. Magic Code
    4. JVM Command Line Options
  5. Casts
  6. Overflow
  7. Containers
    1. Arrays vs. Containers
    2. Type-Specific Containers
    3. Inheriting from Containers
    4. Creating hash key classes
    5. Java 1.0 Containers vs Java 2 Containers
  8. Scope of Classes
  9. Multiple Implementation Inheritance
  10. Exceptions
    1. Declaring Exceptions
    2. Catch all exceptions with catch (Throwable t)
    3. Querying Exception Info
    4. Propagating and Re-Throwing Exceptions
  11. RTTI -- Run-Time Type Identification
    1. Original RTTI
    2. Reflection
  12. Interaction with the Host Environment
  13. Web Programming
    1. Reading from a URL
  14. Efficiency
    1. System.currentTimeMillis -- Simplistic Execution Timing
    2. System.arraycopy -- Copying arrays
    3. Arrays.equals -- Comparing arrays
  15. Javadoc
    1. Default Javadoc Behavior
    2. Javadoc Comments
    3. Javadoc Tags
    4. Javadoc Command Line Interface
    5. Javadoc Formatting
    6. Javadoc Example
    7. Bristle Java Library Javadocs
    8. Overriding Javadoc Behavior
  16. JSP - JavaServer Pages
    1. JSP Intro
    2. JSP Directives
    3. JSP Scripting Elements and Other Syntax
    4. JSP/JSTL Actions
    5. JSP/JSTL Implicit Objects
    6. JSP/JSTL EL Syntax
    7. Accessing Properties of Objects in JSP and JSP/JSTL EL
    8. JSP/JSTL EL Convenience Objects
    9. Enabling EL Expression Evaluation
    10. Looping Through Cookies in JSP and JSP/JSTL EL
    11. Looping Through Parameters in JSP and JSP/JSTL EL
    12. Looing Through Headers in JSP and JSP/JSTL EL
    13. Avoiding "code too large" errors
  17. Why Teach Java?
  18. Easing Into Java
  19. See Also

Details of Tips:

  1. Differences from C++

    Last Updated: 9/5/2000
    Applies to:  Java 1.0+

    Here are some of the differences between Java and C++:

    1. Compiler/Interpreter Features

      1. Interpreted, not compiled

        Java is interpreted, not compiled.  Therefore, it is somewhat slower than C++, but since Java interpreters are available for all (almost all?) browsers, operating systems, and other platforms, it is very portable.  The Java "compiler" compiles the Java source code to "bytecodes" which are interpreted at runtime.  Most traditional compile-time errors (type mismatches, undeclared variables, etc.) are caught at compile time.

      2. No preprocessor

        Java has no equivalent of the C/C++ preprocessor.  This has the following ramifications:

        1. No #include

          Java has no #include statement.  You can refer to classes in other files of the same package directly.  You can refer to classes in other packages via their fully qualified names (or use import).  

        2. Class names = file names

          Since Java has no #include, but still needs to find classes in other files, class names must exactly match disk file names (including case), and package names must exactly match disk directory names. 

        3. No #define

          Java has no macros.  Use methods (functions) instead of parameterized macros.  Use final variables for constants.  See "final" vs "const" for more details.

        4. No #if or #ifdef

          Java has no conditional compilation.

      3. No forward declarations needed

        Java uses a 2 pass compiler, which means that it reads the entire file once, then goes back again to resolve references.  In C/C++, you must order your classes and functions in a source file with all called ones before any that call them, or use forward declarations (function prototypes and incomplete class declarations).  This is not necessary in Java.

      4. No header files or function prototypes.

        C/C++ has header files (.H files) that contain class declarations, function prototypes, constants, etc., but not class and function implementations.  These files serve two purposes:  to be read by the compiler, and to be read by the programmer as documentation of the interfaces to the classes and functions.  Java uses no header files.  Classes and members are declared in source files that also contain the implementations.  The Java compiler doesn't need the header files, and the Java programmer uses the javadoc tool or a class browser to extract documentation.

      5. Introspection

        Like all interpreted languages, Java has traditional compile-time info available to it at runtime.  It allows you to query such info programmatically.   For example, at runtime, you can determine the class name of an object, what methods it has, etc.  See Introspection/Reflection for more details.


    2. Data Types

      1. Standardized sizes of numeric types.

        Each numeric data type in Java has a fixed number of bits regardless of the operating system or other platform:

        boolean (Size not specified, but only 2 values supported:  true, false)
        char 16 (Unicode)
        byte 8
        short 16
        int 32
        long 64
        float 32
        double 64
        void N/A

         

      2. No sizeof

        Java has no sizeof operator.  In C/C++, you need sizeof to determine the size of the primitive types, which have standard known sizes in Java.  In C/C++, you also need sizeof to read/write structs and classes to persistent storage.  Java offers "lightweight persistence" through "object serialization", which uses "introspection" internally to determine the sizes of the objects.  More details in the tips below.

      3. No unsigned types

        Java has no unsigned numeric data types (except for char which is an unsigned 16-bit integer).

      4. Explicit boolean data type

        Java has an explicit boolean data type and 2 boolean values: true and false.  It does not allow you to treat integers, pointers, etc. as booleans.  However, you can explicitly cast the integers 1 and 0 to true and false.  Also, Java converts the boolean data type (and all other data types) to string as needed.  Java can catch common C/C++ errors like:
                while (x = y)     // should be while (x == y)
        automatically.

        Note:  As James Higgins points out, C++ has recently added a bool data type, but it is not yet implemented by all compilers, and many places in the language (if, while, the ?: operator, etc.) still tolerate non-bool expressions.

      5. No structs, unions, or typedefs

        Java has no structs, unions, or typedefs.  Everything except the primitive data types is an object -- an instance of a class.

      6. Built-in String data type

        Java has a built-in String class.  It is a full-fledged class, not simply a C/C++ style null-terminated array of characters.  Methods of the String class include: length(), substring(), indexOf(), charAt(), compareTo(), etc.

        Note:  As James Higgins points out, the STL (Standard Template Library) of C++ now includes a standard String class.  However, much C++ code pre-dates the STL and still uses char* or home-grown String classes.

    3. "final" vs "const"

      C++ uses the keyword const for a variety of purposes.  The Java keyword final is similar in some cases, but not all.

      1. const/final variable

        C++ uses const to say that a variable or data member cannot change.  Java uses final for variables and data members of primitive data types.

      2. const/final pointer

        C++ uses const to say that a pointer cannot change.  Java doesn't have pointers, but uses final to say that an object declaration (which holds a reference to an object since objects are always allocated on the heap, not the stack) cannot be changed to refer to a different object instance.

      3. const heap variable

        C++ uses const to say that the storage space pointed to by a pointer cannot be changed by code that accesses the storage space via the pointer.  However, the storage space can still be changed via another pointer.  Also, the const attribute of the pointer can be "cast away" via a cast operation, so that the same pointer can be used to update the storage space. 

        Java offers no such feature.  To achieve a similar effect, you can create a class with access specifiers that prevent its members from being updated.  For a really good discussion of this technique, including how it is used by the standard String and StringBuffer classes of Java, see Appendix A "Passing & Returning Objects" of [Eckel00].

      4. const/final argument

        C++ uses const to say that an argument of a function is not changed by the function.   Java uses final.

      5. const method

        C++ uses const to say that a member function does not change any of the data members of the class.  Java has no equivalent.

      6. final method

        Java uses final to say that a method cannot be overridden in any subclass.  C++ has no equivalent.  In Java, this also allows the method to be early bound (like a non-virtual method in C++).

      7. final class

        Java uses final to say that a class cannot be subclassed.  C++ has no equivalent keyword, though you can accomplish the same thing in C++ (and in Java) by making all constructors of the class private.

      8. blank final

        Java 1.1 allows a variable to be declared final without being initialized at the declaration.  A value must be provided in every constructor.

    4. Memory Management

      1. No explicit pointers

        Java has no explicit pointers.  The data type of every variable is either a primitive (boolean, char, byte, short, ...) or a class.  When the data type is a class, the variable holds a reference to an instance, not an actual instance.

      2. All objects on heap

        Only primitives and object references are stored on the stack.  All objects are dynamically allocated on the heap.  No primitives are on the heap, except as part of a class or array.

      3. Garbage Collection vs. "delete"

        Java has no "delete" function.  The memory for all dynamically allocated heap objects is reclaimed automatically by the garbage collector.  This means you can easily pass arrays, buffers, etc. into and out of functions without worrying about memory management.

      4. C++ "references"

        Unlike C++ "references", Java "references" can be changed at runtime to point to a different object.  However, declaring a Java object variable or parameter (which are both object references) as final prevents this.

    5. Arrays

      1. Array range-checking

        Java does not let you index off the end of an array like C++.

      2. Array length member

        Java arrays have a length member to return the current length of the array.

      3. Array initialization

        Java arrays are automatically initialized to empty values like zero (numeric), false (boolean), etc. when allocated.

      4. Arrays are objects

        Java arrays are objects, not just linear blocks of storage space.  Like all Java object variables, array variables hold a reference (pointer) to the array contents, not the contents themselves.  Therefore, you cannot declare an array to have a specific size (though you can specify the size of an array instance that you assign to the array variable).  Also, you cannot do the C/C++ style tricks of keeping a pointer into the middle of an array.  You can index into an array, but cannot do pointer arithmetic.

    6. Operators

      1. Shift operators are different

        In Java, char, byte, and short variables are promoted to int before being shifted.  Only the 5 low order bits of the right hand operand are used as the shift bit counter (0-31) when shifting int variables (or those promoted to int).   Similarly, only the 6 low order bits of the right hand operand are used as the shift bit counter (0-63) when shifting long variables.

      2. No comma operator

        Java has no equivalent of the C/C++ comma operator.  However, it does explicitly support one of the most common uses of the comma operator -- it allows multiple statements in the initialization and update portions of a for loop, as:
        	for (i = 1, j = 1; i < 10; i++, j++) {...}
      3. String concatenation

        The Java plus operator (+) can be used to concatenate strings, and is internally overloaded to convert other operands to strings as necessary when concatenating.

    7. Statements

      1. No goto

        Java has no goto statement.  Most uses you would have had for it are handled by C/C++ style break, continue, and return, along with the new Java finally clause.

    8. Subroutines/Functions/Methods

      1. Parameters passed by reference

        In C, all parameters are passed by value.  To achieve the effect of pass by reference, you use ampersand (&) to explicitly pass the value of the address of the argument.  The one exception to this is arrays (which includes strings -- arrays of characters) which are passed by reference. 

        In C++, you have an additional option.  You can use ampersand (&) on the function declaration to cause a parameter to be always passed by reference.   Otherwise, parameters are passed as in C.  You can further use const to cause the parameter to be passed by reference but preventing the function from updating the parameter (unless the function "casts away the const-ness").  (Thanks to James Higgins for reminding me of the C++ references.  I must have been sleep-typing when I wrote the first cut at this tip -- left it out completely.)

        In Java, primitives (int, boolean, char, long, etc.) are passed by value, but all objects (including arrays) are passed by reference.

      2. No variable length argument lists

        C/C++ has a "..." syntax to support variable numbers of arguments.  This does not exist in Java.  To accomplish a similar effect, pass an array of Object (the ancestor of all classes) and use RTTI (run-time type identification) inside the method to determine the actual types of the array elements.

      3. No default argument values

        Java does not support default argument values.  You must explicitly pass a value for each argument when calling a method.

      4. Command line arguments

        Similar to C/C++, Java receives command line arguments as elements of an array of strings passed to the main() method.  In C/C++, the 0th element of the array is the name of the program and the 1st element is the first command line argument.  In Java, the 0th element is the first argument.

    9. Scope/Nesting/Access

      1. No global variables or functions

        Java has no global variables or functions.  You can create static members of a global class as a workaround.

      2. No hiding variables via nested blocks

        In C++, you can declare variables of the same name in nested blocks and have the inner one hide the outer one, as:
                {
                    int i = 1;
                    {
                        int i = 2;
                    }
                }

        Java does not allow this.  However, it does allow the following where the inner variable goes out of scope before the outer variable is declared:

                {
                    {
                        int i = 2;
                    }
                    int i = 1;
                }
        
      3. Access specifiers apply to only one declaration each

        Like C++, Java has access specifiers public, private, and protected.  In C++, the specifier applies to all member declarations until the next access specifier or the end of the class.  In Java, the access specifier must be repeated for each member declaration.

      4. Default access to class member

        In addition to the C++ access specifiers public, private, and protected, Java has a default access commonly referred to as "package" access.  Any member without an explicit access specifier is visible to all classes in the same Java package.  Also, for some reason, protected access implies "package" access -- all protected members are available not only to subclasses, but also to all other classes in the package.  There was originally a way to prevent this by specifying private protected as the access specifier, but this has been removed from the language.  Anyone know why?

      5. Packages/Namespaces

        Java uses packages instead of namespaces.  The package hierarchy must exactly match the directory tree (including case).  You should use your domain name in reverse order (for example, use /com/mycompany if your domain name is mycompany.com), as the root of the directory tree containing your classes.

      6. No "::" scope resolution operator

        Java has no "::" scope resolution operator.  Use dot notation for static members, as:
            ClassName.method
            objectName.method

      7. No "virtual" keyword required for dynamic binding

        In C++ all methods use "early binding" by default; you must specify the "virtual" keyword to cause "late binding" (aka "dynamic binding").  In Java, all methods use dynamic binding, except those that cannot be overridden because you specified the "final" keyword.  Thus, unlike C++, there is never any difference between the following calls where ParentClass is the parent of ChildClass:
                ParentClass.method1()
                ChildClass.method1()

        In C++, these 2 calls would invoke different implementations of method1 if it was not defined as a virtual method, and was implemented by both classes.

      8. No operator overloading

        In Java, you can overload methods, but not operators.

    10. Inheritance

      1. All objects inherit from Object

        In Java, all objects inherit from the Object class.  Like Smalltalk and other object-oriented languages, there is a single inheritance tree.  C++ supports a forest of trees, with no enforced single root class.

      2. super keyword

        In C++, to refer to a member of a parent class that has the same name as a member of the current class, you must repeat the name of the parent class as:
                MyParent::member1

        Java uses the keyword super for this as:

                super.member1
        
      3. Calling parent constructors

        In both C++ and Java, each subclass constructor automatically calls the default (parameterless) base class constructor.  In both languages, you can explicitly call a different base class constructor instead (one with parameters) from the subclass constructor.  In C++ you do this with a "constructor initializer" clause as:
                class MySubClass : public MyBaseClass
                {
                    MySubClass (char* s) : MyBaseClass(s)
                    {
                        ...
                    }
                };

        In Java, you call the base class constructor via the super keyword as:

                class MySubClass extends MyBaseClass
                {
                    MySubClass (String s)
                    {
                        super (s);
                        ...
                    }
                }

        The call to super must be the very first thing in the subclass constructor.  It can't even be nested inside a try block to catch exceptions.

      4. Methods in subclass don't hide all same-named methods in base class.

        In C++, if you declare a method A in a subclass, it hides all methods named A in the parent class, regardless of their signatures.  In Java, only the method with the same signature is hidden.

      5. Explicit "abstract" class

        An "abstract" class is a class which has no implementation.  You cannot create instances of an abstract class; only instances of its non-abstract subclasses.   In C++, a class becomes abstract implicitly if it contains at least one "pure virtual" method (a method with "= 0" in place of the method body).  In Java, there is an explicit abstract keyword.  You can explicitly declare a method to be abstract.  You can also explicity declare the entire class to be abstract, which you must do if it contains at least one abstract method.

      6. Bodies of abstract methods

        In Java, as in C++, you can provide a body (implementation) of an abstract method.   Because the method is abstract, all subclasses are forced to override the method.   However, the code in the overriding methods can still explicitly call the abstract method.

      7. Explicit interface classes

        In C++, you can effectively make a class into an interface with no implementation, by making all methods into pure virtual methods and including no non-const data members.   In Java, you can be more explicit, using the interface keyword in place of the class keyword.

      8. No multiple implementation inheritance

        C++ allows a class to inherit the implementation of multiple base classes, as:
                class MySubClass : public MyBaseClass1, 
                                   public MyBaseClass2
                { ... };

        Java allows implementation inheritance from only one base class:

                class MySubClass extends MyBaseClass { ... }

        However, you can accomplish a similar effect via inner classes, as described in Multiple Implementation Inheritance.

      9. Multiple interface inheritance by a class

        In addition to one implementation, Java allows a class to inherit multiple interfaces via the implements keyword, as:
                class MyBaseClass { ... }
                interface Interface1 { ... }
                interface Interface2 { ... }
                interface Interface3 { ... }
                class MySubClass extends MyBaseClass
                                 implements Interface1, 
                                            Interface2, 
                                            Interface3
                { ... }

        In this case, MySubClass automatically inherits the implementation of the methods in MyBaseClass.  However, it inherits only the interfaces of Interface1, Interface2, and Interface3 -- it must explicitly provide an implementation of each method declared in Interface1, Interface2, and Interface3, though these implementations can also be inherited from MyBaseClass.

      10. Multiple interface inheritance by an interface

        Java allows interfaces (but not classes) to inherit multiple interfaces via the extends keyword, as:
                interface Interface1 { ... }
                interface Interface2 { ... }
                interface Interface3 { ... }
                interface MyInterface extends Interface1, 
                                              Interface2, 
                                              Interface3
                { ... }

        In this case, MyInterface is still an interface, and so provides no implementation of any methods.  However, any class that inherits MyInterface is required to provide implementations of all methods from Interface1, Interface2, and Interface3.

      11. No private or protected inheritance

        In C++, you can inherit from a base class in any of 3 modes:  public, private, protected.  The syntax is:
                class MySubClass : public    MyBaseClass { ... }
                class MySubClass : private   MyBaseClass { ... }
                class MySubClass : protected MyBaseClass { ... }

        Java offers only public inheritance.  The syntax is:

                class MySubClass extends MyBaseClass { ... }
      12. Can't restrict visibility via inheritance

        In Java, you cannot restrict the visibility of a class member via inheritance.  In C++, for example, you can define a method as public, but subclasses can restrict it to protected or private.  In Java, this is not allowed.  Public methods and data members of a parent class can be overridden, but must be at least as visible in the child class as in the parent.

        To accomplish this effect in Java, you must use composition and delegation instead of inheritance.  You create an instance of the "parent" class inside of the "child" class, and write stubs for all the methods you want to make visible, with each stub explicitly calling the corresponding method of the "parent" class.

    11. Enums

      C++ allows you to define a set of related constants as an enum (enumerated type).  In Java, use a class containing data members, as:
              public class Colors 
              {
                public final static int RED = 1;
                public final static int GREEN = 2;
                public final static int BLUE = 3;
              }

      Note:  An earlier version of this tip recommended using an interface instead of a class.   This requires less typing, since all data members of an interface are automatically public, final, and static.  However, as Ken Swanson and Alex Blakemore pointed out, you can't declare variables of an interface type.  Therefore, the class approach is better.

    12. Initialization

      1. Guaranteed initialization

        In C/C++, variables are not necessarily initialized to any predictable value.  When you declare a variable on the stack or allocate one from the heap, it's initial value is likely to be whatever was left at that location in memory by the previous variable or code segment that resided there.  It is a common mistake to use an uninitialized value in a computation or as a pointer.  Such mistakes are hard to debug, because they may cause mysterious and intermittent problems in parts of the code that seem unrelated to the uninitialized variable.  For example, if a pointer is not initialized, but happens to contain a value that is the address of another variable, any attempt to increment the variable it was intended to point to will increment the other variable instead, reporting no error.

        In Java, all member variables of classes and all elements of arrays are automatically initialized to empty values like zero (numeric), false (boolean), etc. when allocated.  Local variables (declared inside a method) are not initialized, but any attempt to use an uninitialized variable is caught at runtime.

      2. Data member initialization clauses

        Java allows you to initialize a member variable at the point of its declaration: 
            class MyClass
            {
                int      i   = 0;
                MyObject obj = new MyObject(57);
            }

        C++ does not allow this.  It requires you to initialize the variable with a separate statement in the class constructor, which is typically in a different file from the class declaration.

      3. Data member initialization blocks

        For cases where the initial value is not so simple, Java allows you to embed an explicit block of initialization code among your member variable declarations, as:

            int i = 0;
            int j = 29;
            int k;
            {
                if (i < j) k = j;
                else k = i;
            }

        This code is executed for each instance of the class before the constructor is executed.  C++ does not offer such a feature.  Such code would have to occur in the class constructor.

      4. Static data member initialization clauses

        As with non-static data members, Java allows you to initialize a static member variable at the point of its declaration: 
            static int      i   = 0;
            static MyObject obj = new MyObject(57);

        C++ does not allow this.  It requires you to use a "static member initializer" statement.  Such a statement cannot occur inside the class declaration, or the class constructor or any other method.  It is typically in a separate file with the bodies of the constructors and other methods of the class.   For example, the C++ static data member declarations in the .H file might look like:

            class MyClass
            {
                static int      i;
                static MyObject obj;
            }

        and the separate static member initializer statements in the .CPP file might look like:

            int      MyClass::i   = 0;
            MyObject MyClass::obj = new MyObject(57);
      5. Static data member initialization blocks

        As with non-static data members, Java allows you to embed an explicit block of initialization code among your static member variable declarations, as: 

            static int i = 0;
            static int j = 29;
            static int k;
            static
            {
                if (i < j) k = j;
                else k = i;
            }

        As with the static data member initialization clauses, this code is executed once for the entire class, not once per instance.  It executes before any instance of the class is created and before the static data member is accessed. 

        C++ does not offer such a feature.  Such code would have to occur in a separate function called by the C++ "static member initializer" statement.  Putting such code in the C++ constructor doesn't work because the constructor can be called too often (once per instance), too late (after the static data member is accessed), or not at all (if the static members and methods of a class are used, but no instance is ever created).  The Java technique deals with all of these possibilities.

    13. Inner/nested/local classes

      A Java "inner" class can access the private and protected members of the enclosing class.  This is not true of C++ "nested" classes, which have no special access to the internals of the enclosing class.  Also, C++ nested and "local" classes cannot be anonymous, or declared inside of expressions, like Java inner classes.  See Multiple Implementation Inheritance for an example of an anonymous inner class declared in an expression.

    14. No destructors

      Each C++ class can have a "destructor" method, named with the class name preceded by a tilda ("~").  The destructor of the class (and those of all ancestor classes in reverse order) are executed automatically.  They are executed for an automatic variable (variable declared inside a function or method and allocated on the stack) when the variable goes out of scope, and for a dynamically allocated variable (allocated on the heap via new) when the variable is explicitly deallocated via delete

      Java does not have destructors.  The finalize() method is not a destructor.  It runs when the garbage collector reclaims the space, which may be never.

    15. Exceptions

      1. Exception specifications on methods

        In C++, you can use a "throw" clause on a function to list all exceptions thrown by the function.  Any attempt by a function to throw an exception not listed in its throw clause is caught at runtime.  However, the throw clause is optional -- a function with no throw clause can throw any exception.  In Java, the corresponding throws clause is not optional, and all checking is done at compile time.  (If you need to bypass the checking, throw an exception derived from the Throwable class, but not the Exception class.)

      2. Object types thrown

        In C++, you can throw any type of object (int, char*, class, etc.).  In Java, you can only throw objects derived from the Throwable class.  Therefore, all exception handlers can use the methods of the Throwable class (getMessage(), toString(), printStackTrace(), etc.).  See Querying Exception Info for an example.

    16. "finally" clause

      One of the most useful features of Java is the finally clause.  How many times have you gone to huge amounts of trouble to ensure that all exit paths from a function get funneled through a common stretch of code to clean things up?  The simplest example is in user interface code where you set the mouse pointer to an hourglass, then perform a series of error-prone operations like accessing a database, then change the mouse pointer back to an arrow.  If you have multiple explicit returns from the function, you have to remember to reset the mouse pointer at each one.   Also, you have to catch all exceptions that can occur in the function or any other functions that it calls, resetting the mouse pointer, and then handling or re-raising the error.  Common workarounds involve using goto to jump to a common exit point, or calling a common cleanup routine at each exit point.  Yuck!  A more sophisticated (but still tedious) workaround involves allocating a local object whose sole purpose in life is to perform the cleanup code when it goes out of scope (during its destructor).  Still yuck!

      Java is the first language I've seen that explicitly deals with this problem.  It extends the try ... catch syntax to include a finally clause.  For example: 
          try
          {
              SetMousePointer();
              DoSomeRiskyStuff();
          }
          catch (Exception e)
          {
              DealWithErrors();
          }
          finally
          {
              ResetMousePointer();
          }

      The code in the finally clause is guaranteed to be executed.  If the code in the try block runs to completion, it executes after the code in the try block.  If the try block contains a return statement, it executes just before control is returned to the caller.  If an exception occurs in the try block, it executes after the local handler (if any) that catches the exception.  If an unhandled exception occurs, or a handled exception is re-thrown, it executes just before the exception is propagated to the caller.  It can be used with or without a catch clause, but requires a try block.

    17. Introspection/Reflection

      Java classes are "introspective".  That is, you can call the getClass() method of any class to get an instance of a Class object that contains information about the class.  Via the Class object, you can get the name of the class, name of the parent, name of the enclosing class (if an inner class), lists of constructors, fields, methods, interfaces implemented, the enclosing package, etc.  Thus, it is extraordinarily easy to write your own Java class browser in Java.

      Java 1.1 supports a feature called "reflection", implemented by enhancements to the Class object and the addition of the Java 1.1 java.reflect package.  Rather than just getting the items listed above as strings, you can get them as objects of type Class, Constructor, Field, Method, Package, etc.  Then you can use the get() and set() methods of Field to access the fields of the class, use the invoke() method of Method to invoke the methods, etc. 

      You can also use the forName() method of the Class object to get the Class object of a class if you have only its name, not an instance.  Thus, you don't need an instance of a class to get started.  Finally, you can call the newInstance() method to create an instance of a class from its corresponding Class object.  Thus it is also very easy to write a Java class loader in Java.

    18. No templates (yet!)

      Java doesn't have templates, though there are efforts underway to add them to the language.

    19. Multi-threading support
      1. Thread class
      2. synchronized keyword
      3. volatile keyword

    20. Object Serialization

    21. Lightweight persistence

    22. Javadoc

    23. Coding conventions
      1. Package names lowercase
      2. Class names mixed case
      3. Member names mixed case w/initial lowercase
      4. Constants (static final variables initialized with constant values at compile time) in all uppercase.
      5. See http://java.sun.com/docs/codeconv/index.html

    24. Different class library

      Java and C++ have entirely different class libraries.
      1. Different I/O routines
      2. Tokenizing support for simplistic parsing of strings and files.
      3. Compression support (ZIP, GZIP, etc.)
    25. --Fred

  2. Java Versions

    1. Java SE 6.0

      Last Updated: 2/22/2007
      Applies to:  Java 6.0+

      Java SE 6.0 was released in December 2006.  For a summary of new features, see:

          http://java.sun.com/developer/technicalArticles/J2SE/Desktop/javase6/beta2.html
          An article by Danny Coward, the lead developer.

          http://java.sun.com/javase/6/features.jsp
          The key features list at the Sun Web site.

          http://jcp.org/aboutJava/communityprocess/final/jsr270/index.html
          The official specification of the final release contents.

      --Fred

  3. Getting Started

    1. Applications

      Last Updated: 10/13/2000
      Applies to:  Java 1.0+

      Here is a simple Java console application:

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

      It must reside in a file named exactly App1.java (case sensitive) to match the class name.  It can be compiled (using the Sun JDK compiler, available for free download at http://java.sun.com) as:

      	javac App1.java

      and invoked from the command line as:

      	java App1

      Like C/C++ programs, Java console applications always begin with the main() method.  They can accept command line arguments, read from standard input, write to standard output, return a status code to the command line processor, read/write files, etc.

      --Fred

    2. Applets

      Original Version: 10/13/2000
      Last Updated: 2/5/2006
      Applies to:  Java 1.0+

      A Java applet is a Java class that runs in the context of an applet container (typically a Web Browser).  Here is a simple Java applet:

      	import java.applet.Applet;
      	import java.awt.Graphics;
      	public class Applet1 extends Applet 
      	{
      	    public void paint(Graphics g)
      	    {
      	        g.drawString("Hello world!", 50, 25);
      	    }
      	}

      It must be a subclass of Applet and must reside in a file named exactly Applet1.java (case sensitive) to match the class name.  It can be compiled (using the Sun JDK compiler, available for free download at http://java.sun.com) as:

      	javac Applet1.java

      It can't be invoked directly from the command line because it has no main() method.

      Java applets can contain several methods that are invoked at various times by the applet container.  This example contains only a paint() method, but a more typical applet might contain the following:

      init() Called to initialize the applet.
      start() Called when the applet becomes visible (a good place to start an animation).
      stop() Called when the applet moves out of sight of the user (may as well stop any animation here to save CPU time).
      destroy() Called when the applet is unloaded from the container.
      paint() Called when the applet should re-draw itself.

      Typically applets are run in the context of a Web Browser, but the Sun JDK includes a tool (a simple applet container) called appletviewer that can also be used to run an applet.  In either case, you run them as part of an HTML page, like the one below:

      	<html>
      	 <body>
      	  Here is the output of my program:
      	  <applet code="Applet1.class" width="150" height="25">
      	  </applet>
      	 </body>
      	</html>

      To run the applet, load this HTML page into a Web Browser.  To run it via appletviewer, specify the name of the HTML file as the command line argument, as:

      	appletviewer Page1.html

      There are lots of restrictions on applets, mostly due to security concerns.  Applets typically reside on a Web server, but are downloaded automatically as part of the Web page, and executed by the Web Browser.   Therefore, they are not permitted to do things that might be dangerous to the computer running the Web Browser, like reading/writing/deleting files, etc.  However, you can get around these restrictions with signed, trusted applets.

      --Fred

    3. Servlets

      Original Version:  10/14/2000
      Last Updated: 2/5/2006
      Applies to:  Java 1.1+

      A Java servlet is a Java class that runs in the context of a servlet container (typically a Web Server).  Here is a simple Java servlet:

      	import java.io.*;
      	import javax.servlet.*;
      	import javax.servlet.http.*;
      	public class Servlet1 extends HttpServlet
      	{
      	    public void doGet(HttpServletRequest request,
      	                      HttpServletResponse response)
      	        throws IOException, ServletException
      	    {
      	        response.setContentType("text/html");
      	        PrintWriter out = response.getWriter();
      	
      	        out.println("<html>");
      	        out.println(" <body>");
      	        out.println("  <h1>Hello World!</h1>");
      	        out.println(" </body>");
      	        out.println("</html>");
      	    }
      	}

      It must implement the Servlet interface (typically by being a subclass of HttpServlet) and must reside in a file named exactly Servlet1.java (case sensitive) to match the class name.

      To compile the servlet, you must have access to a Java compiler (the Sun JDK compiler is available for free download at http://java.sun.com) and the standard Java servlet classes (javax.servlet.* used in the import statements above).  If your Web Server supports servlets (most do) it should include a .JAR or .ZIP file that contains these classes.   Otherwise, you can download "Tomcat", the official reference implementation of a servlet container for free from http://jakarta.apache.org/tomcat.

      Tomcat is easy to download, unzip, and run.  No installation required.  Its servlet.jar file contains the servlet classes.  You can compile the servlet (using the Sun SDK Java compiler and the Tomcat servlet.jar file) as:

      	javac -classpath c:\tomcat\lib\servlet.jar Servlet1.java

      The servlet can't be invoked directly from the command line because it has no main() method.  Before you run it as a servlet, you must register it with a servlet container.  With Tomcat, there are configuration files you can edit to register locations of servlets, but the easiest way to get started is to simply copy the Servlet1.class file (created by the compile command above) into an existing subdirectory of the Tomcat tree.   For example, copy it to:

      	c:\tomcat\webapps\examples\WEB-INF\classes

      Once the servlet is registered, start Tomcat by running the "startup.bat" (Windows) or "startup.sh" (Unix) file.   You can then run the servlet by loading the following URL into your Web Browser:

      	http://localhost:8080/examples/servlet/Servlet1

      Note that, by default, the Tomcat server listens on port 8080. You can change this by editing the server.xml configuration file.

      Java servlets can contain several methods that are invoked at various times by the servlet container.  This example contains only a doGet() method, but the following are also defined:

      init() Called to initialize the servlet.
      doGet() Called to handle all GET requests from an HTTP client.
      doPost() Called to handle all POST requests from an HTTP client.
      service() Called to handle all requests from an HTTP client.
      destroy() Called when the servlet is unloaded from the container.

      This servlet simply writes a hardcoded stream of HTML to the Web Browser.  A more typical servlet would accept parameters (passed as part of the URL and accessed via the request parameter to the servlet's method), access a relational database or other data source, perform some computation, perhaps read and write files on the Web Server, and write a stream or HTML or XML to the Web Browser.

      For more information, see the O'Reilly Java Servlet Programming book.

      --Fred

    4. JSP - JavaServer Pages

      Original Version: 10/14/2000
      Last Updated: 12/12/2006
      Applies to:  Java 1.1+

      JavaServer Pages (JSPs) are similar to Microsoft "Active Server Pages" (ASPs).  They simplify the job of writing a servlet when the primary purpose of the servlet is to generate an HTML page.  Such a servlet would otherwise consist of a sequence of println() calls to print out the HTML, intermixed with occasional other Java statements.  The bulk of the file would be HTML, but all of the HTML would be wrapped in double quotes to make it into Java string literals, making it more difficult to use quotes in the HTML itself.  For example, the following is a valid Java servlet:

      	import java.io.*;
      	import javax.servlet.*;
      	import javax.servlet.http.*;
      	public class Servlet1 extends HttpServlet
      	{
      	    public void doGet(HttpServletRequest request,
      	                      HttpServletResponse response)
      	        throws IOException, ServletException
      	    {
      	        response.setContentType("text/html");
      	        PrintWriter out = response.getWriter();
      	
      	        out.println("<html>");
      	        out.println(" <body>");
              	out.println("  <p><font color=\"red\">" + 
      			    "The Web Server is running Java version " +
                          	    System.getProperty("java.version") +
      			    "</font></p>");
      	        out.println(" </body>");
      	        out.println("</html>");
      	    }
      	}

      The corresponding JSP page consists of a sequence of HTML with occasional embedded Java code:

      	<html>
      	 <body>
      	  <p><font color="red">" 
      	  The Web Server is running Java 
                version <%= System.getProperty("java.version") %>
      	  </font></p>
      	 </body>
      	</html>

      The <% and %> delimiters are used to enclose the Java code.  The rest of the lines are wrapped automatically in Java println() calls as shown in the servlet above.   When a client requests a JSP page, the server automatically calls the JSP compiler to translate the JSP page into a servlet source file, then calls the Java compiler to compile it, then executes the servlet.  Unnecessary steps are skipped based on the last modification date/time of the JSP file, .java file and .class file.

      By default, all of the JSP code ends up in the service() method of the generated servlet.  However, you can exercise more control over the generated code via JSP directives and JSP declarations.

      The above describes the use of "scriptlets" in JSP pages, which is how many JSP programmers get started. However, the real power of JSP is the use of custom tags, like the Struts tag library, the JSTL tag library, JavaServer Faces libraries, and even your own homegrown tag libraries.

      For more info, see:

          JSP - JavaServer Pages

      --Fred

    5. Java Beans

      Original Version: 2/5/2006
      Last Updated: 5/19/2007
      Applies to:  Java 1.0+

      Unlike Java applets and Java servlets, Java beans are not required to implement any particular interface or inherit from any particular class.  Being a Java bean is accomplished by adhering to a design pattern, a coding convention.

      The basic requirement is that Java beans have a public "default constructor" (a constructor with no parameters) and make their properties available via pairs of "accessor" methods known as "getters" and "setters" using the prefixes:

          "set" for setters
         
      "get" for non-boolean getters
          "is" for boolean getters

      For example, the following is a valid (but not very useful) Java bean:

      	public class BeanWithNoProperties
      	{
      	}

      The following is a simple contrived example that is slightly more useful:

      	public class Rectangle
      	{
      	    private Integer width;
      	    private Integer height;
      	    private String  name;
      
      	    public void setWidth(Integer value)   { width = value; }
      	    public Integer getWidth()             { return width; }
      
      	    public Integer getHeight()            { return height; }
              
      	    public void setSquare(boolean square) { height = (square ? width : null); }
      	    public boolean isSquare()             { return width == height; }
              
      	    public void setName(String newname)   { name = newname; }
      	}

      Notes:

      1. Public constructor exists by default.
      2. Standard getters and/or setters are defined for zero or more of the properties.
      3. Internal property values are declared "private" to prevent bypassing the accessor methods.
      4. Each property may have a getter, a setter, both or neither.
      5. Getters and setters may compute values instead of mapping directly to internal variables.
      6. Names of the arguments for getters and setters are irrelevant.
      7. Setters must return "void".
      8. Getters must take no arguments.

      Common uses of Java beans are:

      1. As UI components, where a drag/drop UI builder uses "reflection" to determine what properties are defined for a bean so that it can offer them to the user in a property sheet.
      2. As simple data objects that are pluggable into a wide variety of frameworks.  For example, "backing beans" for JavaServer Faces applications.
      3. As "Enterprise Java Beans" (EJBs), which use the basic Java bean interface, but have additional requirements in order to support features for remote access, concurrency, scalability, data access, etc.

      Other features/requirements of Java beans are:

      1. Must be persistable which the above is by default.  Technically it should implement one of the interfaces:
                java.io.Serializable
                java.io.Externalizable
        but neither of these requires any methods, and many applications requiring Java beans do not require them.
      2. Must support "introspection" which all Java objects do via "reflection", but a bean can choose to explicitly use the BeanInfo class instead.
      3. Can register with other beans of interest as a "listener" to be informed of "events" that occur to those beans.
      4. Can support custom "property editors" for use in property sheets of an enclosing application.

      For more info, see:

      http://en.wikibooks.org /wiki /Computer_programming /Component_based_software_development #The_JavaBeans_Component_Model

       

      --Fred

    6. JDBC - Java Database Connectivity

      Original Version: 2/5/2006
      Last Updated: 5/19/2007
      Applies to:  Java 1.0+

      JDBC (Java Database Connectivity) is the Java equivalent of ODBC.  It provides access to a relational database.  Here is a simple Java application using JDBC:

      import java.sql.*;
      public class JDBCSample
      {
          public static void main(String[] args)
                  throws ClassNotFoundException, SQLException
          {
              String url  = "jdbc:oracle:thin:@my_server.com:1521:DBSID";
              String user = "my_username";
              String pw   = "my_password";
              Class.forName("oracle.jdbc.driver.OracleDriver");
              Connection conn = DriverManager.getConnection(url, user, pw);
              Statement  st   = conn.createStatement();
              ResultSet  rs   = st.executeQuery("select * from table1");
              while (rs.next())
              {
                  String col1 = rs.getString(1);
                  int    col2 = rs.getInt(2);
              }
              rs.close();
              st.close();
              conn.close();
          }
      }

      You can compile it like any Java application, using any Java compiler.  The java.sql classes are included in the standard runtime library.  

      To run it, you must have access to a JDBC driver.  In this case, you'll need the Oracle JDBC driver, which is available for free download at http://www.oracle.com /technology /software /tech /java /sqlj_jdbc /index.html).  It's a simple Java JAR file (though older versions were JAR files named with a .ZIP extension), so just copy it to your Java classpath.  

      Of course, you'll also need access to a relational database (in this case Oracle), running at the specified host ("my_server.com"), listening on the specified port (1521), and named with the specified database SID ("DBSID").  And it should have a table named "table1".

      This example is pretty simple.  It just selects from a single table and examines the String and int values of the first 2 columns in the resultset.  

      However, you can get fancier, using the typical ODBC syntax to call a procedure or function with input and output "bind variables":

              CallableStatement st = conn.prepareCall
      		("{? = call my_function(?, ?, ?)}");
      	st.registerOutParameter(1, Types.VARCHAR); 
      	st.setInt              (2, 57); 
      	st.setString           (3, "a string value"); 
      	st.registerOutParameter(4, Types.INTEGER); 
              st.execute();
      	String returnVal = st.getString(1);
      	int    outParam  = st.getInt(4);

      You can also:

      1. Call conn.setAutoCommit(false), and explicitly call conn.commit() or conn.rollback().
      2. Prepare statements (for faster repeated execution) via conn.prepareStatement().
      3. Call st.registerOutParameter(n, oracle.jdbc.driver.OracleTypes.CURSOR) to return resultsets as output params or function return values. 
      4. Specify resultSetType values on the call to createStatement(), prepareCall(), or prepareStatement():

        ResultSet.TYPE_FORWARD_ONLY
        ResultSet.TYPE_SCROLL_INSENSITIVE
        ResultSet.TYPE_SCROLL_SENSITIVE

      5. Specify resultSetConcurrency values on the call to createStatement(), prepareCall(), or prepareStatement():

        ResultSet.CONCUR_READ_ONLY
        ResultSet.CONCUR_UPDATABLE

      6. Get connections from a connection pool, instead of creating a new one for each query.
      7. etc.

      Thanks to John Moore for pointing out a typo in this tip!

      --Fred

    7. Javadoc - Generating documentation automatically

      Last Updated: 11/23/2003
      Applies to:  Java 1.0+

      The Java JDK (Java Developer's Kit) from Sun includes a standard tool called "javadoc".  It scans your Java code and automatically produces API documentation as a set of linked Web pages.  To run it against a bunch of Java source files, simply type:

              javadoc *.java

      For a typical set of such documentation, see:

              http://bristle.com/Tips/javadocs/index.html

      For more info, see:

              Javadoc

      --Fred

    8. EJB - Enterprise Java Beans

      Last Updated: 10/13/2000
      Applies to:  Java 1.0+

      Coming soon...

      --Fred

  4. Quick Reference

    1. Operators

      Last Updated: 5/21/2000
      Applies to:  Java 1.0+

      = assignment, shallow copy for objects
         
      == comparison
      Shallow compare for objects.  Use equals() for deep compare.
      != comparison
      Shallow compare for objects.  Use equals() for deep compare.
      <  
      <=  
      >  
      >=  
         
      +, += unary and binary
      -, -= unary and binary
      *, *=  
      /, /=  
      %, %= modulus
         
      ++ prefix and postfix
      -- prefix and postfix
         
      && Logical AND
      Short circuit like C++
      || Logical OR
      Short circuit like C++
      ! Logical NOT
         
      &, &= Bitwise AND
      |, |= Bitwise OR
      ~ Bitwise NOT
      ^, ^= Bitwise XOR
         
      <<, <<= Left shift
      >>, >>= Right arithmetic shift (sign extended)
      >>>, >>>= Right logical shift (zero extended)
         
      ? : Ternary if-then-else
         
      , Used only in for loop

      --Fred

    2. Literals

      Last Updated: 11/13/2000
      Applies to:  Java 1.0+

      123 decimal int (32-bit)
      123.4 decimal double (64-bit)
      0177 octal int (32-bit)
      0x1FF, 0X1FF, 0x1ff, 0X1ff hex int (32-bit)
      123L, 123l long (64-bit)
      123F, 123f float (32-bit)
      123D, 123d double (64-bit)
      1.23e-9 scientific notation

      Thanks to John Haibach for pointing out some additions to the original version of this tip!

      --Fred

    3. Magic Code

      Last Updated: 7/17/2000
      Applies to:  Java 1.0+

      Java executes your code magically (without an apparent call to the code) in the following situations:

      constructor Class constructor called whenever a class is created via new().
      Note, however, that there are no destructors.
      parent constructor Default constructor of parent class called automatically from the constructor of a child class, unless the child constructor explicitly calls super() as the first line of code.
      finalize() finalize() method called by garbage collector before reclaiming the storage used for the class.
      finally clause Statements in the finally clause of a block are run when control passes out of the block.
      toString() toString() method called when a class instance is mentioned in a context that requires a string.

      --Fred

    4. JVM Command Line Options

      Last Updated: 1/27/2006
      Applies to:  Java 1.4.2+

      To see the command line options supported by the Java Virtual Machine (java.exe), type:

          java -?
          java -X

      For Java 1.4.2, the output is of java -? is:

      Usage: java [-options] class [args...]
                 (to execute a class)
         or  java [-options] -jar jarfile [args...]
                 (to execute a jar file)
      
      where options include:
          -client       to select the "client" VM
          -server       to select the "server" VM
          -hotspot      is a synonym for the "client" VM  [deprecated]
                        The default VM is client.
      
          -cp <class search path of directories and zip/jar files>
          -classpath <class search path of directories and zip/jar files>
                        A ; separated list of directories, JAR archives,
                        and ZIP archives to search for class files.
          -D<name>=<value>
                        set a system property
          -verbose[:class|gc|jni]
                        enable verbose output
          -version      print product version and exit
          -version:<value>
                        require the specified version to run
          -showversion  print product version and continue
          -jre-restrict-search | -jre-no-restrict-search
                        include/exclude user private JREs in the version search
          -? -help      print this help message
          -X            print help on non-standard options
          -ea[:<packagename>...|:<classname>]
          -enableassertions[:<packagename>...|:<classname>]
                        enable assertions
          -da[:<packagename>...|:<classname>]
          -disableassertions[:<packagename>...|:<classname>]
                        disable assertions
          -esa | -enablesystemassertions
                        enable system assertions
          -dsa | -disablesystemassertions
                        disable system assertions

      and the output is of java -X is:

          -Xmixed           mixed mode execution (default)
          -Xint             interpreted mode execution only
          -Xbootclasspath:<directories and zip/jar files separated by ;>
                            set search path for bootstrap classes and resources
          -Xbootclasspath/a:<directories and zip/jar files separated by ;>
                            append to end of bootstrap class path
          -Xbootclasspath/p:<directories and zip/jar files separated by ;>
                            prepend in front of bootstrap class path
          -Xnoclassgc       disable class garbage collection
          -Xincgc           enable incremental garbage collection
          -Xloggc:<file>    log GC status to a file with time stamps
          -Xbatch           disable background compilation
          -Xms<size>        set initial Java heap size
          -Xmx<size>        set maximum Java heap size
          -Xss<size>        set java thread stack size
          -Xprof            output cpu profiling data
          -Xrunhprof[:help]|[:<option>=<value>, ...]
                            perform JVMPI heap, cpu, or monitor profiling
          -Xdebug           enable remote debugging
          -Xfuture          enable strictest checks, anticipating future default
          -Xrs              reduce use of OS signals by Java/VM (see documentation)
          -Xcheck:jni       perform additional checks for JNI functions
      
      The -X options are non-standard and subject to change without notice.

      Thanks to Michel van der List for pointing out that there is an incredibly extensive list of options, for various versions of the JVM, at:

          http://blogs.sun.com/roller/resources/watt/jvm-options-list.html

      Thanks also to James Higgins for pointing out that Sun has excellent technical documents on using these options to tune the performance of JVM garbage collection, for various versions of the JVM, at:

          http://java.sun.com/docs/hotspot

      --Fred

  5. Casts

    Last Updated: 6/5/2000
    Applies to:  Java 1.0+

    Casts can be done between any of the primitive types except boolean (byte, char, short, int, long, float, double).  Casting to a wider data type can be done implicitly and preserves the signed value (sign-extending the bit pattern for integral types).   Casting to a narrower date must be done explicitly and discards the high order bits.  For example:

    	int int1 = -1;			// -1 (FFFF FFFF hex)
    	long long1 = (long)int1;	// -1 (sign extended to FFFF FFFF FFFF FFFF)
    	int1 = (int)long1;		// FFFF FFFF (low order bits -- happens to be -1)
    	long1 = 0x300000000L;	 	// 3 0000 0000
    	int1 = (int)long1;		// 0 (low order bits = 0000 0000)
    	long1 = 0x00000000FFFFFFFFL;	// 0000 0000 FFFF FFFF
    	int1 = (int)long1;		// -1 (low order bits = FFFF FFFF)

    Casting from a float or double to an integral type discards the fractional portion of the value.

    --Fred

  6. Overflow

    Last Updated: 6/5/2000
    Applies to:  Java 1.0+

    Java does not treat numeric overfow as an error.  For example:

    	int int1 = 0x7FFFFFFF;
    	System.out.println(int1);	// 2147483647
    	int1 *= 4;
    	System.out.println(int1);	// -4

    --Fred

  7. Containers

    1. Arrays vs. Containers

      Last Updated: 6/25/2000
      Applies to:  Java 1.0+

      Here are some of the differences between arrays and containers:

      1. Fixed size

        Arrays are fixed size.  Containers are dynamic.

      2. Compile-time type checking

        Arrays can be declared to hold items of a specific type and the compiler will check this.   Containers all hold Object, so there is no compile-time checking.

      3. Holding primitive data types

        Arrays can be declared to hold a primitive data type (int, long, float, etc.).   Containers only hold objects (though they can hold the wrapper classes for the primitive data types (Integer, Long, Float, etc.)).

      4. Efficiency

        Arrays are more efficient in both space and time.

      5. Access algorithms

        Containers offer a wider variety of orders (FIFO, LIFO, etc.) and access algorithms (hashing, etc.).  Arrays are simply indexed by integers.

      6. Semantics

        Some containers enforce semantic rules.  For example, a Set represents a mathematical set, where no duplicates are allowed.

      --Fred

    2. Type-Specific Containers

      Last Updated: 7/7/2000
      Applies to:  Java 1.0+

      You can create a type-specific container.  Create a class that contains a member of a standard container type (ArrayList, HashSet, etc).  Offer the standard methods (add, remove, clear, get, size, etc.), but declare the method parameters as the specific type of object you wish to contain.  From each of your methods, call the corresponding method of the standard container.  VB programmers may recognize this technique as a "collection class".

      Note 1:  There is an advantage to encapsulating the standard container like this, rather than inheriting from it.  With inheritance, your type-specific methods would overload (but not hide) the standard methods (which take Object parameters).  Therefore, it would still be possible for the user of your class to add any object to the container.  By encapsulating the standard container, you hide its methods from your user.

      Note 2:  If you add an implements clause to your type-specific container, so that it officially implements one of the standard container interfaces (List, Set, etc.), your users can use your class wherever a standard container class is expected.  However, this forces you to add more methods to your class, specifically the ones that you avoided by not using inheritance -- the ones that take Object as parameters and make it possible for your users to add any object to the container.

      --Fred

    3. Inheriting from Containers

      Last Updated: 7/7/2000
      Applies to:  Java 2+

      When using inheritance to create your own containers, use inherits (implementation inheritance) with the standard abstract classes (AbstractList, AbstractSet, AbstractMap, etc.) rather than using implements (interface inheritance) with the standard interfaces (List, Set, Map, etc.).  This allows you to inherit the implementation of much of the boilerplate code, rather than having to write it all yourself.

      --Fred

    4. Creating hash key classes

      Last Updated: 7/7/2000
      Applies to:  Java 2+

      When using creating a class to be used as a key in a HashMap or HashSet, be sure to override both hashCode() and equals().  Override hashCode() so you can use the "value" of the class (whatever you decide that means) as the hash value, not the address of the class which the default Object.hashCode() method would return.  Override equals() so you can compare the value of the class with other keys, rather than comparing the address via Object.equals().

      --Fred

    5. Java 1.0 Containers vs Java 2 Containers

      Last Updated: 7/7/2000
      Applies to:  Java 1.0+

      Java 1.0 included a standard library of containers (Vector, Stack, HashTable, etc.).   In Java 2, these are still supported, but deprecated in favor of a new container library (ArrayList, HashSet, HashMap, etc.). Here are some notes about the old and new libraries:

      1. Use the following Java 2 containers instead of the corresponding Java 1.0 containers:

        Java 1.0 Java 2
        Vector ArrayList
        Stack LinkedList
        HashTable HashMap
      2. Vector weaknesses:

        1. Final methods

          Some of the Vector methods were final and could not be overridden, making inheritance difficult.

      3. Stack weaknesses:

        1. Inherited from Vector

          Since Stack inherited from Vector, all of the vector methods were available.   Therefore, you could violate the "stack" abstraction, accessing items that were not at the top of the stack.

      --Fred

  8. Scope of Classes

    Last Updated: 6/16/2000
    Applies to:  Java 1.0+

    Java classes in a package can have public or "package" access, but not protected or private.  This makes sense since they are nested inside a package, not another class.  However, you can also nest a class inside another class, in which case you can make it protected or private.  Such a class is called an "inner class".  You can also declare an inner class inside a method or inside an unnamed block of code.  You can also declare an anonymous inner class (a class with no name) in an expression, inheriting from a named class.  An inner class can be static, in which case an instance of it can exist without requiring an instance of the enclosing class.  A static inner class can be nested inside an interface.

    --Fred

  9. Multiple Implementation Inheritance

    Last Updated: 6/25/2000
    Applies to:  Java 1.0+

    Unlike C++, Java doesn't directly support multiple implementation inheritance. However, with a little work, you can accomplish the same effect -- inheriting the implementation of multiple base classes with the ability for the subclass to selectively override methods of each base class.

    Approach 0: The C++ approach.

    The C++ approach (expressed in Java syntax) would look like:

            class Base1
            {
                private String myName = "Base1";
                public void method1a () 
                    { System.out.println ("method1a of " + myName); }
                public void method1b () 
                    { System.out.println ("method1b of " + myName); }
            }
            class Base2
            {
                private String myName = "Base2";
                public void method2a () 
                    { System.out.println ("method2a of " + myName); }
                public void method2b () 
                    { System.out.println ("method2b of " + myName); }
            }
           
            class SubClass extends Base1, Base2  // *** Illegal!
            {
                private String myName = "SubClass";
                public void method1b ()
                    { System.out.println ("overridden method1b of " + myName); }
                public void method2b ()
                    { System.out.println ("overridden method2b of " + myName); }
            } 
           
            public class MI
            {
                static void call1a(Base1 base1) { base1.method1a (); }
                static void call1b(Base1 base1) { base1.method1b (); }
                static void call2a(Base2 base2) { base2.method2a (); }
                static void call2b(Base2 base2) { base2.method2b (); }
                public static void main(String[] args) 
                {
                    SubClass s = new SubClass();
                    call1a(s);
                    call1b(s);
                    call2a(s);
                    call2b(s);
                }
            }

    However, this is not supported in Java. You can't put 2 class names after the keyword "extends".

    Approach 1: Interface Inheritance

    The standard Java approach is to use interfaces for any additional base classes beyond the first one, as:

            class Base1
            {
                private String myName = "Base1";
                public void method1a () 
                    { System.out.println ("method1a of " + myName); }
                public void method1b () 
                    { System.out.println ("method1b of " + myName); }
            }
            interface Base2
            {
                String myName = "Base2";
                public void method2a ();
                public void method2b ();
            }
           
            class SubClass extends Base1 
                           implements Base2
            {
                private String myName = "SubClass";
                public void method1b ()
                    { System.out.println ("overridden method1b of " + myName); }
                public void method2a () 
                    { System.out.println ("required method2a of " + myName); }
                public void method2b () 
                    { System.out.println ("required method2b of " + myName); }
            } 
           
            public class MI
            {
                static void call1a(Base1 base1) { base1.method1a (); }
                static void call1b(Base1 base1) { base1.method1b (); }
                static void call2a(Base2 base2) { base2.method2a (); }
                static void call2b(Base2 base2) { base2.method2b (); }
                public static void main(String[] args) 
                {
                    SubClass s = new SubClass();
                    call1a(s);
                    call1b(s);
                    call2a(s);
                    call2b(s);
                }
            }

    However, this requires SubClass to provide the complete implementation of Base2. All it inherits from Base2 is an interface -- a requirement to implement the methods itself, not an existing implementation of the methods.

    Approach 2: Composition

    Another alternative is to use composition instead of inheritance, as:

            class Base1
            {
                private String myName = "Base1";
                public void method1a () 
                    { System.out.println ("method1a of " + myName); }
                public void method1b () 
                    { System.out.println ("method1b of " + myName); }
            }
            class Base2
            {
                private String myName = "Base2";
                public void method2a () 
                    { System.out.println ("method2a of " + myName); }
                public void method2b () 
                    { System.out.println ("method2b of " + myName); }
            }
           
            class SubClass extends Base1 
            {
                private String myName = "SubClass";
                public void method1b ()
                    { System.out.println ("overridden method1b of " + myName); }
                public Base2 base2 = new Base2();
                public Base2 getBase2() { return base2; }
            } 
           
            public class MI
            {
                static void call1a(Base1 base1) { base1.method1a (); }
                static void call1b(Base1 base1) { base1.method1b (); }
                static void call2a(Base2 base2) { base2.method2a (); }
                static void call2b(Base2 base2) { base2.method2b (); }
                public static void main(String[] args) 
                {
                    SubClass s = new SubClass();
                    call1a(s);
                    call1b(s);
                    call2a(s.getBase2());
                    call2b(s.getBase2());
                }
            }

    This allows SubClass to use the implementation of both base classes. However, it has lost the ability to override the behavior of any of the methods of Base2. The advantage is that it is not required to implement all of the Base2 methods, but the disadvantage is that it is not permitted to implement any of them. It can only offer the default Base2 behavior to its clients.  Also, the code using the subclass must "cast" the subclass to the right type by calling the getBase2 method when using SubClass as a Base2, but not when using it as a Base1.

    Approach 3: Inner Classes

    A better alternative is to use inner classes, as:

            class Base1
            {
                private String myName = "Base1";
                public void method1a () 
                    { System.out.println ("method1a of " + myName); }
                public void method1b () 
                    { System.out.println ("method1b of " + myName); }
            }
            class Base2
            {
                private String myName = "Base2";
                public void method2a () 
                    { System.out.println ("method2a of " + myName); }
                public void method2b () 
                    { System.out.println ("method2b of " + myName); }
            }
            
            class SubClass extends Base1 
            {
                private String myName = "SubClass";
                public void method1b ()
                    { System.out.println ("overridden method1b of " + myName); }
                public class Inner2 extends Base2
                {
                    public void method2b ()
                    {
                        System.out.println ("overridden method2b of " + myName);
                    }
                }
                public Base2 getBase2() { return new Inner2(); }
            } 
            
            public class MI
            {
                static void call1a(Base1 base1) { base1.method1a (); }
                static void call1b(Base1 base1) { base1.method1b (); }
                static void call2a(Base2 base2) { base2.method2a (); }
                static void call2b(Base2 base2) { base2.method2b (); }
                public static void main(String[] args) 
                {
                    SubClass s = new SubClass();
                    call1a(s);
                    call1b(s);
                    call2a(s.getBase2());
                    call2b(s.getBase2());
                }
            }

    This allows SubClass to use the implementation of both base classes, and to selectively override the behavior of any of the methods of either base class. 

    Note:  The code using the subclass must still "cast" the subclass to the right type by calling the getBase2 method.  This seems to be unavoidable.

    Approach 4: Anonymous Inner Classes

    A slightly shorter, but stranger-looking alternative is to make the inner class anonymous, as:

            class Base1
            {
                private String myName = "Base1";
                public void method1a () 
                    { System.out.println ("method1a of " + myName); }
                public void method1b () 
                    { System.out.println ("method1b of " + myName); }
            }
            class Base2
            {
                private String myName = "Base2";
                public void method2a () 
                    { System.out.println ("method2a of " + myName); }
                public void method2b () 
                    { System.out.println ("method2b of " + myName); }
            }
            
            class SubClass extends Base1 
            {
                private String myName = "SubClass";
                public void method1b ()
                    { System.out.println ("overridden method1b of " + myName); }
                public Base2 getBase2() 
                {
                    return new Base2() 
                    {
                        public void method2b ()
                        {
                            System.out.println ("overridden method2b of " + myName);
                        }
                    };
                }
            } 
            
            public class MI
            {
                static void call1a(Base1 base1) { base1.method1a (); }
                static void call1b(Base1 base1) { base1.method1b (); }
                static void call2a(Base2 base2) { base2.method2a (); }
                static void call2b(Base2 base2) { base2.method2b (); }
                public static void main(String[] args) 
                {
                    SubClass s = new SubClass();
                    call1a(s);
                    call1b(s);
                    call2a(s.getBase2());
                    call2b(s.getBase2());
                }
            }

    Here the inner class previous named Inner2 is defined without a name, as part of the return statement. An inner class can be defined within another class, or within any method of another class, or even within any expression in a method of another class. This case defines the inner class, including its override of method2b, within the expression returned by the return statement. No name is given to the inner class. Only its parent (Base2), methods (method2b) and instance variables (none) are specified.

    For more details of this and other techniques using inner classes, see the excellent discussion of inner classes in Chapter 8 "Interfaces and Inner Classes" of [Eckel00], and also the "Choosing Between Lists" section of Chapter 9 "Holding Your Objects", which uses an array of anonymous inner classes as a set of tests in a small benchmark program, and also the "Anonymous inner classes" section of the "A directory lister" section of Chapter 11 "The Java I/O System" which shows how to use an anonymous inner class as a FilenameFilter when iterating over files in a directory.

  10. Exceptions

    1. Declaring Exceptions

      Last Updated: 7/16/2000
      Applies to:  Java 1.0+

      To declare an exception:

          class Exception1 extends Exception
          {
              public Exception1() {}
              public Exception1(String msg) { super(msg); }
          }

      Or, if you don't intend to ever pass a details string as part of the exception:

          class Exception1 extends Exception {}

      --Fred

    2. Catch all exceptions with catch (Throwable t)

      Last Updated: 12/6/1998
      Applies to:  Java 1.0+

      To catch all exceptions, even if you don't know which ones might be thrown, use the syntax:

      	catch (Throwable t)

      This is the Java equivalent of the C++ syntax:

      	catch (...)

      --Fred

    3. Querying Exception Info

      Last Updated: 7/16/2000
      Applies to:  Java 1.0+

      After catching an exception, you can query information about it like any other Java Object via getClass().  You can also use methods of the Throwable class (of which Exception is a subclass).  You can get the string message, if any, thrown with the exception via getMessage().  You can get a string containing the name and the message via toString().  Finally, you can print all this info plus a complete stack trace to the console or a file via printStackTrace().For example:

          try
          {
              sub1();
          }
          catch (Exception e)
          {
              System.out.println ("getName():" + e.getClass().getName());
              System.out.println ("getMessage():" + e.getMessage());
              System.out.println ("toString():" + e.toString());
              System.out.println ("printStackTrace():");
              e.printStackTrace();
          }

      Here, printStackTrace is called with no parameter, so it prints to standard error, but you can also call it with a stream parameter (and thus capture the output in a file or string).  See Propagating and Re-Throwing Exceptions for a more complete sample.

      --Fred

    4. Propagating and Re-Throwing Exceptions

      Last Updated: 7/16/2000
      Applies to:  Java 1.0+

      Once you've caught an exception, you can deal with the problem (correcting it, and re-trying the same operation that originally failed, trying an alternative approach, skipping the operation, etc.), and continue normally.

      If you don't catch an exception, it propagates to the caller, carrying along the detailed message, if any, that was specified at the point where it was thrown, any other instance variables of the exception that you may have declared, and a full stack trace to the point where it was thrown.  See example sub1 below.

      Alternatively, you can catch the exception, take some local corrective action, and then re-throw it without disturbing the error details, stack trace, etc.  See example sub1a below.

      You can even re-throw the same exception with an updated stack trace to show the error as having been originally thrown from your error handler, via fillInStackTrace().   This is useful to hide the details of lower level components used by your component.  See example sub1b below.

      You can go one step further, updating not only the stack trace, but also the detailed message and all instance variables, by allocating a new exception object of the same type and throwing it instead of the one you caught.  See example sub1c below.

      Finally, of course, you can throw an entirely different exception from your exception handler.  Here is a complete sample:

      // Test case to show propagation, handling, re-throwing, and reporting
      // of exceptions.
      
      class Exception1 extends Exception
      {
          public Exception1() {}
          public Exception1(String msg) { super(msg); }
      }
      
      public class Thrower
      {
          //
          // Nested calls that allow the exception to propagate.
          //
          public int sub1() throws Exception1
          {
              return sub2();
          }
          public int sub2() throws Exception1
          {
              return sub3();
          }
          public int sub3() throws Exception1
          {
              throw new Exception1("This is the detail info.");
          }
      
          //
          // Nested calls with one that catches and re-throws the same exception.
          //
          public int sub1a() throws Exception1
          {
              return sub2a();
          }
          public int sub2a() throws Exception1
          {
              try
              {
                  return sub3a();
              }
              catch (Exception1 e)
              {
                  // Take some cleanup action here, but we still want to 
                  // report the original location of the exception.
                  // ...
                  throw e;
              }
          }
          public int sub3a() throws Exception1
          {
              throw new Exception1("This is the detail info.");
          }
      
          //
          // Nested calls with one that catches the exception, updates the 
          // stack trace info, and re-throws it.
          //
          public int sub1b() throws Exception1
          {
              return sub2b();
          }
          public int sub2b() throws Exception1
          {
              try
              {
                  return sub3b();
              }
              catch (Exception1 e)
              {
                  // Take some cleanup action here, then re-throw the 
                  // exception with the original details string, but 
                  // updating the stack info to report this as the original
                  // throw of the exception, hiding the fact that sub3b was 
                  // ever involved.
                  // ...
                  throw (Exception1) e.fillInStackTrace();
              }
          }
          public int sub3b() throws Exception1
          {
              throw new Exception1("This is the detail info.");
          }
      
      
          //
          // Nested calls with one that catches the exception, and throws a 
          // different one of the same type.
          //
          public int sub1c() throws Exception1
          {
              return sub2c();
          }
          public int sub2c() throws Exception1
          {
              try
              {
                  return sub3c();
              }
              catch (Exception1 e)
              {
                  // Take some cleanup action here, then re-throw the 
                  // exception with a new details string, and updating 
                  // the stack info to report this as the original throw 
                  // of the exception, hiding the fact that sub3b was 
                  // ever involved.
                  // ...
                  throw new Exception1("This is different detail info.");
              }
          }
          public int sub3c() throws Exception1
          {
              throw new Exception1("This is the detail info.");
          }
      
      
          public static class Tester
          {
              public static void main(String[] args)
              {
                  Thrower t = new Thrower();
                  System.out.println ("Begin tests of " + 
                                      t.getClass().getName() + "...");
                  //
                  // Prints stack trace as expected.
                  //
                  System.out.println ("\nTest sub1:");
                  try
                  {
                      t.sub1();
                  }
                  catch (Exception e)
                  {
                      System.out.println ("getName():    " + e.getClass().getName());
                      System.out.println ("getMessage(): " + e.getMessage());
                      System.out.println ("toString():   " + e.toString());
                      System.out.println ("printStackTrace():");
                      e.printStackTrace();
                  }
      
                  //
                  // Prints stack trace as expected.  Note that the sub2a line 
                  // number shows the call to Sub3a that originally raised the 
                  // exception, not the line that re-throws it.
                  //
                  System.out.println ("\nTest sub1a:");
                  try
                  {
                      t.sub1a();
                  }
                  catch (Exception e)
                  {
                      System.out.println ("getName():    " + e.getClass().getName());
                      System.out.println ("getMessage(): " + e.getMessage());
                      System.out.println ("toString():   " + e.toString());
                      System.out.println ("printStackTrace():");
                      e.printStackTrace();
                  }
      
                  //
                  // Prints stack trace as expected.  Note that the sub2b line 
                  // number shows the line that re-throws the exception, and that
                  // the call to Sub3b is not mentioned in the stack trace.
                  //
                  System.out.println ("\nTest sub1b:");
                  try
                  {
                      t.sub1b();
                  }
                  catch (Exception e)
                  {
                      System.out.println ("getName():    &