By Robert Simmons, Jr.
Book Price: $39.95 USD
£28.50 GBP
PDF Price: $31.99
Cover | Table of Contents | Colophon
package oreilly.hcj.review;
public class PointersAndReferences {
public static void someMethod(Vector source) {
Vector target = source;
target.add("Swing");
}
}
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 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.
}
}
}
}
}
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.
}
}
}
}
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.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.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.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
}
}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( );
}
}
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 final
can be instrumental in turning
thousands of logic errors into compiler errors without too much
effort. With some training in coding standards and some code
retrofitting, you can save an enormous amount of man-hours that are
better spent elsewhere. Also, you can save your support departments
from having to deal with irate customers.package oreilly.hcj.finalstory;
public class FinalConstants {
public static class CircleTools {
public double getCircleArea(final double radius) {
return (Math.pow(radius, 2) * 3.141);
}
public double getCircleCircumference(final double radius) {
return ((radius * 2) * 3.141);
}
public double getCircleExtrudedVolume(final double radius,
final double height) {
return ((radius * 2 * height) * package oreilly.hcj.finalstory;
public class FinalConstants {
public static class CircleTools {
public double getCircleArea(final double radius) {
return (Math.pow(radius, 2) * 3.141);
}
public double getCircleCircumference(final double radius) {
return ((radius * 2) * 3.141);
}
public double getCircleExtrudedVolume(final double radius,
final double height) {
return ((radius * 2 * height) * 3.141);
}
}
}
3.141, his estimate
for π, in all three methods if he wants to make his
calculations more precise. Seasoned developers will see the
opportunity for a
class-scoped constant, as seen in
Example 2-2.package oreilly.hcj.finalstory;
public class FinalConstants {
public static class CircleToolsBetter {
/** A value for PI. **/
public final static double PI = 3.141;
public double getCircleArea(final double radius) {
return (Math.pow(radius, 2) * PI);
}
public double getCircleCircumference(final double radius) {
return ((radius * 2) * PI);
}
public double getCircleExtrudedVolume(final double radius,
final double height) {
return ((radius * 2 * height) * PI);
}
}
}
final variables, you should keep in mind that
these variables don't have to be primitives to be
useful. Final variables that are scoped and constructed can be used
as a powerful tool to solidify code in methods.final variables that appear within methods are a
little strange to some people at first, they become quite addictive
once you get used to reading them. See Example 2-5.package oreilly.hcj.finalstory;
public class FinalVariables {
public static String someMethod(final String environmentKey) {
final String key = "env." + environmentKey;
System.out.println("Key is: " + key);
return (System.getProperty(key));
}
}
final variable
that adds a prefix to the parameter
environmentKey. In this case, the
final variable is final only within the
execution scope
, which is different at each execution of
the method. Each time the method is entered, the
final is reconstructed. As soon as it is
constructed, it cannot be changed during the scope of the method
execution. This allows you to fix a variable in a method for the
duration of the method. To see how this works, use the test program
in Example 2-6.package oreilly.hcj.finalstory;
public class FinalVariables {
public final static void main(final String[] args) {
System.out.println("Note how the key variable is changed.");
someMethod("JAVA_HOME");
someMethod("ANT_HOME");
}
}
>ant -Dexample=oreilly.hcj.finalstory.FinalVariables run_example
run_example:
[java] Note how the key variable is changed.
[java] Key is: env.JAVA_HOME
[java] Key is: env.ANT_HOME
finals. To illustrate,
suppose you hire a new developer and, while adding a new feature, he
decides to make a little change to the equation2(
) method from Example 2-4. The changes he
makes are shown in Example 2-9.package oreilly.hcj.finalstory;
public class FinalParameters {
public double equation2(double inputValue) {
final double K = 1.414;
final double X = 45.0;
double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
double powInputValue = 0;
if (result > 360) {
powInputValue = X * Math.sin(result);
} else {
inputValue = K * Math.sin(result);
}
result = Math.pow(result, powInputValue);
if (result > 360) {
result = result / inputValue;
}
return result;
}
}
if
statement, the developer made one little mistake—he typed
inputValue instead of
powInputValue. This caused errors in the
subsequent calculations in the method. The user of the function
expects certain output and doesn't get it; however,
the compiler says that everything in the code is okay. Now
it's time to put on another pot of coffee and hope
your spouse remembers who you are after you figure out this rather
annoying problem.final variables for public use. This desire can
lead to all sorts of problems. Consider the code in Example 2-11.package oreilly.hcj.finalstory;
public class FinalCollections {
public static class Rainbow {
public final static Set VALID_COLORS;
static {
VALID_COLORS = new HashSet( );
VALID_COLORS.add(Color.red);
VALID_COLORS.add(Color.orange);
VALID_COLORS.add(Color.yellow);
VALID_COLORS.add(Color.green);
VALID_COLORS.add(Color.blue);
VALID_COLORS.add(Color.decode("#4B0082")); // indigo
VALID_COLORS.add(Color.decode("#8A2BE2")); // violet
}
}
}
Set of final and
static
Colors representing the
colors of the rainbow. You want to be able to use this
Set without concerning yourself with the
possibility of accidentally changing it. The problem is that the
Set isn't
final at all! Break it with Example 2-12.package oreilly.hcj.finalstory;
public final static void someMethod( ) {
Set colors = Rainbow.VALID_COLORS;
colors.add(Color.black); // <= logic error but allowed by compiler
System.out.println(colors);
}
Set is
final, but the Set itself is
mutable. In short, your constant variable isn't very
constant. The point is that final is not
the same as immutable.package oreilly.hcj.finalstory;
public static class RainbowBetter {
public final static Set VALID_COLORS;
static {
Set temp = new HashSet( );
temp.add(Color.red);
temp.add(Color.orange);
temp.add(Color.yellow);
temp.add(Color.green);
temp.add(Color.blue);
temp.add(Color.decode("#4B0082")); // indigo
temp.add(Color.decode("#8A2BE2")); // violet
VALID_COLORS = Collections.unmodifiableSet(temp);
}
}
}final class member that can be very useful is
instance-scoped final attributes. Consider the
code in Example 2-14.package oreilly.hcj.finalstory;
public class FinalMembers {
/** Holds the creation date-time of the instance. */
private Date creationDate =
Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime( );
/**
* Get the Date-Time when the object was created.
*
* @return The creation date of the object.
*/
public Date getCreationDate( ) {
return this.creationDate;
}
}
creationDate is to hold
the date and time of the instance's creation. This
property represents a read-only property that is set once; after all,
an object can be created only once. However, there is a problem with
this property: it leaves a massive potential bug lurking in your
code. To illustrate this, lets look at another part of the same class
in Example 2-15.package oreilly.hcj.finalstory;
public class FinalMembers {
/** Holds the modification date-time of the instance. */
public Date modificationDate = creationDate;
public void setModificationDate(Date modificationDate) {
if (modificationDate == null) {
throw new NullPointerException( );
}
this.creationDate = modificationDate;
}
public Date getModificationDate( ) {
return this.modificationDate;
}
}
setModificationDate( ) method is obviously setting
the wrong parameter. Due to a simple typo, instead of setting
modificationDate, this method sets
creationDate. No one ever intends to write bugs
like this, but it happens. Fortunately, there is a way you can block
this problem with a coding standard:final class is a class that does not allow itself
to be inherited by another class. Final classes mark endpoints in the
inheritance tree.final in the class declaration:public final class SomeClass {
// . . . Class contents
}
private:public class SomeClass {
public final static SOME_INSTANCE = new SomeClass(5);
private SomeClass(final int value) {
}
}
private visibility,
you are implicitly declaring the class as final;
often, this is not the intended result. In fact, it is the omission
of the keyword final on the class declaration that
should alert you to the fact that something is wrong. The class above
may very well need to be final, in which case you should always
specifically use the keyword final in the class
declaration. If you don't follow this rule, you
could end up causing some devious problems.java.beans package,
you will find a class called
Introspector
(see Chapter 8).
Take a look at its single constructor in Example 2-17.
public class Introspector {
// . . . snip . . .
private Introspector(Class beanClass, Class stopClass, int flags)
throws IntrospectionException {
// . . . snip . . .
}
}
Introspector class is
private. I noticed this while studying this class. My goal was to
extend the Introspector and create a class that is
more feature-rich than Introspector itself.
Unfortunately, since the only constructor of the class is private, it
is impossible to extend this class. In the case of the
Introspector class, there is no reason that the
class should be final. The Introspector class is a
good example of how implicit final keyword on the declaration, as shown
in Example 2-20.package oreilly.hcj.finalstory;
public class FinalMethod {
public final void someMethod( ) {
}
}
abstract
keyword. Whereas the abstract keyword declares
that subclasses must override the method, the
final keyword guarantees that the method can
never be overridden by subclasses. Subclasses
can inherit from the FinalMethod class and can
override any method other than someMethod( ).final keyword off a method. After all, you never
know the kinds of variations the users of your class may come up
with.package oreilly.hcj.finalstory;
public class FinalMethod {
/** A demo property. */
private final String name;
protected FinalMethod(final String name) {
this.name = name;
}
public final String getName( ) {
return this.name;
}
}
name property is set at
construction time and can never be changed. Also, you have defined
that you never want a subclass to hide this property (which it could
by declaring its own name property if
getName( ) wasn't final). This is
a good reason to make a method final. By making getName(
) a final method, you can guarantee that
the user of subclasses of this object will always call this method
when she executes getName( ). In the JDK, the
method getClass( ) in
java.lang.Object is final for this very reason.package oreilly.hcj.finalstory;
import org.apache.log4j.Logger;
public class ConditionalCompile {
private static final Logger LOGGER =
Logger.getLogger(ConditionalCompile.class);
public static void someMethod( ) {
// Do some set up code.
LOGGER.debug("Set up complete, beginning phases.");
// do first part.
LOGGER.debug("phase1 complete");
// do second part.
LOGGER.debug("phase2 complete");
// do third part.
LOGGER.debug("phase3 complete");
// do finalization part.
LOGGER.debug("phase4 complete");
// Operation Completed
LOGGER.debug("All phases completed successfully");
}
}
final all over your code. You
should use it so much that not seeing it becomes a rare, if not
completely unknown, occurrence.final heavily whenever you
write or edit code. Also, you should force the junior developers
working for you to adopt the use of final as a
coding standard. They may grumble and balk for a bit, but the coding
standard will quickly become so automatic that the developers
won't consciously think about it.final everywhere you can. Also, when you edit
someone else's code, introduce the
final variable liberally. Doing so helps to
guarantee that no one can mess up your code without actually trying
to do so.
null, the program would crash with
NullPointerExceptions. Since the part of the
program that changed the data object is different from the part that
generated the exceptions, the bug could be very difficult to locate.set methods as well as other
methods that alter the state of the instance. See Example 3-1.package oreilly.hcj.immutable;
public class ImmutablePerson {
private String firstName;
private String lastName;
private int age;
public ImmutablePerson(final String firstName, final String lastName,
final int age) {
if (firstName == null) {
throw new NullPointerException("firstName");
}
if (lastName == null) {
throw new NullPointerException("lastName");
}
this.age = age;
this.firstName = firstName;
this.lastName = lastName;
}
public int getAge( ) {
return age;
}
public String getFirstName( ) {
return firstName;
}
public String getLastName( ) {
return lastName;
}
}
ImmutablePerson class, you allow the user
to pass in all arguments to the constructor and then simply
don't declare any write methods to the attributes.
Once the person is constructed, it looks like it
can't be changed. However, unfortunately, it can be
changed. This type is immutable by all appearances, but there is
actually a hole.String trap is
one of the most prevalent examples of this problem.java.lang.String
. However, many good
developers don't know that String
is immutable. They are fooled by all of the
"operations" that can be done to a
String. They often say, "How can
String be immutable? I can concatenate strings and
replace values." However, these well-meaning
developers are wrong!public void buildSentence (String[] words) {
String sentence = new String( );
for (int idx = 0; idx < words.length; idx++) {
sentence += " " + words[idx];
}
}
for loop, the virtual machine allocates an
entirely new String object; this new object
contains the characters in the sentence variable
concatenated with the word being added. Since the
String object is immutable, a new
String object must be created to reflect each
modification. Assuming that 234,565 words are being added, you may
want to take a long lunch.String objects that this code created
are not purged from memory until garbage collection is run, at which
point the program will hit another speed bump the size of Mt.
Everest. So, on top of a slow program, you have a program that eats
memory like candy.StringBuffer
is changed frequently, so it wouldn't make much
sense as an immutable object.java.util package. Since there are many Java
developers out there that have migrated from other professions, this
is not surprising. Those who haven't taken
university-level data structures courses may find the collections to
be a bit confusing.java.util
package. They are
structured using an interface-implementation model. For each type of
collection, there is one interface and various implementations. For
example, the java.util
package. They are
structured using an interface-implementation model. For each type of
collection, there is one interface and various implementations. For
example, the List interface is implemented by the
AbstractList, ArrayList,
LinkedList, and Vector classes.ArrayList and a
LinkedList are both conceptually lists.java.util package are the
only components that should be exposed to the users of your classes,
unless you have no other choice. This follows from the idea of
encapsulation,
which dictates that the programmer should not expose implementation
details to the user of a class, but only the interface to that class.
For example, the following code is an example of a good
interface-based technique:List, for example, can be implemented a number of
ways, each with advantages and disadvantages. However, before you can
understand the various implementations of Lists
and other collections, you need to understand how collections and
maps determine equality and order.Map decide if two keys are the
same, and how does a SortedSet determine the order
used to sort the objects? To answer these questions, you have to
tackle the basic principles of object equality: identity and
comparability.Set
can't exclude duplicate entries if it
doesn't know how to check to see whether supplied
objects are equal. These collections take advantage of the fact that
all objects in Java descend from the common class
java.lang.Object.Object, the
Set implementations can call the method
equals( )
on all objects. This allows the set to
compare objects. However, there is one catch: for this to be
effective, the object must define (or inherit) a valid implementation
of equals( ).equals( ) compares
two objects to see whether they are the same object in the virtual
machine by identity
, not by
equality. Suppose you create two
Address objects that define the addresses of
employees. Both address objects can contain the same street name,
number, country, postal code, and other data. However, these objects
are two different instances (and therefore reside at two different
memory addresses). In this case, the Map type. If not, then you can use a more general
Collection type. If you decide to use a
Collection type, the next thing you need to ask is
whether you need duplicates in your collection. If so, then you will
need to use a List; otherwise, a
Set should suffice.SortedSets or
SortedMaps only if there is a reason why the
collection should always be sorted in a specific manner. Sorted
collections and maps must maintain the sorting order, so they tend to
be significantly slower than other collections and take up more
memory. You should incur this performance hit only if you need the
functionality.
http://jakarta.apache.org/commons/collections.htmljava.util.Enumeration,
java.util.Iterator, and
java.util.ListIterator interfaces are used to
iterate through data objects in a collection. The collections
themselves are responsible for implementing these interfaces and for
providing the iterator for the caller. They do this using inner
classes.Iterator interface. The
Collection and Map interfaces
require the developer to implement an Iterator but
not an Enumeration. Therefore, you probably
won't use this interface often unless the collection
classes are not available (for instance, during J2ME programming on
some limited profiles).Enumeration, but it
allows you to remove an element from the collection by calling
Iterator.remove( ). However, using
Iterator.remove( ) can cause some rather confusing
code.ListIterator
interface allows you to iterate backwards
in a list as well as forwards. This iterator is available only on
classes that implement the