This is the story of the changes made to Java from the beginning of time up to Java 7. For changes after that, the path continues in the section at the end of my Java Cookbook, from which this material is resurrected, with permission from O’Reilly Media. Links that show up beginning with javacook are left over from when this was part of the book, and refer to sections within that book. The reader is advised to purchas a copy and keep it handy.

Pre-history to Java 7

Java Preview: HotJava

The first time that the world at large heard of Java was the May 1995 release of HotJava, a web browser written entirely in Java and introducing the Java applet for dynamic web content. The download included the first Java compiler, aimed primarily at writing applets, and the source code for much of the system. That year’s SunWorld conference helped escalate this release into the public eye, with the dramatic last-minute announcement by Marc Andreessen of Netscape that Java had been licensed and would be incorporated into all Netscape browsers.

Java Arrives: 1.0

Early in 1996, Java 1.0 was "officially" released, with its API featuring the basic structure that has underpinned all the Javas that have appeared ever since (java.lang, java.io, java.util, and so on).

What Was New in Java 1.1

The first major revision, 1.1, was released in February 1997. It featured the new Readers/Writers classes for reading/writing text in any of the Unicode subsets or "character sets," making Java one of the first languages to support the Unicode standard for Internationalization. Supporting this was the new package java.text and some code in java.util, building upon the Readers and Writers to provide broader support for internationalization; this change introduced Calendar and GregorianCalendar, Format (DateFormat, MessageFormat, NumberFormat), and the Locale and ResourceBundle classes.

In the GUI toolkit AWT, the event model was changed from a single method to handle all events, to the now-universal event listener model used on all GUI toolkits invented since. To help support event listeners, inner classes were added to the language, which would remain the preferred way of implementing listeners up until Java 8. Two other significant additions in 1.1 were the JavaBeans conventions and packaging, and the JDBC Database Access in package java.sql, which was to become one of the cornerstones of Java Enterprise Edition (Java EE).

What Was New in Java 2 (Java SDK 1.2)

Java 1.2 was one of the two largest revisions—​including Collections, Swing, and Reflection—​to the Java Platform. So large was this revision that the marketing of this release introduced the term "Java 2" for the Java Platform, which was to stick around until the next of the largest releases, Java 5 and its implementation JDK 1.5. This "Java 2" name is the basis of the now long-defunct names "J2SE" and "J2EE" for the Standard and Enterprise platform editions, respectively. The Java 2 release featured the Collections framework, a unified system for data structuring, introducing the basic Collections structure described in [javacook-structure-collections] with new implementations (and adapting older classes such as Vector and Hashtable to conform to the new interfaces).

It had become obvious that AWT was inadequate for advanced desktop GUI development, because it had been (deliberately) chosen to provide a "least common denominator" approach to GUIs on all the platforms common at the time, including Mac OS 7, OS/2, Windows 95 and Unix—​if any of these platforms didn’t support a feature, it was omitted. And worse—​if different platforms worked differently or had bugs in a given feature, Java inherited those differences and bugs, because AWT always used the native platform components directly. Swing provided a clean break from this tradition, doing all the rendering in Java—​providing consistency and full-featuredness on all platforms—​and providing pluggable "look and feel" implementations to make the GUI components "look right" on each platform.

Swing’s use of application rendering significantly upped the CPU load on the Java Virtual Machine, so this release also introduced the first use of "Just In Time" (JIT) in the JVM, a technique for transforming Java bytecode into native CPU (Intel, SPARC, PowerPC) code for better performance.

Although java.lang.Class was included in 1.0 and gained methods such as getClass() in 1.1, further additions and the new package java.lang.reflect constituted the Reflection API (see [javacook-reflection]), providing a standard way for classes to examine ("reflect"), instantiate, and manipulate other classes.

Finally, this release also introduced Java Interface Description Language, an interface-like definition of remote methods intended for for CORBA interoperability.

There were a few minor language changes in 1.2, most notably the inclusion of the strictfp keyword to allow non-strictfp floating-point calculations to use greater precision than the 32/64-bit IEEE-754 floating-point standard.

What Was New in Java 1.3

Released in May 2000, Java 1.3 was a smaller, more incremental release. However, it did introduce another core Enterprise API, the Java Naming and Directory Interface (JNDI).

Another of 1.3’s interesting features was the Dynamic Proxy mechanism, which provided official support for dynamically generating proxy objects. To continue 1.2’s gains in performance, the HotSpot JVM was included in the JDK.

Also introduced here were the first well-documented debugger mechanism, the Java Platform Debugger Architecture (JPDA), and JavaSound, an API for controlling sound devices.

What Was New in Java 1.4

This section reviews some of the changes made to Java in 1.4.

Java 1.4 Language Changes

Java SE 1.4 introduced assert keyword, which allows assertion testing; testing can be switched on or off dynamically without recompilation. The intention of assert is to encourage developers to write pre- and post-code assertions to verify correct input and output of methods; how to write unit test-like tests into your code (see [javacook-getstarted-junit]).

Java 1.4 API Changes

Java SE 1.4 introduced a variety of technologies, such as a standardized Java implementation of regular expressions (see [javacook-regex]). Regular Expressions, while borrowed from formal language theory or theoretical computer science, found singularly powerful use in early Unix systems as the basis for almost all pattern-matching commands. Larry Wall extended them with his Perl language (see Learning Perl and Perl Cookbook [O’Reilly]); it is these extended regexes that Java implements (with a few minor changes). See [javacook-regex] for plenty of details on regex.

To facilitate "exception translation" to provide tiering isolation, the platform introduced "exception chaining", the ability to wrap one exception in another; many of the standard Exception types were modified to support this. Recognizing the increasing importance of the eXtensible Markup Language, 1.4 introduced the options covered in [javacook-xml] (the Java API for XML Processing, or JAXP). Finally, recognizing that applets were never going to take over the world, Java’s leaders with this release brought about #javacook-packages-SECT-13.

There were a number of other features added, such as:

  1. #javacook-netserver-SECT-9

  2. #javacook-structure-SECT-7

  3. Security and crypto (JCE, JSSE, JAAS)

What Was New in Java 5

Java 5 (JDK 1.5) is the other "largest" release, containing a variety of changes both in the language and in the APIs.

Java 5 Language Changes

Language changes include:

  • foreach loop

  • Enumeration types (enum keyword)

  • Annotations (metadata; see [javacook-reflection-annotations])

  • Generic Types

  • printf, scanners, and the Scanner classes

  • Variable arguments (varargs)

  • Improved semantics for the Java Memory Model

  • static import

Java 5 foreach loop

You want a convenient means of accessing all the elements of an array or collection. The Java 5 foreach loop syntax is as follows:

for (Type localVar : IterableOfThatType) {
	...
}

For example:

for (String s : myListOfStrings) {
	// use s here
}

This form of for is pronounced as "for each" and is referred to that way in the documentation and the compiler messages; the colon (:) is pronounced as "in" so that the statement is read as "foreach String s in myListOfStrings." The String named s will be given each value from myListOfStrings for one pass through the loop. How is myListOfStrings declared? The foreach construction can be used on Java arrays, on Collection classes, and on anything that implements the Iterable interface. The compiler turns it into an iteration, typically using an Iterator object where Collection classes are involved. ForeachDemo.java shows using foreach to iterate through an array and a List.

Example 1. ForeachDemo.java
		String[] data = { "Toronto", "Stockholm" };
		for (String s : data) {
			System.out.println(s);
		}

		// Show the Java 5 foreach loop - do not modernize to Java 8
		List<String> list = Arrays.asList(data);
		for (String s : list) {
			System.out.println(s);
		}

In modern Java, the foreach loop is used more commonly than the older for loop. The main times when the older style would be used are:

  • When you need the number of each element for calculation or indexing a target array or arrays

  • When you are creating new data using a numeric index

  • When the control involves floating point calculations rather than integer

  • When you want to remove items from the collection during iteration, so you need explicit access to the iterator

Java 5 enums

Enumerations implement the Typesafe Enumeration Pattern described in Josh Bloch’s book Effective Java. Bloch worked at Sun to implement this language feature, among others. The basic idea is that where you have a small, rarely changing set of values, it makes sense to list them and have them known at compile time. For example, the colors at a stoplight are red, amber, and green; we might code this as in Color.java, at a bare minimum.

Example 2. Color.java
public enum Color {
	RED, AMBER, GREEN
}

Enums are covered in [javacook-CHP-8-SECT-5].

Java 5 annotations

Java Annotations are metadata-like "sticky notes" that you can attach to various places in your Java code to provide extra information beyond normal Java syntax and semantics. Some (such as java.lang.Override) are only used at compile time; others—​the majority—​are consulted at runtime. They are described, with examples, in [javacook-reflection-annotations].

Java 5 generic types

One of the most notable additions to Java 5 is "generic types," such as collections, that are defined to hold objects of a certain type rather than just Object (obviating the downcast otherwise needed each time you get an object back from a collection). For example, with a List of Strings, prior to 1.5 you might have written the code in ListsOldAndNew.java.

Example 3. ListsOldAndNew.java
	List myList = new ArrayList();
	myList.add("hello");
	myList.add("goodbye");

	// myList.add(new Date()); This would compile but cause failures later

	for (int i = 0; i < myList.size(); i++) {
		String s = (String)myList.get(i);
		System.out.println(s);
	}

In Java 5, you would be more likely to write this as:

	List<String> myList = new ArrayList<>(); // Java 6: new ArrayList<String>();
	myList.add("hello");
	myList.add("goodbye");

	// myList.add(new Date()); This would not compile!

	for (String s : myList) {	// Look Ma, no downcast!
		System.out.println(s);
	}

This mechanism is called "generics" because it allows you to write generic classes, the arguments and return types of methods of which are specified when the class is instantiated.

Although the original definition of the List interface and the ArrayList class had methods dealing in java.lang.Object, in 1.5 these types have been changed to a Generic type or "type parameter" so that you can declare and instantiate them with any object type (String, Customer, Integer), and get the benefits of stronger type checking and elimination of downcasts.

Not just these types, but all of the Collections API and most of the other parts of the standard Java API in 1.5 have been updated to be generic types. If you write your own multipurpose classes, you can fairly easily change them to be generics in the same fashion.

The notation <type> is used to specify the particular type with which the class is to be instantiated. Java developers had better get comfortable with this notation, because it is used extensively in the 1.5 javadoc!

These additions related to data structuring are all covered in [javacook-structure]. Also see Java Generics and Collections.

Variable argument lists

Java 5 introduced method declarations with variable-length argument lists, commonly called "varargs." This allows you to write a method that can be called with any number of arguments of the given type. For example, a method mySum to be called with a variable number of int arguments can be written as:

	static int mySum(int... args) {
		int total = 0;
		for (int a : args) {
			total += a;
		}
		return total;
	}

Note that there can only be one variable-length argument list in the parameter list of a method, and that it must be last; there can be other arguments before it, but not after.

Any of the following would be a valid call to this method:

		System.out.println(mySum(5, 7, 9));
		System.out.println(mySum(5));
		System.out.println(mySum());
		int[] nums = {5, 7, 9};
		System.out.println(mySum(nums));

That last one may be a bit surprising. When you think about it, the "…​" in a method declaration is a kind of syntactic sugar for "array of," so you can pass an explicit array. This does not mean that arrays and ... are interchangeable; if you declare the method parameter as, say, int[] args, then you will be required to pass an array, not use a variable-length argument list.

The objects can be of any type; see the file lang/VarArgsDemo.java for examples of other types, and argument lists with other arguments before the varargs in the method declaration.

Java 1.5 API Changes

This section lists some of the changes made to Java with the Java 5 (JDK 1.5) release.

Java 5 threading: Concurrency utilities

Java was the first mainstream language with explicit support for multithreading. It has always been possible to write Java applications that run multiple sections of code more or less concurrently. In fact, even the simplest Java application creates at least one thread—Java’s memory allocation garbage collection runs in a background thread, started by the Java runtime before you can say "Hello World."

However, the code required has sometimes been slightly convoluted. In Java 1.5, the API has been significantly expanded to provide a series of utility classes that make it much easier to support multithreaded applications. Even such complex operations as various types of locking, and creation of thread pools, have been addressed. This work is an outgrowth of an open source library developed by Doug Lea, author of the book Concurrent Programming in Java (Addison-Wesley) and a computer science professor at State University of New York in Oswego, New York. This code was contributed to the Java Community Process where it has been extensively worked over by a multitasking committee of multithreading experts. The package java.util.concurrent and its subpackages contain all of the new classes, and there are quite a few of them.

One of the key differences from traditional Java synchronization is that the new classes really are concurrent. In other words, while a synchronized class such as Vector or Hashtable uses the object’s monitor lock to block all but a single thread from running any synchronized method at a time, the new concurrent classes will allow multiple threads to access them at the same time, yet still provide "thread-safe" access. For example, the new ConcurrentHashMap class allows an unlimited number of concurrent reading threads and a (settable) maximum number of writes. This will generally lead to much better scalability and faster performance, both of which become important when designing enterprise-scale application services. What’s also nice about the ConcurrentHashMap is that, because it still implements the Map interface from java.util, it is a drop-in replacement for the older synchronized Hashtable class (in most cases, but do refer to the documentation for some edge cases that need consideration). Using it where suitable is as simple as adding an import and changing:

Map myMap = new Hashtable();

to

Map myMap = new ConcurrentHashMap();

Of course, because you read the section on Generics (see Java 5 generic types), you’ll know that you probably want to use it in a typesafe way, so if your Map were hashing from a String to a CustomerAddress object, you’d actually write:

Map<String,CustomerAddress> myMap =
    new ConcurrentHashMap<String,CustomerAddress>();

I did say you have to get used to that <type> notation. Note that in 1.5, the Map interface is now declared as Map<K, V> (for Keys and Values); the iteration methods are declared in the interface as Enumeration<K> keys() and Collection<V> values(). So in this example you would get Enumeration<String> keys() and Collection<CustomerAddress> values from the "keys" and "values" methods, respectively.

Since you give the type for the keys and values when you instantiate a class implementing Map, you get back the iterators with the correct types built in, meaning no downcast needed when you extract the elements.

printf is back

In the early days of Java, it was common for people to try to bring the C-language printf functionality to Java. Most of these attempts worked only for certain cases, and were not really object-oriented approaches. After much prodding from developers and much internal debate, Sun relented and included printf functionality into Java 5.

The functionality is contained in the new java.util.Formatter class (not to be confused with the existing DateFormat, NumberFormat, etc., classes) and is also available in convenience routines in System.out (actually, in the PrintStream and PrintWriter classes). The format codes are more comprehensive than the original C printf, but the basic idea is the same: you pass a format string and one or more objects to be formatted according to the format string. For example, you might write:

System.out.printf("Pi is approximately %6.4f\n", Math.PI);

The % is the lead-in to the format code, and the 6.4f is (as it was in printf and in Fortran before that) the code to print a floating-point value with a field-width of six characters and four digits after the decimal place. So the program prints:

Pi is approximately 3.1416

There is much more to this, with support for date formatting, localization, and more. See the documentation for java.util.Formatter for details.

There is also scanf-like functionality, in the [javacook-io-SECT-5]. This does not use % format codes, but uses a variety of next() methods, such as next(String), next(Pattern), nextInteger, nextDouble, and so on, plus all of the corresponding hasNext() methods. See the documentation for java.util.Scanner. printf, and the scanning utilities are covered in [javacook-io-SECT-5.3].

Bibliographic note/full disclosure

Some of the Java 5 material in this section originally appeared in an article I wrote on O’Reilly Web back in the day.

What Was New in Java 6

By contrast to Java 5, December 2006’s Java 6 release was more incremental. Though there were no radical changes to the language, there were changes to the underlying JVM, including performance improvements, upgrades to garbage collection, faster application start-up, and a handful of new APIs.

Java 6 API Changes

The API changes included:

  • Swing improvements include SwingWorker, table sorting and filtering, and Swing double-buffering for performance;

  • Scripting Engines Support (see [javacook-otherlang-scripting]) provides a formal structure for invoking external scripting languages from within Java;

  • Java Compiler API lets a Java program compile code on the fly;

  • JAX-WS mini-server javax.xml.ws.Endpoint in the JDK;

  • JDBC upgraded to JDBC 4;

  • Version 2 of JAXB (Java API for XML Binding);

  • Support for pluggable annotations.

What Was New in Java 7

This section lists the main changes added in Java 7.

Java 7 Language/JVM Changes

The Java 7 language introduces a small variety of useful features, including:

  • Multicatch (see the following section)

  • Type Inference for Generics (see Type Inference for Generics (Diamond Operator, <>))

  • String Switch (see Java 7 String Switch)

  • Binary constants, like int delta = 011001b;

  • Use of “_” as numeric group separator, like long ageOfOldestFossil = 4_000_000_000L; (but note that these are not used or verified, they’re just for readability)

  • try-with-resource (see Try With Resources)

  • JVM invokedynamic instruction

  • JVM performance improvements

  • JVM garbage collection (GC) improvements

The first few of these are detailed starting at Multicatch.

The Java Virtual Machine has always had InvokeVirtual as a JVM instruction for support of possibly overridden methods. Java 7 introduces a related new JVM machine instruction called InvokeDynamic, which is intended for use by language developers to provide better support for dynamically typed languages such as JRuby, Jython (Java Python), and Clojure, some of which are discussed in [javacook-otherlang]. As this book focuses on Java, and because the changes will only result in better performance, not linguistic change, for these languages, I don’t discuss it here.

Also on the JVM side, the default garbage collection algorithm has changed to “Garbage First” (G1GC). The G1GC is a server-style garbage collector targeted at MP apps with large amounts of real memory. Further, it meets a soft real-time goal with high probability—​meaning better response for most apps. Although this is not the last word in GC, it is the long-term planned replacement for the widely used Concurrent Mark-and-Sweep Collector (CMS) GC.

Multicatch

You can now list multiple exception types in one catch:

Object newbie = null;
try {
	Class.forName(clazzName).newInstance();
} catch (InstantiationException|IllegalAccessException|
    ClassNotFoundException e) {
	// app-defined handler
	handleError("Could not create instance of " + clazzName, e);
}

In previous versions of Java you’d have needed three catch clauses with a lot of copy-and-paste, error-prone error handling, or, have something like catch (Exception e), which might be more than you want to catch.

Try With Resources

Another great simplification for proper error handling! You can now create resources (I/O Streams/Writers, JDBC Connections, …​) as the argument of a try statement and have them closed automatically. The resource must implement the (new for this purpose) AutoCloseable interface. Many standard classes have been modified to implement AutoCloseable to support this.

try (BufferedReader is = new BufferedReader(new FileReader(fileName))) {
	String line;
	while ((line = is.readLine()) != null) {
		System.out.println(line); }
} catch (IOException e) {
	handleError("Problem reading " + fileName, e);
} // No finally needed, no close needed - it's all done automatically!
Type Inference for Generics (Diamond Operator, <>)

Java 5 introduced Generic Types, as described in Java 5 generic types. For example, to create a List of Strings without any warning messages, you might write:

List<String> names = new ArrayList<String>();

Java 7 brings "Type Inference" for Generic Types. Instead of having to repeat the type parameter from the declaration in the definition, omit it and just put <>. The preceding list of strings can be rewritten as:

List<String> names = new ArrayList<>();
Java 7 String Switch

This long-awaited feature lets you replace a series of if statements using string comparisons, by a single switch statement; i.e., you can now use Strings as case labels:

String input = getStringFromSomewhere();
switch(input) {
	case "red":
		System.out.println("Stop!");
		break;
	case "amber": case "yellow":
		System.out.println("Caution!");
		break;
	case "green":
		System.out.println("Go (placidly among the haste)");
		break;
	default:
		handleError("Invalid input: " + input);
		break;
}

Obviously the strings have to be compile-time constants, either string literals or strings marked final. In fact, in real code, you would probably want to define final String variables for the cases. This should lead you to wonder why you’re not using a Java 5 Enum (see Java 5 enums). The String Switch has a place where long strings are in use, but it needs care for issues like case sensitivity (convert to lowercase first, see [javacook-strings-SECT-9.1]).

Java 7 API Changes

Released in July of 2011, Java 7 includes considerable support for concurrency, including the new fork/join framework (see [javacook-threads-forkjoin]).

Large parts of the standard API including I/O and JDBC were updated to implement AutoCloseable for use in try-with-resource. Closeable now extends AutoCloseable:

public interface java.lang.AutoCloseable {
  public abstract void close() throws java.lang.Exception;
}
public interface java.io.Closeable extends java.lang.AutoCloseable {
  public abstract void close() throws java.io.IOException;
}

URLClassLoader implements Closeable, and gains a close() method, which allows for updating of external JAR files being classloaded (on MS Windows, files can’t be overwritten while open). This is aimed at, and very useful for, development of web apps.

NIO2—​new Filesystem API—​includes more filesystem support and operators, and a new java.io.Path class intended to replace most uses of java.io.File (see [javacook-dirfile-usingPath-1]).

Version 4.1 of JDBC updated the RowSet API to version 1.1, which includes support for creating online or detached rowsets, and removed the older unsupported com.sun.rowset implementations.

A new package, java.lang.invoke, was added in support of the InvokeDynamic JVM changes for dynamically typed languages; it has classes such as MethodHandle and CallSite for use mainly by language developers.

Numerous small changes made to graphics rendering/fonts, Swing, networking, desktop, I18N, and more-- see Oracle’s website.

What’s new in later Java releases

For changes after this point, the trail leads into the section at the end of my Java Cookbook.