O'Reilly logo

Hardcore Java by Robert Simmons

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 1. Java in Review

I can hear the groans from here—a review on Java? Don’t worry, I won’t bore you with all of the gory syntax details or concepts of the Java language that you can easily pick up in other books. Instead, I will present a conceptual review that focuses on some various important issues that are often overlooked or underemphasized. The study of these issues will not only give you a better understanding of the Java language, but prepare you for what’s covered in the rest of the book. You should think of this chapter as a roving spotlight, highlighting various issues of Java that are worthy of mention; even the intermediate and advanced programmer will benefit from the study of these issues.

Core Concepts

To understand the advanced concepts of the Java language, there are a few core concepts that you must have firmly in mind. Without these concepts, much of this book will not make a lot of sense. The concepts of pointers in Java, its class hierarchy, and RTTI (runtime type identification) are three of the most important on this list.

Constant Problems with Pointers

Java and C++ use a very analogous syntax to symbolize their instructions to the computer’s CPU. In fact, there are probably more similarities between these languages than the two entrenched camps of supporters would like to admit. One difference between Java and C++ that is often mentioned, though, is that Java does not use pointers.

Pointers in C++ were a constant source of problems and were determined to be the programming equivalent of evil incarnate. There was, and is to this day, a large group of applications in C++ that suffer from the effects of this particular wrong. Therefore, Sun decided to leave them out of Java—at least that’s the theory.

In reality, Java uses what C++ calls references . In fact, other than primitives, all variables in Java are references. References and pointers are very similar; both contain the memory location of a particular object. Pointers, however, allow you to do arithmetic whereas references do not. The fact that Java uses so many references introduces some difficulties that novice and proficient Java developers often get burned by. The code shown in Example 1-1 demonstrates one of these difficulties.

Example 1-1. Collections are passed as references
package oreilly.hcj.review;
public class PointersAndReferences {
  public static void someMethod(Vector source) {
    Vector target = source;
    target.add("Swing");
  }
}

Here, you simply copy the passed-in source vector and add a new element to the copy (named target); at least that is how it appears. Actually, something quite different happens. When you set target equal to source in the code, you copy a reference to the target vector, and not the contents of the vector itself. Since both variables now point to the same vector, this function actually adds an element to the source vector that was passed into the method; this was almost certainly not the desired effect! We will discuss how to prevent this problem in Chapter 2.

Since you can change the contents of an incoming collection, Java actually does have pointers—that is, Java references embody the same computer science principles that pointers do. So, when someone tells you that Java doesn’t have pointers, you can correct them by saying, “Java has pointers, it just doesn’t have pointer arithmetic.”

Also, Java’s use of references isn’t a bad thing. In fact, the references are actually necessary to the Java language. Without them, you would have to pass everything by value. This would entail copying every single object each time an object was passed to a method. If the object were a String, this copying probably wouldn’t be a big deal. However, if the object is a large array or set, the copy could take a long time. Therefore, passing everything by value would make Java code run extremely slowly. Furthermore, some objects simply don’t make sense to copy at all. If you have a GUI panel and wish to pass the GUI panel to another component that needs to refer to it, you certainly don’t want two copies of the panel floating around in memory. Instead, you want to send a reference to the existing panel. All of these issues point out the need for references in Java.

Everything Is a Class and Object Is God

In Java, all constructed types are regarded as objects. In fact, the only items in Java that are not objects are the primitive types, such as int and boolean; and even these primitive types can be treated as objects under some circumstances, such as when you use reflection.

I would expect that most Java programmers would find this trivial since they already know that every nonprimitive in Java is an object. The point that I am really trying to make, though, is that every constructed type in Java descends from the class java.lang.Object. Even if you don’t declare a class as extending java.lang.Object, you still get this behavior. Example 1-2 shows a class that explicitly extends java.lang.Object, while Example 1-3 is a class that has no explicit extension.

Example 1-2. Explicitly extending java.lang.Object
public class SomeClass extends Object {
}
Example 1-3. Implicitly extending java.lang.Object
public class SomeClass {  
}

In Example 1-2, SomeClass makes its object hierarchy clear; its superclass is Object.

Example 1-3, while visibly different, is actually equivalent to Example 1-2. The Java compiler will automatically assign the superclass of Object to this version of SomeClass.

In fact, even a class that implements interfaces instead of extending another class extends java.lang.Object. As a result, these two declarations are identical:

public class SomeClass extends Object implements Serializable {
}

public class SomeClass implements Serializable {
}

So, unlike C++, you cannot create a class that does not have a superclass. Object is the superclass to all classes in the entire language. In the example code for this book, you will find the class oreilly.hcj.review.ObjectIsGod, which demonstrates this concept:

> ant -Dexample=oreilly.hcj.review.ObjectIsGod run_example
run_example:
     [java] class oreilly.hcj.review.ObjectIsGod$SomeClass --|> class 
java.lang.Object
     [java] class oreilly.hcj.review.ObjectIsGod$SomeOtherClass --|> class 
java.lang.Object

There are three main reasons why java.lang.Object is always at the top of the inheritance tree:

  • For Java to know the type of objects it must load, and to enforce type safety, it has to have some base type with which it can refer to all objects.

  • Java currently lacks the concept of the parameterized type (also known as a template). This means that all the objects that are be stored in collection classes ultimately have to descend from one class.

  • The fact that all objects have java.lang.Object as a superclass gives you supreme power when it comes to using reflection to turbocharge your code. Without this sort of inheritance, many techniques in reflection would be impractical. (We will discuss reflection in detail in Chapter 9.)

RTTI

Runtime type identification , or RTTI, is an extremely powerful tool. With RTTI, objects become very friendly and readily tell you what they are, how they are used, and more. In Java, RTTI is built right into the core of the virtual machine. You’ve almost certainly used RTTI, even if you don’t realize it; it’s all over the place. For example, consider that every object in Java can tell you what type it is through the getClass( ) method. Whenever you invoke getClass( ), you use RTTI. Example 1-4 shows the getclass( ) method in action.

Example 1-4. Demonstration of basic RTTI usage
package oreilly.hcj.review;
public class RTTIDemo {
  public final static void basicRTTIDemo ( ) {
    Float y = new Float(15.0);
    String name = "Fred";
    System.out.println(y.getClass( ));
    System.out.println(name.getClass( ));
  }
}

This method will, quite politely, print the following:

>ant -Dexample=oreilly.hcj.review.RTTIDemo run_example
run_example:
     [java] class java.lang.Float
     [java] class java.lang.String

Since the getClass( ) method is a method defined by the class java.lang.Object, it is guaranteed to be there for all objects.

Java also uses RTTI to protect a programmer from her own errors, as Example 1-5 demonstrates.

Example 1-5. RTTI enforces type safety
package oreilly.hcj.review;
public class RTTIDemo {
  public static class A {
  }

  public static class B extends A {
  }

  public static class C {
  }
  

  public final static void castingWithRTTI ( ) {
    A a = null;
    A a1 = new A( );
    B b = new B( );
    C c = new C( );
    
    a = (A)b; // no problem
    b = (B)a; // still no problem, casting back to what it was created as. 
    a = a1; // Same type so no problem
    Object d = (Object)c; // no problem because of implicit inheritance
    c = (C)d; // casting back
    b = (A)a1; // compiler error: a1 is not a B. 
    b = (B)c; // compiler error: c is not a B.   
  }
}

This code shows how to create an object of a subclass, cast it to its base class, and then cast it back to the subclass. RTTI works in the background to ensure that your casting is always legal; in other words, an object can safely be cast only to its own type, or to a type that it is inherited from. In the sample code for the book, Example 1-5 is replicated with the bad casts commented out. If you uncomment these lines, you will see that the program won’t even compile, and you will get errors such as those shown here:

>ant -Dexample=oreilly/hcj/review/RTTIDemo.java compile_example
compile_example:
    [javac] Compiling 1 source file to C:\dev\hcj\bin
    [javac] C:\dev\hcj\src\oreilly\hcj\review\RTTIDemo.java:54: incompatible types
    [javac] found   : oreilly.hcj.review.RTTIDemo.A
    [javac] required: oreilly.hcj.review.RTTIDemo.B
    [javac]     b = (A)a1;  // compiler error: a1 is not a B.
    [javac]         ^
    [javac] C:\dev\hcj\src\oreilly\hcj\review\RTTIDemo.java:55: inconvertible types
    [javac] found   : oreilly.hcj.review.RTTIDemo.C
    [javac] required: oreilly.hcj.review.RTTIDemo.B
    [javac]     b = (B)c;  // compiler error: c is not a B.
    [javac]            ^
    [javac] 2 errors

RTTI is useful for preventing common errors in programming. It also has many other uses; we will get to the juicy details of exploiting RTTI in Chapter 7 and Chapter 8.

Syntax Issues

When compared to languages such as C++, Java has a very simple syntax. However, there are some points of syntax that should be covered, even for the intermediate and advanced Java programmer.

Abbreviated if

One of the things that is not well understood about the if statement is that it abbreviates evaluations in order from left to right. For example, consider the following code:

package oreilly.hcj.review;

public class SyntaxIssues {
  public static void containsNested(final List list, 
                                    final Object target) {
    Iterator iter = list.iterator( );
    for (Set inner = null; iter.hasNext( ); inner = (Set)iter.next( )) {
      if (inner != null) {
                       if (inner.contains(target)) {
                         // do code.
                       }
                     }
    }
  }
}

In this code, the method is passed a list of sets to determine whether the targeted element is in one of the nested sets. Since a list can contain nulls, the method wisely checks for null before dereferencing inner. As long as inner isn’t null, the method checks to see whether the set contains target. This code works, but the deep nesting is not necessary. You can write the code in another way:

package oreilly.hcj.review;

public class SyntaxIssues {
  public static void containsNested2(final List list, 
                                     final Object target) {
    Iterator iter = list.iterator( );
    for (Set inner = null; iter.hasNext( ); inner = (Set)iter.next( )) {
      if ((inner != null) && (inner.contains(target))) {
                       // do code.
                     }
    }
  }
}

In this version, the method checks for null and containment on the same line. This version of the method is in no danger of throwing NullPointerExceptions because the evaluation of the if statement is abbreviated at runtime. While evaluating an if statement, the evaluations are run from left to right. Once the evaluation reaches a definitive condition that cannot be altered by any other evaluation, the remaining evaluations are skipped.

Ternary Expressions

Ternary expressions look a little strange at first. However, they can be useful in making code read better. For example, consider the following code:

package oreilly.hcj.review;

public class SyntaxIssues {
  public int someMethod(final Point p) {
    if (p == null) {
      return 0;
    } else {
      return p.x + p.y;
    }
    
  }
}

Although this method runs properly, it is unnecessarily wordy. The same thing could have been accomplished using a much simpler ternary expression:

package oreilly.hcj.review;

public class SyntaxIssues {
  public int someElegantMethod(final Point p) {
    return p == null ? 0 : p.x + p.y;
  }
}

The emphasized line is not as cryptic as it may appear at first. It is merely a large evaluation expression. If the given clause evaluates as true, then the value of the entire statement is in the ? clause. However, if the condition evaluates as false, then the value of the entire statement is in the : clause. The result of the evaluation is returned by the return statement.

Ternary statements are very useful for performing evaluations such as this. However, you have to be aware of a few gotchas. For example, the following code will not work:

p == null ? System.out.println("p is null!") : return p.x + p.y;
return 0;

In this statement, the user wants to make an if-then clause that would print a message if the point passed to the method was null or return the value if the point was not null. Basically, he is going for a less wordy version of the following code:

package oreilly.hcj.review;

public class SyntaxIssues {
  public static int someMethod(final Point p) {
    if (p == null) {
      System.out.println("p is null!")
    } else {
      return p.x + p.y;
    }
  }
  return 0;
}

The problem is that a ternary expression is not an if-then clause. It is an evaluation clause. Both of the clauses in the ternary expression must evaluate to something. The statement System.out.println("p is null!") evaluates to void because the return of the println( ) method is void. Additionally, the statement return p.x + p.y is pure syntactical nonsense because return is a keyword that doesn’t evaluate to anything.

Although ternary expressions are useful for shortening what would be an otherwise long and drawn-out piece of code, they can be cryptic to read if they are abused. I recommend that you use ternary expressions only for very small evaluations. If your evaluation is complex, you are better off going with the if-then structure.

Leveraging for Loops

The for loop is one of the most elegant and underused looping structures in the Java language. Most developers use only the basic concept of a for loop to iterate through a predefined series of numbers:

for (int idx = 0; idx < args.length; idx++) {
  //  . . . do something. 
}

To the average reader, this code should be completely boring. However, to many developers, this is the pinnacle of for loop usage. But there are many more things that you can do with a for loop to make your code more elegant while dealing with some annoying programming issues.

Although the techniques in this section are not necessarily required, they do make your code look a lot nicer. However, if there is one concept that you should take from this section, it’s that for loops can do much more than simply cycle through numbers.

for loop fundamentals

One recurring problem when dealing with collections is the need to iterate through the collection and keep track of an index at the same time. For example, you may need to copy all of the x coordinates from a List of Point objects into an array of ints. The following code represents a first attempt at implementing this method:

package oreilly.hcj.review;

import java.awt.Point;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class SyntaxIssues {
  public static int[] extractXCoords(final List points) {
    int[] results = new int[points.size( )];
    Point element = null;
    int idx = 0;
    Iterator iter = points.iterator( );
    while (iter.hasNext( )) {
      element = (Point)iter.next( );
      results[idx] = element.x;
      idx++;
    }
    return results;
  }
}

Although this piece of code will work, it isn’t very elegant. The code would look much neater written in the following way:

package oreilly.hcj.review;

public class SyntaxIssues {
  public static int[] extractXCoords2(final List points) {
    int[] results = new int[points.size( )];
    Point element = null;
    Iterator iter = points.iterator( );
    for (int idx = 0; iter.hasNext( ); idx++) {
      element = (Point)iter.next( );
      results[idx] = element.x;
    }
    return results;
  }
}

This second example is much more elegant than the first. However, the important thing to note about the rewritten method is that the exit clause to the for loop has nothing to do with idx.

You can use the for loop structure in this manner because each of the statements inside the for loop are completely independent of each other. This is a fact that seems to escape many Java developers. The point is that the for loop has the following grammar:

for (Allocation Statement; Expression; Iteration Statement)

The allocation statement in the for loop is executed when the loop is set up. At each iteration of the loop, the expression in the middle is evaluated and, if the expression is true, the iteration statement is run. You can stick any statement or expression in the for loop regardless of whether they are using the same variables. For example, the following is perfectly legal:

package oreilly.hcj.review;

public class ForLoops {
  public static void forWeird( ) {
    boolean exit = false;
    int idx = 0;

    for (System.setProperty("user.sanity", "minimal"); exit == false;
         System.out.println(System.currentTimeMillis( ))) {
      // do some code.
      idx++;
      if (idx == 10) {
        exit = true;
      }
    }
  }
}

When the loop is initialized, the system property user.sanity will be set to minimal. At each iteration of the loop, the current time in milliseconds will be printed as long as exit is false. This is a great demonstration of what you can do with for loops.

Now that you know just what is possible with for loops, you can leverage them even further to do things that are a bit more mainstream.

Collection iteration with for

Iterating through collections offers you another chance to have a bit of fun with the for loop. Let’s begin with typical code used to iterate through a collection of objects:

package oreilly.hcj.review;

public class ForLoops {
  public static void forLong( ) {
    Properties props = System.getProperties( );
    Iterator iter = props.keySet( )
                         .iterator( );

    String key = null;
    while (iter.hasNext( )) {
      key = (String)iter.next( );
      System.out.println(key + "=" + System.getProperty(key));
    }
  }
}

This snippet will iterate through all of the system properties and dump them to System.out. It works fine but it is very wordy and can be slightly problematic. For example, try the following bug on for size:

package oreilly.hcj.review;

public class ForLoops {
  public static void propsDump(final Set customKeys) {
      Properties props = System.getProperties( );
      Iterator iter = props.keySet( )
                           .iterator( );

      String key = null;
      System.out.println("All Properties:");
      while (iter.hasNext( )) {
        key = (String)iter.next( );
        System.out.println(key + "=" + System.getProperty(key));
      }
  
      System.out.println("Custom Properties:");
      iter = customKeys.iterator( );
      while (iter.hasNext( )) {
        System.out.println(key + "=" + System.getProperty(key));
      }
    }
  }
}

If you didn’t see the bug in this piece of code instantly, then you are in trouble. The problem is that this code generates a logic error. In the second iteration, key = (String)iter.next( ) wasn’t called. However, since key is still in scope from the previous loop, the code compiles and runs. Trivial errors such as this can consume tons of hours in complex applications.

It would be great if you could scope the key variable and the iter variable to the for loop where they are used. You can do this and make your program more elegant with the following code:

package oreilly.hcj.review;

public class ForLoops {
  public static void forShort( ) {
    Properties props = System.getProperties( );
    for (Iterator iter = props.keySet( ).iterator( ); iter.hasNext( );) {
      String key = (String)iter.next( );
      System.out.println(key + "=" + System.getProperty(key));
    }
  }
}

In this example, the ability of for was leveraged to isolate your iteration variable. In this example, key is defined only within the for loop, and any attempt to access it outside the for loop will generate a compiler error. Additionally, the iterator itself is scoped to the for loop, which guards against a user that forgets to set up his iterator. Finally, the increment portion of the for loop has no value at all, which is completely legal.

When looking at forShort( ), you may be inclined to object that key is being allocated in a loop and that loop allocation is bad. In fact, in this example, the compiler notices that as well and optimizes out the variable to be allocated once while keeping it scoped to the for loop.

Abrupt Flow Control

Two of the more poorly understood pieces of Java syntax are the break and continue keywords. Both are used to control flow within loops or other complex statements. They can be very useful in some situations, but you need to be aware of their limitations.

break

The break keyword is probably familiar to most Java programers who have a flair for using switch statements. An example of a common usage is shown here:

package oreilly.hcj.review;

public class SyntaxIssues {
    public static String decode(final int input) {
      String decoded = null;
      switch (input) {
        case 1:
          decoded = "Option 1";
          break;
        case 2:
        case 3:
          decoded = "Option 2";
          break;
        default:
          return "Option 3";
      }
      return decoded;
    }
  }
}

The break keyword tells the virtual machine to exit the switch statement. In this switch statement, if the user inputs a 1, the result will be "Option 1“. If the user inputs a 2 or a 3, the result will be "Option 2“. Finally, if she inputs anything else, the result will be "Option 4“. The break statement lets certain cases, such as 2, fall through, but breaks out when it is done with the relevant logic. Without the break, you would always get "Option 4" as the result of the method because all lines inside the switch statement would run.

The break statement is a great way to make decisions such as those in the previous example. However, every now and then you get silly people that do something like the following:

package oreilly.hcj.review;

public class SyntaxIssues {
    public static String decode(final int input) {
      String decoded = null;
      switch (input) {
        case 1:
          decoded = "Option 1";
          break;
        case 2:
        case 3:
          decoded = "Option 2";
          break;
        default:
          return "Option 3";
          break; // <= someone doesn't get it!
      }
      return decoded;
    }
  }
}

In this code, the final break statement is completely unnecessary, and also unreachable. The code will exit the switch statement regardless of whether the break was there. If you find it hard to believe people actually write code like this, the templates to generate switch statements that come with Eclipse do exactly this. However, this is a rather minor, if somewhat silly, problem. Some problems with break are a lot more serious. Consider the following code:

package oreilly.hcj.review;

public class SyntaxIssues {
    public static void matrixMeth(final Point[][] values) {
      for (int x = 0; x < values[0].length; x++) {
        for (int y = 0; y < values.length; y++) {
          if ((values[x][y].x < 0) 
               || (values[x][y].y < 0)) {
            break; // exit to the System.err line.
          }
          // do something with the value
        }
      }
      System.err.println("Invalid Point in Matrix");
      // cleanup processing resources
    }
  }
}

In this piece of code, the developer wants the nested for loops to exit if a bad Point is detected in the matrix. However, the break statement doesn’t do this. It merely breaks out of its current loop. The outer loop still runs, and the method processes bad data with potentially disastrous results.

When you use break in your code, you should be aware of these limitations and plan accordingly. The break keyword can be extremely useful for quick and abrupt termination of complex loops but its limitations can bite you if you aren’t careful.

continue

The continue keyword is similar to break in that it abruptly changes the flow control of a method. However, whereas break exits a loop completely, continue merely skips back to the loop clause itself:

package oreilly.hcj.review;

public class SyntaxIssues {
  public static void continueFunc( ) {
    for (int idx = 0; idx < 1000; idx++) {
      //  . . . do some complex code. 
      if (idx == 555) {
        break;
      }

      //  . . . processing code
    }

    for (int idx = 0; idx < 1000; idx++) {
      //  . . . do some complex code. 
      if (idx == 555) {
        continue;
      }

      //  . . . processing code.
    }
  }
}

In the first loop, the processing code is executed only 554 times. When the value of idx is 555, the break statement is hit and the loop exits. However, in the second loop the processing code is executed 999 times. In this case, when idx is 555, the continue statement is hit, and the remainder of the logic inside the for loop is skipped. Subsequently, the loop then continues on normally with 556; therefore, the processing code is skipped only for element 555.

Although this example is trivial, the use of the continue statement can be a significant asset to your development when the code inside the loop is very complex. With many such possible “skip” conditions, your code can become deeply nested. The continue statement simplifies things dramatically. Without the continue statement, you would have to enclose the entire remainder of the for loop inside an if statement.

Tip

The continue statement has the same semantics as the break statement in that it applies only to the loop in which the continue is enclosed.

Labels

Labels are one of those obscure pieces of Java syntax that you see only once in a blue moon. They are a way to mark a statement with an identifier to be used by a break or continue statement.

The following code declares labels on two different lines in a program:

                  LOGLINE: System.err.println("Invalid Point in Matrix");
LINE: for (int x = 0; x < values[0].length; x++) { ... }

The label is declared by simply prefacing the line with a legal variable name and then a colon. As usual with Java, any leading whitespace on the line will be ignored. However, keep in mind that you cannot declare a label unless it is the first statement on a line. The compiler would spit out all sorts of parsing errors if you tried something like the following:

System.err.println("Invalid Point in Matrix");  LOGLINE2:

Once you have declared the label, you can use it in a break or a continue statement. The following code shows how you can use break to repair a faulty matrix:

package oreilly.hcj.review;

public class SyntaxIssues {
  public static void matrixMeth2(final Point[][] values) {
    RESTART: {
      for (int x = 0; x < values[0].length; x++) {
        for (int y = 0; y < values.length; y++) {
          if ((values[x][y].x < 0) || (values[x][y].y < 0)) {
            values[x][y].x = Math.max(values[x][y].x, 0);
            values[x][y].y = Math.max(values[x][y].y, 0);
            break RESTART;  // Try to process again!
          }
  
          // do something with the value
        }
      }
    }
    // continue processing
  }
}

In this version, if an error condition is detected, the method will try to repair the error condition and then start to process the matrix all over again. This is accomplished by the break RESTART; statement. This statement tells the virtual machine to immediately transfer control to the statement labeled RESTART. This will work only if the break statement is nested inside the labeled statement. Otherwise, the compiler will just laugh at you sardonically and tell you that the label RESTART isn’t declared.

Using labels with continue is slightly different. continue must be nested within the code block of any label to which it refers. Additionally, a continue can transfer control only to a label set on a looping construct such as a for, while, or do statement. For clarification on this rule, look at the following code:

package oreilly.hcj.review;

public class SyntaxIssues {
  public static void matrixMeth3(final Point[][] values) {
    LINE: {
      for (int x = 0; x < values[0].length; x++) {
        COLUMN: for (int y = 0; y < values.length; y++) {
          if ((values[x][y].x < 0) || (values[x][y].y < 0)) {
            continue LINE;  // skip the rest of the line;
          }
  
          // do something with the value
        }
      }
    }
    LOGLINE: System.err.println("Invalid Point in Matrix");

    // continue processing

    PASS_TWO: for (int x = 0; x < values[0].length; x++) {
      // do some code.
    }
  }
}

In this example, instead of exiting to the log line, the programmer wants to simply skip the rest of the problematic line in the matrix. Using a continue statement without a label would have merely transferred control to the inner for loop. However, by telling the continue statement to continue at the LINE label, you can exit the inner for loop and skip the rest of the line properly. However, you could not use LOGLINE as a continue target because the statement after the label isn’t a loop construct, such as a for, while, or do statement. You also couldn’t use PASS_TWO as a target for the continue statement because although the label marks a looping statement, the continue statement is not nested in that looping statement. Furthermore, it would be impossible to break or continue to a label outside of the method.

Although labels can be useful in controlling the flow in a program, they can lead to a lot of confusion in development due to their idiosyncrasies. I recommend that you not use them as a rule; however, you should now be able to unwind the spaghetti code of another developer that did use them.

assert

The assert keyword is a new addition to Java that was introduced in JDK 1.4. It was a long overdue addition to the language, as it provides for error checking that C++ and other languages already have. However, since its introduction, I haven’t seen it used nearly as much as it should be. The reason for this is a basic lack of understanding as to how assert works. The assert keyword has two forms. The first form of assertion uses the following grammar:

assert Boolean_Expression;

In this form, Expression is replaced by an expression that evaluates to a boolean result. If the result is false, then an AssertionError will be thrown by the compiler:

package oreilly.hcj.review;

public class Assertions {
  protected static void helperParseArgs (final String[] args) {
    assert (args != null);
    //  . . . code
  }
}

In this example, the programmer wants to make sure that the user of the helperParseArgs( ) method didn’t send a null for the arguments parameter. To accomplish this, he uses the assert keyword. If the user passes a null, then the virtual machine will throw an AssertionError with no description, but with a stack trace to help the errant user figure out what he did wrong. If the developer of the method wants to provide an error message, he could have used the second form of assert:

package oreilly.hcj.review;

public class Assertions {
  protected static void helperParseArgs (final String[] args) {
    assert (args != null) : "args cannot be null.";
    //  . . . code
  }
}

In this case, if the assertion is thrown, the second expression is evaluated and the result of that evaluation is used for the detail message of the AssertionError. Keep in mind that you can use any expression that evaluates to anything other than void as the second expression. For example, the following would be illegal:

package oreilly.hcj.review;

public class Assertions {
  protected static void helperParseArgs (final String[] args) {
    assert (args != null) : String s = "args cannot be null.";
    //  . . . code
  }
}

An attempt to do something like this would be rejected by the compiler since the evaluation of the expression is void. Similarly, any method that returns void is off limits as well.

Assertions versus exceptions

One of the most common questions asked about assertions is why you should bother using them when you can perform these checks and indicate errors with exceptions.

First of all, the code doing the check may be far more complicated than a simple check for null. In fact, the body of assertion expressions can easily be as much work for the processor as the body of the method itself. If you use exceptions, then these checks will be performed every time the method is called. In a production system, this can add significant overhead.

On the other hand, with assertions, the virtual machine can be set to ignore the assertions. As a result, their processing time is near zero. In development mode, during testing, or during diagnosis of some nasty problem, you can turn on assertions and figure out the problems. Once the software is stable, you can turn off assertions and gain a performance boost. As a bonus, assertions offer a shorthand that makes your code look more elegant.

Tip

Assertions are off by default and have to be turned on manually.

To assert or not to assert

Deciding whether to use assertions or exceptions is a decision that you have to take on a case-by-case basis. Here are some tips that will help you make the right decisions:

  • Don’t use assertions to validate parameters of public functions. These functions should throw NullPointerException, IllegalArgumentException, and other relevant exceptions instead. Since public functions will be used by other programmers, you should make sure that they get the right errors if they mess up.

  • Use assertions to check preconditions and postconditions on parameters of protected and private access methods.

  • Don’t use assertions to check for software user errors. If you expect the user of your web-based online sales system to enter a 10-digit credit card number and she enters only 9 digits, don’t use an assert. Instead, throw IllegalArgumentException. If you use assert, as soon as someone turns off assertions on your servlet container, the checking logic in your system would go away.

  • Use assertions to check parameters and variables for conditions that shouldn’t happen. For example, consider the following event handler method:

    package oreilly.hcj.review;
    
    public class Assertions {
      public void mouseClicked(final MouseEvent event) {
        Object source = event.getSource( );
        assert(source != null);
        
        int hc = source.hashCode( );
        //  . . . do code using source
      }
    }

    The virtual machine should never pass null as a source for a mouse event. However, just to be sure, the user asserts it. The granularity of assertions that you should use here is something of a judgment call. If you assert everything, you will have to write a lot more code; however, your code will be rock solid.

  • Use assertions to check for invalid code branches. For example, consider the following GUI class:

    package oreilly.hcj.review;
    
    public class Assertions {
    package oreilly.hcj.review;
    
    import java.awt.event.ActionEvent;
    import javax.swing.JButton;
    
    public class SomeGuiPanel {
    
      private JButton cancelBtn;
      private JButton noBtn;
      private JButton yesBtn;
    
      public void actionPerformed(final ActionEvent event) {
        Object source = event.getSource( );
        assert (source != null);
    
        if (source == yesBtn) {
          //  . . . do code
        } else if (source == noBtn) {
          //  . . . do code
        } else if (source == cancelBtn) {
          //  . . . do code
        } else {
          assert false : "Invalid Source " + source.toString( );
        }
      }
    }

    In this class, the action handler expects the user to click on one of three buttons. The method doesn’t expect there to be any other buttons to which the class is listening. However, if a developer added a new button but forgot to change the handler, there would be an extra button to deal with. On the final else clause, the method checks to make sure there aren’t any other buttons by using assert. If the emphasized line is hit, the program will generate an assertion exception. In this case, using assert is much more elegant than using throw.

    Tip

    Some may object that the program should always throw an irrecoverable error at this point regardless of whether assertions are enabled. This is a valid point. However, in a production environment, that is not always a good idea. The accountants using your software wouldn’t know what an AssertionError is. Instead, when the program crashes, they are likely to give you an informative bug report such as, “It crashed while I was making my finance report.” Its better to use assertions so the program can at least limp along if something weird happens. Let the developers and QA people deal with the assertion errors.

  • Don’t use an assertion to do any work. Assertions are developer-level errors and shouldn’t be used to repair state in the program or perform complex logging. Also, don’t forget that if a user runs the program without assertions, the code will be gone. If that code was critical to the functioning of the program, you could be in deep trouble.

  • Don’t bother internationalizing assertion error messages. Again, since assertions are developer-level issues, internationalizing them would be a waste of time.

  • Use assertions to check post conditions. If you create a method and expect that it will never to return null to the user, you might write something like the following:

    package oreilly.hcj.review;
    
    public class SyntaxIssues {
      protected Object someHelperMethod( ) {
        Object result = null;
        //   . . . do some code that sets result.
        assert (result != null); // check post condition.
        return result;
      }
    }

    In this code, the method checks the return value against a post-condition before returning the value. If the value is still null after all of the work, the assertion will toss an error.

Now that you have a good idea of when to use assert and when not to, apply what you have learned on an actual class:

package oreilly.hcj.review;

import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class Assertions {

  public static void main(final String[] args) {
    helperParseArgs(args);
    Iterator iter = System.getProperties( )
                        .keySet( )
                        .iterator( );
    for (String key = null; iter.hasNext( ); key = (String)iter.next( )) {
      assert (key != null);
      System.out.println(key + "=" + System.getProperty(key));
    }
  }
  
  protected static void helperParseArgs(final String[] args) {
    assert (args != null);
                      assert (!Arrays.asList(args)
                                     .contains(null));
    // --
    List arglist = Arrays.asList(args);
    Iterator iter = arglist.iterator( );
    for (String argument = null; 
         iter.hasNext( ); 
         argument = (String)iter.next( )) {
      if (argument.startsWith("-D")) {
        if (argument.length( ) < 3) {
                            int idx = arglist.indexOf(argument);
                            throw new IllegalArgumentException("Argument" + idx
                                               + " is not a legal property argument.");
                          }
        int valueIdx = argument.indexOf('=');
        System.setProperty(argument.substring(2, valueIdx),
                           argument.substring(valueIdx + 1));
        assert (System.getProperty(
                               argument.substring(2, valueIdx))
                                       .equals(argument.substring(valueIdx + 1)));
      }
    }
  }
}

In this code, you have a method that parses the arguments to the program and looks for system properties to set. If it encounters an argument such as -Dmy.property=5 on the command line, it will set the system property my.property to the value 5.

The method that does the actual work is a protected helper method that checks the arguments to the method for validity using assertions. You should also note that the second check to the method takes quite a bit of work to accomplish since the list has to go through and see whether there are any nulls in the argument list. At production time, however, this work will be removed by turning off assertions. Inside the for loop, the method doesn’t use an assertion if the user gives a bad property name because this is an error made by the user of the program and not by the developers of the program.

Finally, the last assert statement checks that the system property was actually set properly. Notice that this assert does the exact same string manipulation work as the original setting of the system property. Some performance-minded developers may catch on to this fact and get the bright idea to store the key and value at each iteration of the loop, as shown here:

package oreilly.hcj.review;

public class Assertions {

for (String argument = null; iter.hasNext( ); argument = (String)iter.next( )) {
  if (argument.startsWith("-D")) {
    if (argument.length( ) < 3) {
      int idx = arglist.indexOf(argument);
      throw new IllegalArgumentException("Argument" + idx
                         + " is not a legal property argument.");
    }
    int valueIdx = argument.indexOf('=');
    String key = argument.substring(2, valueIdx);
                      String value = argument.substring(valueIdx + 1);
    System.setProperty(key, value);
    assert (System.getProperty(key).equals(value));
  }
}

Although this will work, it is a bit wasteful. The extra allocations of the key and value variables are needed only if assertions are enabled. If assertions are disabled, then these variables will be used only once. Whether to use variables to store parts of an assertion is a judgment call. If the code that is creating the variable is very long, a variable may be the best bet. If the code is simple, you should just save yourself the extra allocations at production time.

Assertions and deployment

If you are developing an application using assertions, you should be aware that by default, assertions are not enabled in the virtual machine. To enable all assertions, you need to pass the argument -ea to your virtual machine. If you need to enable assertions on system classes, use the switch -esa. Additionally, there are facilities for enabling assertions on individual packages. You should consult your virtual machine documentation for information on how to do this.

When developing a product, you should always have assertions enabled, as they will help you catch bugs. Also, your QA testers should run a program with assertions enabled since it is their job to figure out how to break your code.

In a production release, the situation is a bit foggier. In the first days of a production product release, you may want to make sure the project runs with assertions enabled. Although you will loose some performance and incur a few crashes, you will be able to detect many errors caught by your assertions. When to turn off the assertions in a production environment is a judgment call that the project manager has to make. There will come a time when you think you have used the assertions enough to catch most of the errors, and performance and stability become the overriding need; after all, assertions do crash your program. At that point, it is time to update your shortcuts and batch files to turn off assertions. When releasing new features, you should employ a similar operating strategy. This will help you catch unexpected bugs.

Unfortunately, there is no way to turn assertions on and off in a running system; however, you can easily find out whether they are on by using the little assertion trick shown here:

boolean ASSERTIONS = false;
assert ASSERTIONS = true;

The assert expression here evaluates to true and will not throw an assertion error at any time. If assertions are enabled, then the code will run and ASSERTIONS will be set to true. If assertions are disabled, then the code won’t be executed and ASSERTIONS will remain false.

Chaining Constructors

One of my personal pet-peeves is duplicated code. Unfortunately, you see a lot of it, especially in the creation of GUI widgets. The problem results from the way these widgets are built. Consider the button class shown in Example 1-6.

Example 1-6. Unchained constructors
package oreilly.hcj.review;
public class UnchainedConstructors extends JButton 
  implements ActionListener {
  
  public UnchainedConstructors(final String text) {
    setText(text);
    String tooltip = new String("A button to show " + text);
    setToolTipText(tooltip);
  }

  public UnchainedConstructors(final String text, final String tooltip) {
    setText(text);
    setToolTipText(tooltip);
  }

  public UnchainedConstructors(final String text, final String tooltip,
                               final ActionListener listener) {
    setText(text);
    setToolTipText(tooltip);
    addActionListener(listener);
  }
}

Although this code will work, it is wasteful and prone to errors. For example, if you change a line in the constructor that accepts three parameters and forget to make the same change in the other constructors, you would have a button that builds correctly under some circumstances, but fails to build correctly under others. This would lead to a debugging session that wouldn’t be much fun. The symptom of the problem is in the thought, “If I change this in one constructor, I have to remember to change it in the others.” Whenever you hear yourself thinking similar thoughts, you should realize that something is wrong. All of your code dependencies should be in the code, and not in your head. Inevitably, you will forget some of these dependencies during your project. Also, even if you have a photographic memory and perfect recall, the junior programmer working for you won’t be privy to these details and could unintentionally bust the code you had so well thought out. To avoid having to remember these things, you can rewrite the class as shown in Example 1-7.

Example 1-7. Simple chained constructors
package oreilly.hcj.review;
public class ChainedConstructors extends JButton 
  implements ActionListener {

  public ChainedConstructors(final String text) {
    this(text, new String("A button to show " + text), null);
  }

  public ChainedConstructors(final String text, final String tooltip) {
    this(text, tooltip, null);
  }

  public ChainedConstructors(final String text, final String tooltip,
                             final ActionListener listener) {
    setText(text);
    setToolTipText(tooltip);
    if (listener != null) {
      addActionListener(listener);
    }
  }

This code is much better; it chains the constructors so that each delegates to the constructor that takes all parameters. This constructor is referred to as the primary constructor . By using this technique, the code for the other constructors is limited only to what they do differently. This technique relieves you of having to remember to change each constructor if the common code is modified. You chain the constructors via the this( ) method. This special method allows you to call one constructor from another. It is analogous to using the super( ) method to construct base classes. The only remaining problem is that you have some rather ugly code as a result of chaining your constructors:

this(text, new String("A button to show " + text), null);

This code results from the fact that, just like super( ), you have to use the this( ) method as the first line in the constructor. I have yet to understand why this limitation is in Java, but it does indeed exist. As a result, some rather annoying problems are introduced. In this case, you were merely building a String object in the call to this( ). However, there are other cases where you will need to do a lot more work before calling the primary constructor. The secret to doing this without the ugliness is via helper methods:

package oreilly.hcj.review;
public class ChainedConstructors extends JButton 
  implements ActionListener {

  public ChainedConstructors(final String text, final boolean showDate) {
    this(text, buildDateToolTip(text, showDate), null);
  }

  private static String buildDateToolTip(final String text, 
                                         final boolean showDate) {
    final StringBuffer buf = new StringBuffer(250);
    buf.append("A panel to show ");
    buf.append(text);
    if (showDate) {
      buf.append(" created on ");
      DateFormat df = DateFormat.getInstance( );
      buf.append(df.format(Calendar.getInstance( ).getTime( )));
    }
    return buf.toString( );
  }
}

Here, you create a new static method that manipulates the input to the constructor and returns the tool tip text. Then you use the resulting text as the input to the tooltip parameter of the primary constructor. This constructor helper technique makes your code much cleaner and allows you to chain constructors even when there is complex logic within the constructors. The resulting code is much more stable and easier to maintain.

When using helper methods, it is important that you keep in mind one restriction: the this pointer is not available to the virtual machine at the time of a this( ) or super( ) call. The result of this restriction is that you can’t use any instance-scoped methods on the object being constructed. For example, the following code would be illegal:

public class ChainedConstructors extends JButton 
  implements ActionListener {

  public ChainedConstructors(final Class dataType) {
    this(buildClassText(dataType), dataType.getName( ), this);
  }

  protected String buildClassText(final Class dataType) {
    return dataType.getName( ).substring(dataType.getName( )
                             .lastIndexOf('.') + 1);
  }
}

In Java, a call such as buildClassText( ) is identical to the call this.buildClassText( ). The this reference is assumed unless you explicitly add some other kind of reference, such as an object or a class. In this case, the this pointer is not available yet, so you can’t call instance-scoped methods on this object. In fact, even though the class ChainedConstructors implements ActionListener, you can’t even use the this pointer to pass the object as a listener to itself. If you uncomment the above constructor in the code and try to compile it, you will get the following results:

>ant -Dexample=oreilly/hcj/review/ChainedConstructors.java compile_example
compile_example:
    [javac] Compiling 1 source file to C:\dev\hcj\bin
    [javac] C:\dev\hcj\src\oreilly\hcj\review\ChainedConstructors.java:43: cannot 
reference this before supertype constructor has been called
    [javac]     this(buildClassText(dataType), dataType.getName( ), this);
    [javac]          ^
    [javac] C:\dev\hcj\src\oreilly\hcj\review\ChainedConstructors.java:43: cannot 
reference this before supertype constructor has been called
    [javac]     this(buildClassText(dataType), dataType.getName( ), this);
    [javac]                                                        ^
    [javac] 2 errors

In short, the this pointer is off-limits to all constructor helper methods. Therefore, if you want to declare a helper method, you must make sure that the method is declared static. Since a static method doesn’t have a this pointer and doesn’t need one, the call will work properly. However, keep in mind that instance methods on objects already in scope are completely permissible, as shown here:

public ChainedConstructors(final Color color) {
  this(color.toString( ), "", null);
}

In this example, the instance method toString( ) on the color parameter passed to you is legal because it is already in scope.

While using chained constructors, you will come across situations in which you do not want to expose the primary constructor to the user of the class. The solution to this problem is to merely make the visibility of the primary constructor protected. That way, you get the best of both worlds. The constructors will be chained, and you won’t reveal any more about the class than you want to. Although it is unusual to see a class with constructors that have various visibilities, it is quite legal.

Initialization

Initialization is the process of assigning values to variables prior to the execution of the constructor for a class. Initialization is used in many places, and is often poorly understood by developers. Many developers think of initialization as one topic, when, in fact, there are six types of initialization that you can use in your code. The simplest type of initialization is shown here:

package oreilly.hcj.review;
public class InitalizerDemo {
  public String description = "An initialized member";
}

The variable description in this class is assigned the value of "An initialized member" just prior to construction of each instance of the object. Therefore, even if the constructor does not explicitly set description, it will be guaranteed to contain the value set in the initializer.

This type of initialization is important for solidifying code because it allows you to preset instance attributes to default values before the constructor is called. In this manner, constructors can ignore these attributes if they wish. Although this type of initialization is useful, there are times when initializations need to be a bit more complex:

package oreilly.hcj.review;
public class InitalizerDemo {
  public long timestamp = System.currentTimeMillis( );
}

In this initializer, you can call a method on the line of the initialization. This ability allows you to perform more complex initializations that would be impossible without a method. However, keep in mind that methods used in initializers are under the same restrictions as the helper methods for chained constructors that we discussed earlier—they should be static and can’t use the this pointer.

In fact, you don’t even need to define a helper method if you don’t want to; an initializer can be a method itself:

package oreilly.hcj.review;
public class InitalizerDemo {

  private String xmlClasspath;
                 {
                   final StringBuffer buf = new StringBuffer(500);
                   final String classPath = System.getProperty("java.class.path");
                   StringTokenizer tok = new StringTokenizer(classPath,
                                                   System.getProperty("path.separator"));
                   buf.append("<classpath>\n");
                   while (tok.hasMoreTokens( )) {
                     buf.append("  <pathelement location=\"");
                     buf.append(tok.nextToken( ));
                     buf.append("\"/>\n");
                   }
                   buf.append("</classpath>");
                   xmlClasspath = buf.toString( );
                 }
}

In this code, you translate the classpath into an XML structure upon initialization of the object. This code will work fine, as you can see by running the oreilly.hcj.review.InitializerDemo class in the example code:

>ant -Dexample=oreilly.hcj.review.InitializerDemo run_example
run_example:
     [java] ------Dumping Contents-----------
     [java] ---------------------------------
     [java] Initializer Demo
     [java] x86
     [java] C:\Documents and Settings\Robert
     [java] An initialized member
     [java] <classpath>
     [java]   <pathelement location="c:\j2sdk\\lib\tools.jar"/>
     [java]   <pathelement location="C:\j2sdk\addons\jakarta-ant-1.5\bin\\..\lib\xml-apis.jar"/>
     [java]   <pathelement location="C:\j2sdk\addons\jakarta-ant-1.5\bin\\..\lib\xercesImpl.jar"/>
     [java]   <pathelement location="C:\j2sdk\addons\jakarta-ant-1.5\bin\\..\lib\optional.jar"/>
     [java]   <pathelement location="C:\j2sdk\addons\jakarta-ant-1.5\bin\\..\lib\ant.jar"/>
     [java] </classpath>
     [java] ---------------------------------

Although this initialization technique is useful, you shouldn’t use it routinely. Instead, it should be saved for special circumstances, as it makes code a little cryptic to read. Use it only when you really need a special multiline initialization.

Instance attributes aren’t the only kind of attributes in a class. Attributes declared with the static keyword are class-scoped. Class-scoped attributes can be initialized in a manner similar to instance-scoped attributes. Here is a simple case:

package oreilly.hcj.review;
public class InitalizerDemo {
  public static final String NAME = "Initializer Demo";
}

This code works exactly like the instance initializer, except that it is called when the class is loaded into the virtual machine by the ClassLoader. Another difference is that this attribute is final, so it cannot be changed after it is initialized.

Tip

Although I show final class-scoped attributes in the examples, keep in mind that the behavior of class-scoped attributes that are not final is identical to that of final class-scoped attributes.

Like instance initializers, you can call methods in static variable initialization:

package oreilly.hcj.review;
public class InitalizerDemo {
  public static final String ARCH = System.getProperty("os.arch");
}

You can also defer an initialization of a static variable to do something more complex:

package oreilly.hcj.review;
public class InitalizerDemo {
  public static final String USER_HOME;
}

In this code, you have created a constant without giving the constant a value. Perhaps the value you want requires more complex code than a simple assignment. This is where the special static{} method comes in. The ClassLoader automatically invokes all static{} initializer methods when the class is loaded into the virtual machine. It is in this method only that you can initialize final static variables that are not initialized on their declaration line. You can use this method in a similar manner as the method-based instance initializers:

package oreilly.hcj.review;
public class InitalizerDemo {
  public static final String USER_HOME;

  static {
                   USER_HOME = System.getProperty("user.home");
                 }
}

The variable USER_HOME will be filled with the string version of the user’s home directory from the system properties. The value will then be final for the duration of the life of the virtual machine.

While using the static{} method, it is important to keep order in mind. Since static initializers are called in order of their declaration, you can get into a bit of trouble if you aren’t careful:

public static class Values {
  public final static String VALUE_ONE = "Blue";
    
  static {
    System.out.println("static{} method for One");
    System.out.println(VALUE_ONE);
    System.out.println(VALUE_TWO);  // <= compiler error
  }

  public final static String VALUE_TWO = "Red";
}

In this code, the compiler cannot find VALUE_TWO because it has not been declared at the time when the static{} method was run. Since initializers are processed in order, you get a bug called an illegal forward reference . Get rid of the bug with a coding standard that declares all static variables first, and then declares static methods. This will keep you out of trouble with your compiler (although not necessarily with your programming logic).

While order is important, be aware that you cannot depend on the order of static initialization. Any methods that depend on ordering are likely to end up with some strange results. To make things a bit clearer, let’s intentionally build an example of this problem. Example 1-8 shows the constructed bug.

Example 1-8. Erroneous dependence on static initializer order
package oreilly.hcj.review;
public class StaticOrderDemo {

  public StaticOrderDemo( ) {
  }

  public static final void main(final String[] args) {
  }

  public static class Ranges {
    public static final String[] RANGE_BLUE = { "Sky", "Navy" };

    public static final String[] RANGE_RED = { "Light", "Dark" };

    static {
      System.out.println("static{} method for Ranges");
      System.out.println(Arrays.asList(RANGE_BLUE));
      System.out.println(Values.VALUE_SPECIFIER);
      System.out.println(Arrays.asList(RANGE_RED));
    }
  }

  public static class Values {
    public static final String VALUE = "Blue";

    public static final String VALUE_SPECIFIER;

    static {
      System.out.println("static{} method for Values");
      System.out.println(VALUE);
      System.out.println(Ranges.RANGE_BLUE);
      VALUE_SPECIFIER = Ranges.RANGE_BLUE[1];
    }
  }
}

When the example is run, the static nested class Ranges is initialized first. As a result, you will see the following output:

>ant -Dexample=oreilly.hcj.review.StaticOrderDemo run_example
run_example:
     [java] static{} method for Values
     [java] Blue
     [java] static{} method for Ranges
     [java] [Sky, Navy]
     [java] null
     [java] [Light, Dark]
     [java] [Ljava.lang.String;@1e3118a
     [java] Class oreilly.hcj.review.StaticOrderDemo$Values Loaded

Note especially the null. Since Values.VALUE_SPECIFIER had not been initialized when it was used in the Ranges initializer, its value was null at the time of the System.out.println( ). Only microseconds later, when Values finishes initializing, it is no longer null. This kind of bug is very difficult to find because of its very transient life. The moral of this story is that you can never depend on the initialization order of static members.

Tip

Although this example was specifically constructed for this book, I actually encountered this bug in production code several times. The first time resulted in an overnight programming frenzy to try to find and correct the bug.

To summarize, there are many kinds of initializers, which are all useful for setting values prior to object construction, but beware of the little gotchas with static initializers. In Example 1-9, all of the possible initializers are shown in one class.

Example 1-9. All the various initializers together
package oreilly.hcj.review;
public class InitalizerDemo {
  /** Simple static initialization. */
  public static final String NAME = "Initializer Demo";

  /** Initialized static on one line. */
  public static final String ARCH = System.getProperty("os.arch");

  /** Static method based initialization. */
  public static final String USER_HOME;

  static {
    USER_HOME = System.getProperty("user.home");
  }

  /** Simple instance member initialization. */
  public String description = "An initialized member";

  /** Method call instance member initialization. */
  public long timestamp = System.currentTimeMillis( );

  /** Complex instance member initialization. */
  private String xmlClasspath;
  {
    final StringBuffer buf = new StringBuffer(500);
    final String classPath = System.getProperty("java.class.path");
    StringTokenizer tok =
      new StringTokenizer(classPath,
                          System.getProperty("path.separator"));
    buf.append("<classpath>\n");
    while (tok.hasMoreTokens( )) {
      buf.append("  <pathelement location=\"");
      buf.append(tok.nextToken( ));
      buf.append("\"/>\n");
    }
    buf.append("</classpath>\n");
    xmlClasspath = buf.toString( );
  }
}

One final thing to remember about all initializers is that you cannot throw any exceptions other than subclasses of RuntimeException within the initializer. All other exceptions will cause all sorts of compilation errors. Therefore, if you want to use methods that throw other types of exceptions, you will have to wrap up any possible exceptions in a try and catch block and then throw a RuntimeException; of course, you’re caught up on chained exceptions by now, so this is no problem. With this arsenal of initializers at your command, you should be ready to tackle setting defaults for any type of class you can think of.

Access Issues

When you look through the many Java books that are available, they all talk about access restrictions. The words private, protected, and public are some of the first keywords that a newbie Java programmer learns. However, most of these books discuss access restrictions only with regards to the impact of restrictions on the code.

By now, you should know what a private method is and the difference between private and protected. Therefore, I won’t bother rehashing this familiar territory. Instead, I would like to take your understanding of access restrictions to another level. Instead of focusing on what they do, I will focus on which to use in various situations.

Preferred Restrictions

While writing Java programs, many programmers fall into a definable pattern. All attributes are private, all interface methods are public, and all helper methods are private. Unfortunately, this causes a ton of problems in the real world. Consider the following common GUI code:

package oreilly.hcj.review;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

public class SomeDialogApp extends JDialog implements ActionListener {
  
  private JButton okBtn = null;
  private JButton someBtn = null;

  //  . . . etc.

  public SomeDialogApp( ) {
    setJMenuBar(buildMenu( ));
    buildContents( );
  }

  public void actionPerformed(final ActionEvent event) {
    Object source = event.getSource( );
    if (source == this.okBtn) {
      handleOKBtn( );
    } else if (source == this.someBtn) {
      handleSomeBtn( );
    }

    //  . . . etc.
  }

  private void buildContents( ) {
    this.okBtn = new JButton("OK");
    this.okBtn.addActionListener(this);
    this.someBtn = new JButton("Something");
    this.someBtn.addActionListener(this);
    //  . . . etc.
  }

  private JMenuBar buildMenu( ) {
    JMenuBar result = new JMenuBar( );

    //  . . . add items and menus
    return result;
  }

  private void handleOKBtn( ) {
    // handler code
  }

  private void handleSomeBtn( ) {
    // handler code
  }
}

In this code, the attributes are all private. There are also four private helper methods: handleSomeBtn( ), handleOKBtn( ), buildContents( ), and buildMenu( ). Everything in this class is okay until someone wants to modify the class. For example, what if I only want to change the functionality of the handleOKbtn( ) method and reuse the rest of the class? In this case, I would basically have to reimplement the entire dialog. Accessing the button instance itself is impossible, so I wouldn’t be able to rebuild the actionPerfomed( ) method. Furthermore, since the helper method is private, I can’t access that either. Time to reinvent the wheel.

On the other hand, if all those helper methods were protected instead of private, I would be able to simply redefine the meaning of the handleOKBtn( ) helper method and reuse the entire class.

When developing classes that others will use, you can never be sure what they will want to do to the class. But if your goal is to promote reuse, making the helper methods protected allows the users to extend the class. Also, since protected blocks users attempting to use the method directly, you won’t be giving up any security on that front. On the other hand, those inheriting from your class will have access to these helper methods. However, the general assumption you should be making is that people extending the class generally know what they are doing. Even if they don’t, the worst they can do is break their derived class.

Tip

I came hard up against this problem recently when trying to extend the Introspector class to perform some functionality. I merely wanted to redefine one method. However, because that method was private, and Introspector is implicitly final (which we will discuss in the next chapter), I couldn’t do it.

In the end, you are better off using private for attributes—public for public methods, and protected for helper methods. This gives your class maximum reusability without breaking encapsulation.

Friends Allow Unrestricted Access

Most access permissions are clearly laid out in the class file. However, many Java programmers don’t understand the access permissions as they are related to instances of the same class. I call these instances friend instances.

In real life, friends don’t allow unrestricted access. You can rarely borrow a friend’s car without asking. Java is much more friendly and trusting. In Java, instances of the same class are friends and give unrestricted access to all of their properties and methods. This unlimited access is a serious hazard that must be carefully avoided while making solid code. The class in Example 1-10 shows how friend instance access works.

Example 1-10. Demonstration of friend instance access
package oreilly.hcj.review;
public class FriendAccess {
  private int value;

  public FriendAccess(final int value) {
    setValue(value);
  }

  public void setValue(final int value) {
    this.value = value;
  }

  public int getValue( ) {
    return value;
  }

  public void someMethod(final FriendAccess obj) {
    if ((obj.value == 5) && (this.value == 5)) {
      obj.value = 25;  // <= Ouch, this works and bypasses the setter.
    }
  }
}

In someMethod, different instances of the same class have complete access to each other. This can become a problem when you modify the class a little bit. Hack on the class until you end up with this variation, which demonstrates the dangers of friend access:

package oreilly.hcj.review;
public class FriendAccess {
  private int value;

  public FriendAccess(final int value) {
    setValue(value);
  }

  public void setValue(final int value) {
    if (value > 10) {
                     throw new IllegalArgumentException( );
                   }
    this.value = value;
  }

  public int getValue( ) {
    return value;
  }

  public void someMethod(final FriendAccess obj) {
    if ((obj.value == 5) && (this.value == 5)) {
      obj.value = 25; 
    }
  }
}

In this variation, the range-checking code that is emphasized in the setValue( ) method was added. Now, if you look back at someMethod( ), you see that the method body sets the property value of the instance passed and completely bypasses the setter. The problem is that you set value to something that the setter would have rejected as illegal. However, since you have access to the variables directly, you can make the change and get away with it. Later, when some other class uses the object, expecting its value to be less than or equal to 10, your code will explode. Fortunately, you can easily block this with another edit to the code:

package oreilly.hcj.review;
public class FriendAccess {
  private int value;

  public FriendAccess(final int value) {
    setValue(value);
  }

  public void setValue(final int value) {
    if (value > 10) {
      throw new IllegalArgumentException( );
    }
    this.value = value;
  }

  public int getValue( ) {
    return value;
  }

  public void someMethod(final FriendAccess obj) {
    if ((obj.value == 5) && (this.value == 5)) {
      obj.setValue(25); // <= IllegalArgumentException
    }
  }
}

If you write your class this way, you generate a lot of overhead due to the call to the setter. However, you also reap the benefits of having your setters work and your debugging time massively reduced. The bug in the above class should take any competent programmer about 10 seconds to fix once he sees the RuntimeException.

Directly setting member variables of a different instance is a problem waiting to happen and should be avoided. The compiler won’t stop you from doing it—then again, a gun won’t stop you from shooting yourself in the foot either.

In fact, I wouldn’t even advise setting properties directly in the same instance. Properties that use setters and getters are special little creatures with their own needs. Many of the setters perform logic checks or do other tasks that you may miss if you set them directly in other utility methods. (See Example 1-11.)

Example 1-11. A bean that uses friendship to bypass setters
package oreilly.hcj.review;
public class FriendBean extends MutableObject {
  private int value;

  public FriendBean( ) {
    super( );
  }

  public void setValue(final int value) {
    if (value > 10) {
      throw new IllegalArgumentException( );
    }
    int oldValue = this.value;
    this.value = value;
    propertyChangeSupport.firePropertyChange("value", oldValue, value);
  }

  public int getValue( ) {
    return value;
  }

  public void someMethod( ) {
    if (Calendar.getInstance( ).get(Calendar.DAY_OF_WEEK) ==
        Calendar.THURSDAY) {
      this.value = 25;
    }
  }
}

One problem with this code is that someMethod( ) sets the property value directly while bypassing the setter and the property change event. If any GUI objects are registered as property change listeners, they won’t know about the change and will display stale data. To fix this, you could potentially alter the method so that the method fires the property change event.

This will work, but, of course, you will have to replicate all of the other logic in the setter method whenever you set the value in a utility method. To make matters worse, whenever you add new logic to the setter, you must also remember to add the same logic to someMethod( ). This would be extremely bad practice in object-oriented, or even procedural, software engineering. If you use the setter for the property, as the variation below shows, your life will be much easier:

package oreilly.hcj.review;
public class FriendBean extends MutableObject {
  private int value;

  public FriendBean( ) {
    super( );
  }

  public void setValue(final int value) {
    if (value > 10) {
      throw new IllegalArgumentException( );
    }
    int oldValue = this.value;
    this.value = value;
    propertyChangeSupport.firePropertyChange("value", oldValue, value);
  }

  public int getValue( ) {
    return value;
  }

  public void someBetterMethod( ) {
    if (Calendar.getInstance( ).get(Calendar.DAY_OF_WEEK) ==
        Calendar.THURSDAY) {
      this.setValue(25);
    }
  } 
}

In this variation of your class, you reuse the functionality of the setter by calling the setter whenever you want to change the property’s value. This approach is far easier to maintain and to change if the need arises; you have to change only the setter and not any other code. Overall, you should not be setting the value of properties, even in the same instance, without using the setters in the class. In fact, if a property has a setter, that setter should be the only thing that ever alters that property. In addition to preventing possible bugs, this technique implements proper encapsulation.

Common Mistakes

There are a certain group of mistakes in Java programming that are made over and over again at hundreds of companies throughout the world. Knowing how to avoid these mistakes will make you stand out from the crowd and look like you actually know what you are doing.

System Streams

The Java System streams represent the ability to write to the console or to read from it. When you invoke a method such as printStackTrace( ) with no arguments, its output is written to the default System stream, which is usually the console that started the program. However, these streams can cause problems in your code. Consider the following from a hypothetical GUI:

public void someMethod( ) {
  try {
    // do a whole bunch of stuff
  } (catch Exception ex) {
     ex.printStackTrace( );
     throw new MyApplicationException( );
  }
}

To debug this GUI, you print the stack trace if something goes wrong. The problem is that the code will print the stack trace to the console window, which may be hidden, or even running on another computer.

Printing to the console window is iffy, at best, in enterprise Java. In fact, there are times when you cannot use the console at all, such as when you write EJB code. At other times, you may be writing a library for others to use, and not have any idea of what the runtime environment is. Therefore, since one of your prime goals should be to promote reusability, you cannot count on the console always being around. The solution to the problem is to keep throwing those exceptions.

In JDK 1.4, there is a new facility that will tell you if one exception caused another. This is called the Chained Exception Facility . In short, if you throw an exception inside of a catch block, the virtual machine will note the exception that you are throwing, along with the exception and stack trace that caused you to enter the catch block in the first place. For more information on this facility, consult the JDK documentation at http://java.sun.com/j2se/1.4.1/docs/relnotes/features.html#chained-exceptions. The basics of using the chained exception facility are illustrated in this code block:

public void someMethod( ) {
  try {
    // do a whole bunch of stuff
  } (catch IllegalAccessException ex) {
    throw new MyApplicationException( );
  }
}

This version of the method takes advantage of chained exceptions by responding to the initial IllegalAccessException by throwing a new exception (MyApplicationException). The exceptions will keep propagating through the application until some code decides to catch them. When you finally do print the trace (presumably through a GUI, rather than a console window!), you will see that MyApplicationException was caused by IllegalAccessException, and the correct stack trace will be indicated.

Feel free to use the System streams in main( ) methods and in other classes in which you control their runtime environment. While you wouldn’t want to use it in your application’s data model classes, it may be acceptable for a GUI’s frame window class.

When logging errors and debugging traces are inside a program or library, a much better solution is to use a logging package, which is much more robust. The Log4J package from Jakarta (http://jakarta.apache.org/log4j/docs/index.html) will allow you to print your exception messages in a configurable way, as shown in Example 1-12.

Example 1-12. Log4J as an alternative to the System streams
import org.apache.log4j.Logger 

class SomeClass {

  /** Logger for this class. */
  private final static Logger LOGGER = Logger.getLogger(SomeClass.class);

  public void someMethod( ) {
    try {
      // do a whole bunch of stuff
    } (catch Exception ex) {
      LOGGER.error("Failure in SomeMethod", ex);
      throw new MyApplicationException( );
    }
  }
}

The benefit to the altered approach shown here is that you can route the output to whichever stream you desire. You can send the output to an XML file, relay it to another application via JMS, create an email to the system administrator, or even dump the information to /dev/null (though there’s really no good reason to ever do this!). Using a logging package gives you far more flexibility than Java’s System streams could ever provide.

Ultimately, using the System streams is okay only if you are writing a console-based application; for error handling, I wouldn’t advise them at all. Also, tools and libraries should never write directly to System streams. Instead, they should pass errors and exceptions up to application-specific code that will do the logging for them.

System.exit( )

Every now and then you will encounter a third-party library that has code such as the following:

if (someSeriousErrorCondition) {
  System.exit(-1);
}

This code looks really benign, but watch out! It’s a wolf in sheep’s clothing. The problem here is that if you are a user of this library, you may not want the entire application to close because of this error. For example, if you are using this library as a part of a plug-in to another product, you should have only the plug-in crash and not the entire tool platform.

However, System.exit( ) crashes the entire application in an exceedingly brutal and bloody fashion. Just imagine the look of surprise, consternation, and growing anger on the face of your users when they try to run the plug-in, and their application simply exits without bothering to save two hours’ worth of data. Although it sounds funny, it could lead to sore feet from looking for another job.

If you use System.exit( ) at all, your best bet is to use it only in the main method. If you ever write a library and embed System.exit( ) into it, you probably deserve any resulting physical violence.

Tip

If you think this sort of thing doesn’t happen in real life, you are in for a brutal surprise. While developing a scripting support plug-in for Eclipse, I found that the Jython libraries embed System.exit( ) in their code. This resulted in a very long debugging session in which I tried to figure out what the heck I did to get Eclipse to simply die. One more bug report filed; at least I’m an expert at Bugzilla now.

Default Execution

One common mistake I see developers make is shown in the following code:

if (source == yesBtn) {
  processYesBtn( );
} else if (source == noBtn) {
  processNoBtn( );
} else {
  processCancelBtn( );
}

In this code, the developer has three buttons in his panel. The if structure is designed to process the buttons if the user clicks on them. If the user clicks on yesBtn, then the first branch will be executed; if the user clicks on noBtn, then the second branch will be executed. Otherwise, the user must have clicked on cancelBtn.

However, there is one small problem. A junior programmer added a new button and registered the panel as an action listener. Unfortunately, he forgot to add handler code for the button. Now, when the user presses Load Favorites, the dialog simply doesn’t work. The user subsequently files a bug report that reads something like, “When I try to load favorites, it doesn’t work.” However, your JUnit test works fine (because you don’t depend on the action handling).

Now you get to spend 12 hours stepping through all 2,000 lines of code related to loading favorites. Eventually, you will detect the missing handler and feel an irresistible compulsion to toss your computer out the window.

Close that window and implement a coding standard instead. Change your if statements to look like the following:

if (source == yesBtn) {
  processYesBtn( );
} else if (source == noBtn) {
  processNoBtn( );
} else if (source == cancelBtn) {
  processCancelBtn( );
} else {
                 assert false : source.toString( );
               }

Those of you who studied the Section 1.2.5 probably saw this coming. The point I am trying to make here is to never use else clauses or default blocks in switch statements to perform specific tasks without checking your assumptions. If your if-then-else statement or switch statement is dealing with an enumerated set of values, go ahead and enumerate them; in your default or final else clause, throw an error.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required