Neat and Obscure Java Features and Quirks

Just to see what’s new I checked out some core Java 9 and 10 books from my local library – they remind me of the old WhitePages and YellowPages in terms of thickness. Like most people I navigate through Java source code, read Javadocs, and visit StackOverflow to find what we want to know. But, what don’t we know we don’t know? I was surprised by all the neat quirks of even Java 8 through 10 I had either forgotten or didn’t know.

I’ve noted some of the lesser-known, obscure features and quirks of Java 8+. You probably know them already, but I find them neat and want to reference them for myself.

Java 9 SE reference books
Java 9 SE reference books

Unicode Literals

This still blows me away that this is valid syntax:

Also, this comment will yield a syntax error! It’s all in the malformed \u.

Finally, these are perfectly valid variable names:

Number Representations

I love this next feature of Java. We can write numbers with visual-only underscores in Java like so:

Math, Floats, Overflows, and Precision

This is a quirk. By adding the strictfp keyword to a method signature the JVM will truncate intermediate floating-point results to 64 bits. This is to maintain portability because some processors have 80-bit extended floating point registers which can lead to different precision between JVMs otherwise. However, strict calculations may overflow!

If this keyword is strange, there is always the StrictMath library.

There are methods like Math.multiplyExactly(1_000_000_000, 3) that will throw an exception on integer overflow instead of silently going negative. Very cool.

Here is another quirk: BigDecimal objects won’t equal each other if they vary in precision.

Shift Operators

The >> operator is a signed right-shift operator. What is neat about Java is there is an unsigned right-shift operator >>> that will pad the left of the most significant bit with zeros no matter what. The former will pad the left with 1s in the case the number is negative (the first bit is a 1). We can also use >>>=.

Strings

Java 11 finally has a repeat operator! It is "A".repeat(3) // AAA.

Joining strings with a delimiter doesn’t require me to break out StringBuilder; I could use

Reminder: Always remember to use .equals() (or .equalsIgnoreCase()) and not == to compare strings!

Are there UTF-16 characters in my strings? I had better not use char if I can help it: UTF-16 can take 2 bytes which don’t fit in a char. Both books advise against using chars at all.

Date formatting is a piece of cake. What is neat is the < preceding parameter flag.

Labels

Java has labeled breaks and labeled continues to hop out of nested loops. It’s weird because they remind me of ancient goto statements, but they work.

The labeled continue exists in the wild. For instance, in ConcurrentLinkedQueue.java I found this:

Arrays

Primitive arrays can be declared with either int[] a or int a[]. The latter is weird to me, but it compiles.

Coming from C++ in the earlier days this blows my mind. We can use shorthand like in JavaScript, a trailing comma like in Python and PHP7, and the array doesn’t need to be square. Nice!

Arrays can be printed with Arrays.toString() or Arrays.deepToString() for multi-dimensional arrays.

Interestingly, new ArrayList<MyClass>(100) is not the same as new MyClass[100]. Under the hood, the former will call new Object[100] to create an initial capacity of 100 object references; the latter will create 100 concrete MyClass objects.

Var

Jave 10 introduces var. These are identical:

Null Checks

Java 9 introduces slicker sanity check utilities:

Static Fields and Blocks

Static fields are better called class fields, and the keyword static is a holdover from C++. This is just a parlance adjustment.

Also, up until Java 6, it was possible to echo “Hello World” without the main method!

Call by Value

Java is a call by value language, not call by reference. If an interviewer asks, it is call by value. This is because object references are passed by value. And, of course, primitive data types are copied and passed by value.

Shutdown and Cleanup

Java 9 has a Cleaner library. When an object has phantom references – a fancy of way of saying no more references when the GC runs – we may be able to run some task on a background thread. There is also the shutdown hook that does the same thing but on the application level. These are neat.

Java CLI Arguments

As of Java 9, we can now specify things like --class-path (in addition to -cp and -classpath) on the CLI. Thank you for adding double-dash.

Multi-Release JARs

Java 9 supports muti-release JARs, but I’m still a fan of using tricks with class loading and reflection to support older JRE versions.

Varargs

These two signatures are identical, and you can pass an array to a method with an ellipsis in the signature.

Java has the annotation @SafeVarargs for working with generics. Java 9 now allows this annotation with private constructors or methods.

Reflection

This is neat. At startup, the application entry point loads all the explicitly imported classes, and those classes import any explicitly imported classes, and so on. This can take a few seconds for a large application. Instead, the main method could display a CLI message or splash screen, then use Class.forName() to load the needed classes during the splash screen. This is easier if the entry point is a facade for the real main method.

We can also do fun things like this (not recommended for production, just nice to know).

Did you know that primitive types have a class? This is valid: long.class.

Private and Static Methods

Since Java 8 we can add (public) static methods to interfaces. Since Java 9 we can add private static or instance methods. Java 11 has the Path interface allowing implementing classes to use of() instead of Paths.get(). Interesting.

How to access the getClass() information in a static method? Below, an anonymous object in a static method is instantiated, then the enclosing class is queried. This is especially useful for logging.

Lambdas

Lambdas are also called closures in Java. Outer block variable values are said to be captured in lambdas and must be immutable or effectively final. Lambdas were invented by the mathematician Alonzo Church. I enjoyed the nomenclature and history around lambdas.

Sorting

Here’s a neat way to sort POJOs and other data structures without Java 8’s stream (which I admit I use too much with sorted):

Arrays.sort() uses a “tuned” version of QuickSort. Under the hood, it calls DualPivotQuicksort.sort(). The entire source is available and the primary method is over 330 lines. This is not the trivial QuickSort we do on a whiteboard interview. This sort is also stable.

We can chain comparators to sort further in the event of a tie. For example, I can sort a simple array on string length, then on the string value when the strings are the same length:

Inner Classes

Not only can we have public and static inner classes at the class level, but also at the method level! Knock knock, Neo.

The Double-Brace Initialization Trick

I love using this trick for date string formatters because I can set the timezone once and reuse a static formatter. The first brace makes an anonymous subclass of DateFormat and the inner braces are an object initialization block. The alternative is to set the timezone in the constructor, but then the formatter cannot be static. This double-brace trick is neat.

Or, for safe multi-threaded awesomeness:

Proxy Objects

Let’s see what Arrays.binarySearch() does under the hood by proxying each array element (Integer objects) and printing the compareTo() calls. Beautiful.

Try-With-Resources

This has been around since Java 7 in 2011. Since Java 9, however, we can provide previously declared effectively final variables in the try header. For example:

Generics

This is wild fun. What I hadn’t noticed before is that a generic variable can be bounded by multiple interfaces (but just one class). For example:

Generics with getClass() only return the raw type. For example:

Linked Lists

The get(n) method has to traverse the LinkedList for the nth element. If n >= size()/2 then the search starts from the end of the list backward. Clever.

Hash Codes

If I need to override the hashCode() method in my class, Objects.hash( field1, fields2, ... ) saves the day.

Hash Tables

As of Java 8, buckets changed from linked lists to balanced binary trees when they get full – a welcome change. Remember to set the bucket capacity to a prime number that assumes a load factor of 0.75. The default implementation of HashTable and HashSet sets the capacity to powers of 2 with a default capacity of 16.

Concurrent Updating of Maps

With a single-threaded app, a simple map can be updated in one line:

To do this concurrently across threads, one way is to increment a ConcurrentHashMap value like so:

However, with merge this way is much more elegant. There are interesting compute functions to do flexible operations on the objects (values) stored in a map as well.

BitSet

This makes coding interview questions so much easier! It’s been around since Java 1.0, but sometimes oldies are goodies. With bit-twidling to make a more efficient hash table for some questions, we are limited to either 32 bits (int) or 64 bits (long). With BitSet we have access to millions of bits all compact into bytes and operable simply with get() and set().

New Java Versioning

The Java major version is now incremented every six months: The March 2018 release is JDK 10, the September 2018 release is JDK 11, the March 2019 release is JDK 12, and so forth. This is going to be confusing and will make skill assertions such as “Experienced with Java 8/9” sound funny. See JEP 322.


More to come…