Contents
Copyright © 1997 Sun Microsystems, Inc. "Java" is a trademark of Sun Microsystems, Inc. This document is covered by the full copyright and trademark declarations in The Java Language Specification. This document is excerpted from the fourth printing of The Java Programming Language by Ken Arnold and James Gosling, Addison-Wesley, 1996, ISBN 0-201-63455-4.

Appendix D

Changes for Java 1.1

No unmet needs exist and...current unmet needs that are being met will continue to be met.

-- Transportation Commission on Unmet Transit Needs, California

A newer version of Java has arrived since the original publication of this book. Called the Java 1.1 Platform (hereafter simply "Java 1.1"), it adds some language features, a few packages, and many classes to existing packages. The goals of Java 1.1 are to support internationalization better, fix the incongruities of the original abstract window toolkit package (java.awt, or just "AWT" for short), complete functionality that was missing or impoverished, and add several useful packages. This book does not cover AWT, so we do not cover it in this appendix.

This appendix does summarizes the other changes. For extensive changes, especially those having to do with new classes and packages, you will have to use the reference documentation shipped with your Java 1.1 system to supplement this appendix. The section titles in this appendix correspond to the chapters of this book where the new features would appear.

D.1 Classes

D.1.1 Inner Classes

Classes and interfaces can be nested inside other classes. Such classes, called inner classes, should be ones that exist only to support the work of the class in which they nest. For example, the sole purpose of the SortMetrics class on page 75 is to return multiple values from the metrics method of the SortDouble class. It is a good candidate to be an inner class:

    abstract class SortDouble {
	static final class SortMetrics implements Cloneable {
	    // ... the rest of SortMetrics (see page 75)
	}
	// ... the rest of SortDouble (see page 73)
    }

An inner class name is qualified by its enclosing class. In this case, the proper name of SortMetrics is SortDouble.SortMetrics, and so its declaration in SimpleSortDouble on page 76 would be

    SortDouble.SortMetrics metrics = bsort.sort(testData);
Inner classes and interfaces can use the same access modifiers as other members of a class. A class that was used only in internal data structures might be marked private; one intended only for use by subclasses would be protected. Inner classes can also be public or package accessible.

The example above declares SortMetrics as static. An inner class that is not static has an implicit reference to its enclosing object, which is the object that created the inner class object. This reference is useful because many inner classes are strongly tied to particular objects, and need to access their fields. For example, the class EnumerateWhichChars on page 222 is used to implement the Enumeration interface for the WhichChars class, and could be nicely rewritten to look like this:

    public class WhichChars {
	private BitSet used = new BitSet();     // in original
	private class Enumerate implements Enumeration {
	    private int pos = 0;
	    private int setSize = used.size();

	    public boolean hasMoreElements() {
		while (pos < setSize && !used.get(pos))
		    pos++;
		return (pos < setSize);
	    }

	    public Object nextElement()
		throws NoSuchElementException
	    {
		if (hasMoreElements())
		    return new Character((char)pos++);
		else
		    throw new NoSuchElementException();
	    }
	}

	public Enumeration characters() {       // from page 223
	    return new Enumerate();
	}
    }

We use the shorter class name Enumerate because the class is already nested inside WhichChars, so EnumerateWhichChars would be redundant. The class is private so that even other classes in the package can't use the type directly-it is purely an implementation detail of the WhichChars class.

In the original code on page 222, used was passed as a parameter to the class's constructor, which stored the reference in a field of its own called bits. Here the code for Enumerate directly accesses the field used of its enclosing object. When resolving identifiers, the inner class's enclosing object (also called its enclosing instance) is searched for fields and methods after the class's own this. Because Enumerate does not have a field named used, the identifier used in the inner class refers to the enclosing object's field of that name. The enclosing object for an inner class is the this reference of the method that created the inner class object. In our example above, when an Enumerate object is created inside a particular WhichChar object, the Enumerate object's enclosing object is set to be the this reference of the WhichChar method that created it. This means you cannot create an instance of a non-static inner class in a static context (inside a static method, a static block, or as an initializer for a static field).

You can get a reference to an enclosing object using its class name. For example, code in an Enumerator object could get a reference to its enclosing object using WhichChar.this.

D.1.2 New Uses for final

Method parameters and local variables can be declared final. If you do not expect to change the value of a parameter or variable inside the method, you can declare it final to let the compiler enforce that. The compiler can also optimize uses of a final parameter or variable since it knows the value will never change.

The final-ness of a parameter is not part of the method signature-it is simply a detail of the implementation. A subclass can override a method and add or drop any final parameter modifiers you wish. You can also add or drop final modifiers in a method's parameters without causing any harm to existing compiled code that uses that method. The final declaration does not show up in the documentation generated from doc comments.

You can defer initialization of a final field or variable, as long as you initialize it before it is used and assign a value to it exactly once. The compiler will check for proper assignment, as will the verifier before code is executed. Deferred initialization can be useful when the proper value can only be calculated by a loop or other code that is hard or impossible to encode in a variable initializer, such as code that throws exceptions that must be caught and handled.

D.1.3 Object Initializers

You can have arbitrary blocks of code that are executed after the superclass constructor is executed, but before the class's own constructor body is executed. Such blocks are analogous to static blocks described on page 44, except the keyword static is left off.

D.2 Extending Classes

D.2.1 Anonymous Classes

When you are writing simple subclasses or implementations of interfaces, creating a bunch of classes for each trivial class can be awkward. Anonymous classes are a convenient short form of inner classes that have no name, only an implementation that is specified right along with the new. Suppose you want to create a simple Observer object (see page 233) that keeps a history of the events stored in a field of your object. The following would do this directly and simply:

    private Vector history = new Vector();

    public void watch(Observable o) {
	o.addObserver(new Observer() {
	    public void update(Observable o, Object arg) {
		history.addElement(arg);
	    }
	});
    }

The addObserver has an embedded new of an anonymous class that implements the Observer interface, implicitly extending Object. This anonymous class implements update by adding an element to the enclosing object's history vector. The compiler creates an unnamed (hence "anonymous") class that overrides update as specified. The end of the anonymous class declaration is the end of the allocation expression started by the new. Here we simply close off the call to addObserver with a closing parenthesis.

An anonymous class can extend a class instead of implementing an interface, in which case it is an unnamed subtype of that class. Such a new can invoke any superclass constructor. Anonymous classes cannot have their own constructors.

Anonymous classes are a quick, terse subclassing tool that is useful in certain situations, such as creating several types of AWT buttons, each with its own small action, or creating simple Runnable objects. Unfortunately, they share a common failing of many terseness features-it is trivial to write code that is torture to read. You should use of anonymous classes only for tiny classes that override one, or at most two, methods, with a total of four lines of code or fewer. Otherwise people who read your code can become confused trying to keep track of what piece of which class's method's code they are reading, and what the outer context is. Use this tool sparingly. When it's good, it's very, very good. When it's not, it's awful.

D.2.2 New Hashing Method

The default implementation of Object.hashCode is to return keys that are likely to be different for different objects. As described on page 64, many classes override both hashCode and equals to provide different notions of equality. However, sometimes you need the original notion of equality, in which all objects are different, even for an object that usually is used with a broader notion. You can use == to test if two objects are the same, but you will need to use System.identityHashCode to hash the objects, since it preserves the default implementation of hashCode in which all objects are considered different.

D.3 Tokens, Operators, and Expressions

Documentation comments ("doc comments") contain @ tags (such as @author and @see) that are not documented in this book. The new @deprecated tag has particular meaning-it marks a class, interface, field, or method as not recommended for continued use. Existing code that uses the deprecated entity will still compile and run, but the compiler will generate a warning, suggesting that you update your code to avoid that entity. You should follow that recommendation.

To have this effect on a type or a member of a type, the @deprecated tag must be at the beginning of a doc comment line (ignoring white space and any * character). This is the only place in the entire Java language where the contents of a comment affect the generated code. The @deprecated tag should always refer readers to the preferred replacement(s). For example

    /**
      * @deprecated	This call has been replaced with dwishm
      * @see dwishm
      */
    public void dwim() { /*...*/ }
The transient keyword, marked as "unused" on page 93, now has defined meaning; see Section D.5.2.

You can initialize the contents of an array when you new it. For example, the following would be a flexible way to create an array of strings:

    String[] martians = new String[] {
			    "Gidney", "Cloyd"
			};

D.4 Threads

The interruption mechanism briefly described on pages 173-174 as future functionality has been implemented in Java 1.1. See those pages for more details.

D.5 I/O

D.5.1 Internationalization

Many new I/O classes have been added to handle full internationalized character issues. The original classes really worked only with ISO-Latin-1 8-bit characters (notice that InputStream.read, for example, returned eight bits, not sixteen, in its int). The most specific change is that PrintStream is now a deprecated class, since it thinks only in terms of ISO-Latin-1. The new class is called PrintWriter, and provides all the methods of PrintStream except those for writing raw bytes. Existing code that uses the print and println methods of PrintStream will have the output translated to the local character set. (To prevent a massive outpouring of deprecation warnings, only the constructors of PrintStream are deprecated.)

The autoflushing of PrintStream described on page 199 has been cleaned up. If autoflush is turned on, any newline anywhere in the output causes a flush, as does any write of a byte array. If autoflush is off, no automatic flushing is done.

PrintWriter is a subclass of the abstract Writer class, which is the parallel to OutputStream for classes that understand internationalization. The Reader abstract class is the parallel to InputStream. There are several new Reader and Writer classes for internationalized I/O.

The fields System.in, System.out, and System.err are final in Java 1.1 for security reasons, so code that modifies these fields (like that shown on page 197) will not work.

The new package java.text includes many classes useful for parsing and producing internationalized text. It includes classes for date and number formatting, comparing and sorting strings in a locale-sensitive way, splitting up native language text, and other locale-related I/O.

D.5.2 Object Serialization

The streams ObjectInputStream and ObjectOutputStream help you read and write entire objects and object graphs. Objects written to an ObjectOutputStream using writeObject generate a stream of bytes that, when read by an ObjectInputStream with readObject, create a full copy of the original objects. The process of creating such a stream of bytes is called serialization. Serialized copies are deep-you can serialize an entire graph of objects, and when you deserialize the generated bytes, you will get a full copy of the original graph. If two or more objects refer to a particular object in the original graph, a deserialized copy will have copies of those two or more objects that refer to a copy of that same particular object.

The byte stream encodes the state of the serialized object, including private fields. Therefore any serializable object can have the values of its private fields examined by someone who serializes the object and reads the generated bytes. Some programmers view private fields as secret, and would not be pleased to have values exposed in this way. For this reason, objects are not serializable unless they implement the Serializable interface. Serializable is an empty interface that simply marks the object as one that can be serialized. The default serialization mechanism just writes the non-static, non-transient fields of an object. You can override this by providing readObject and writeObject methods.

A class that instead implements the interface Externalizable-a subinterface of Serializable-is serializable but must provide custom readExternal and writeExternal methods; the default serialization will not be used.

D.6 Utilities

D.6.1 Resource Localization

Several new utilities have been added for localization. A new Locale class describes particular locales to define a user's preferred language and other properties. A ResourceBundle superclass is provided to customize a set of resources (such as messages that might be displayed to the user) based on the user's preferred locale. Some subtypes of ResourceBundle are provided: ListResourceBundle, an abstract class that provides a simple implementation of ResourceBundle to which you must simply provide the list of resources and keys for each resource, and PropertyResourceBundle, which uses files that contain the keyed resources.

D.6.2 Dates and Times

The functionality of the Date class has been split up into a richer system that can cope with varying reckonings of time. A Date object now only represents a particular moment in time with millisecond granularity. Its other constructors and methods previously performed two other duties: formatting strings and viewing a moment in time as year, month, day, hour, minute, and second values. All these methods are deprecated in favor of:

These abstract classes allow flexibility, but almost everyone will need to work with Gregorian dates that are used in most of the world and in international commerce, so Java 1.1 also provides:

    class CurrentYear {
	public static void main(String[] args) {
	    GregorianCalendar now = new GregorianCalendar();
	    int year = now.get(Calendar.YEAR);
	    System.out.println("The year is " + year);
	}
    }

D.7 Programming with Types

D.7.1 Reflection

The package java.lang.reflect provides a reflection mechanism for Java. This is a tool for examining classes fully. A Class object can now return a list of all the public methods and fields of the related class. You can use the Method objects returned to invoke methods, and the Field objects to get and set field values. A Class object can also return a list of all members, including non-public ones, but security restrictions usually prevent doing this.

You should avoid the temptation to use reflection when other tools more natural to the language would suffice. If you are accustomed to using function pointers in another language, for example, you might think that using Method objects are a natural replacement, but usually an object-oriented tool-such as an interface that is implemented by objects that perform the needed action-is better. Reflection is intended for use by language tools such as debuggers and class browsers.

D.7.2 Wrapper Classes

Two new wrapper classes have been added: Short and Byte. These are subtypes of Number. A new Void class has also been added for completeness, and is used by the reflection methods to represent void return types.

D.7.3 Getting the Class Object for a Named Class

You can get the Class object for a given type with a new use of the class keyword:

    Class threadClass = Thread.class;
    Class intClass = int.class;
    Class voidClass = void.class;

D.8 Native Methods

The API for writing C implementations of Java methods is now completely different. The mapping has been regularized, and is less tied to a particular implementation of the Java Virtual Machine. The general comments about native method mappings in Appendix A are still valid, but the specific details described, while still supported, are strongly deprecated. The new Java Native Interface (JNI) is much easier to learn and to use.

D.9 New Packages

Java 1.1 has several new packages: