This is the story of the changes made to Java from the beginning of time up to Java 13. 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 of course advised to purchase a copy of my book 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.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, nowadays Arm64) 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. shows using foreach to iterate through an array and a List.

Example 1.
		String[] data = { "Toronto", "Stockholm" };
		for (String s : data) {
		// Show the Java 5 foreach loop - do not modernize to Java 8
		List<String> list = Arrays.asList(data);
		for (String s : list) {

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, at a bare minimum.

Example 2.
public enum Color {

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

Example 3.
	List myList = new ArrayList();

	// 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);

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

	List<String> myList = new ArrayList<>(); // Java 6: new ArrayList<String>();

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

	for (String s : myList) {	// Look Ma, no downcast!

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));
		int[] nums = {5, 7, 9};

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/ 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();


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 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.


You can now list multiple exception types in one catch:

Object newbie = null;
try {
} 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, also called the "diamond operator". 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":
	case "amber": case "yellow":
	case "green":
		System.out.println("Go (placidly among the haste)");
		handleError("Invalid input: " + input);

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 extends java.lang.AutoCloseable {
  public abstract void close() throws;

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 class intended to replace most uses of (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 Was New in Java 8

Java 8 Language Changes

The biggest new feature in the Java 8 language is lambda expressions. After a decade of debate on how to implement them, closures, or lambda expressions, finally arrived with Java 8. This is such a vast topic that it gets an entire chapter in this edition; see [javacook-fp].

Annotations can now be placed on structured types.

Default methods in interfaces, allowing for the addition of functionality without breaking all implementations of the interface.

Java 8 API Changes

Java 8 brings in the new date/time API from JSR-310. This provides a more consistent and sensible set of classes and routines for dealing with time. [javacook-dates] has been completely rewritten to use the new API, ending with a recipe showing various conversions between the old and new APIs.

Java 8 introduced functional programming techniques such as closures, Streams, and parallel collections, which we discuss in [javacook-fp]. In support of Streams, there are new methods in interfaces such as List, Map, and Set, which had until now been largely unchanged since the long-gone days of Java 1.1. Fortunately the Java 8 language support adds a default method type in interfaces, so your custom implementations of these interfaces are not required to change (as long as you make sure you change your IDE settings to an up-to-date compiler level).

As one example of default methods in action, Iterable gets a new default method called forEach(), which lets you write code like this:

myList.forEach(e -> /* do something with e here... */);

This is discussed further in [javacook-structure-iterate.Iterable.forEach].

A new JavaScript implementation codenamed Nashorn is available via javax.script (see [javacook-otherlang-scripting]) and can also be run from the command line.

Javadoc (see [javacook-packages-javadoc]) was extended to the API.

Annotations can be repeated, obviating the need to manually code wrapper annotations, for example, javax.persistence.NamedQueries (plural), which is just a container for a list of javax.persistence.NamedQuery (singular) annotations.

Finally, Java provides support for Base 64 encoding/decoding in the form of java.util.Base64 with two nested classes for encoding and decoding.

There were also dozens of other small changes, such as those covered by OpenJDK.

What Was New in Java 9

Java 9 is best known for introducing the Java Platform Module System, JPMS.

Since the JDK itself is modularized (the original intention of JPMS!), the new jlink tool lets you build a minimal JDK with only the parts needed for your modularized application.

Another new tool is JShell, a REPL (Read-Evaluate-Print-Loop) expression evaluator for Java. Also known as an interactive Java, JShell is useful for prototyping, trying out new ideas, and so on. JShell is covered in [javacook-getstarted-JSHELL].

This release also marked the beginning of the six-month major release cadence, in which a new major release (Java 10, Java 11, etc) would be made available every six months. At the same time, Java 8 and Java 11 were declared to be LTS (Long-Term Support) releases.

Java 9 Language Changes

The new module-info file introduces several pseudokeywords, words which have reserved meaning only in a module-info file, but can still be used as user-defined names in Java classes. These include module, requires, exports, provides, with, and a few others. This also impacts the meaning of the visibility modifiers when used within a module.

Interfaces (which added default methods in Java 8) now allow private methods as well, for use by default methods.

Java 9 API Changes

Improvements to the Streams API, with several new methods in the Stream interface.

Improvements to the Collections API, including the of() factory method to quickly create a List or Set from several values.

What Was New in Java 10 (March 2018)

Java 10 is famous for the var keyword and the first actual release on the six-month cadence.

Java 10 introduces GraalVM, a just-in-time compiler (like HotSpot) but written in Java.

In Java 10, the OpenJDK version of the cacerts file is fully populated, making it far more likely that connecting via https will work out of the box.

The javah tool for native code headers is removed, replaced by equivalent-or-better functionality in javac itself.

Java 10 Language Changes

The var keyword, for local variables only, allows you to not fuss over the actual type of a variable. Of course the compiler must be able to infer the type of the variable. Let’s explore some options in jshell:

jshell> var x = 10;
x ==> 10

jshell> var y = 123.4d;
y ==> 123.4

jshell> var z =;
z ==> 2019-08-31T20:47:36.440491

jshell> var map = new HashMap<String,Integer>();
map ==> {}

jshell> map.put("Meh", 123);
$4 ==> null

jshell> var huh;
|  Error:
|  cannot infer type for local variable huh
|    (cannot use 'var' on variable without initializer)
|  var huh;
|  ^------^


Somewhat surprisingly, var is not actually a language keyword, so this word can still be used as a user-defined name:

jshell> var var = 123;
var ==> 123

jshell> var
var ==> 123

Java 10 API Changes

List and Set add the new copyOf() method to make a truly unmodifiable copy; the previous List.unmodifiableList() made an unmodifiable view, which would appear to change if the underlying List were changed.

See Also

Quite a few old features were removed or deprecated; see this list on DZone.

Simon Ritter has an article titled "Java 10 Pitfalls for the Unwary".

What Was New in Java 11 (September 2018)

Java 11 introduced what I call "single-file run-from-source" (JEP 330); you can now type the following:


and the Java command will both compile and run the named program. This makes it much easier to work with single files, which is the primary thing it works with. If you have two or more files, the second through nth must be compiled and on your CLASSPATH; the source file you specify on the command line must be the one with main() and must not be compiled on your CLASSPATH. So it’s good for simple things, but not for complex applications.

See also this list on DZone.

Java 11 API Changes

For a more complete list of Java 11 changes, see this DZone list.

What Was New in Java 12 (March 2019)

Java 12 introduced the notion of Preview Changes, features added to the JDK but not yet made part of the official specification. This is basically what others might have called beta mode; if enough users indicate that they have serious issues with a Preview Mode feature, the JDK team can fix it or even kill it off before declaring it part of the JDK specification (or declaring it dead).

Java 12 Language Changes

  • switch statements that can yield a value (Preview)

Java 12 API Changes

Some of the more visible changes:

  • A Tee Collector for Streams (copies input to multiple output Streams).

  • A CompactNumberFormat, replacing my ScaledNumberFormat (prints the number 2,048 as 2K, for example).

  • String.indent(n) returns a copy of the String with n spaces prepended.

  • GC improvements (JEP 189: Shenandoah: Low-Pause-Time GC); pause-time improvements to G1 GC.

What Was New in Java 13 (September 2019)

Java 13 was the latest official release as of this writing. It includes the following features:

  • Improved garbage collection (again)

  • Improved application class-data sharing (AppCDS) to allow writing an archive of all classes used in an app

  • Text blocks to replace and simplify multiline String literals (Preview)

  • Improvements to switch statements that can yield a value

  • Rewrite of the Socket and ServerSocket implementation (not changing the API)

What Was New in Java 14

Java 14 arrived around the time the 4th edition of this book went to press.

These are some of the features that in the release:

  • Record types (in Preview; see [javacook-structure-record]).

  • Sealed types, which permit a class designer to control subclassing by enumerating all the allowed subclasses. The syntax looks like this:

    public abstract sealed class Person permits Customer, SalesRep {
    class Customer extends Person {
  • Text blocks (Preview), a.k.a. multiline text strings, delimited with a triplet of double quotes:

    String long = """
    This is a long
    text String."""
  • A new packaging tool, jpackage, which will generate a complete self-installing application on the main supported operating systems.

There are several other interesting JEPs for Java 14. A complete list can be found at OpenJDK. The JEPs linked from that page are interesting reading for those interested in the rationale for (and the amount of work that goes into) each of these new features.

What Was New in Java 15

  • Multi-line Text Blocks (no longer Preview)

  • Sealed Classes (Preview)

  • Hidden Classes

  • Rewrite the DatagramSocket implementation

  • Pattern Matching for instanceof (Second Preview)

  • New GC’s: ZGC for low-latency and Shenandoah for low-pause GC

  • Foreign-Memory Access API (Second Incubator)

  • Records (Second Preview)

  • Removal of the Nashorn JavaScript Engine

  • Deprecate RMI Activation for Removal

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.