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.
Unicode Literals
This still blows me away that this is valid syntax:
1 | public static void main( String\u005B\u005D args ) |
Also, this comment will yield a syntax error! It’s all in the malformed \u
.
1 | // Default folder is c:\users\default |
Finally, these are perfectly valid variable names:
1 2 | double Π = 3.14159 boolean Javaは最高 = true; |
Number Representations
I love this next feature of Java. We can write numbers with visual-only underscores in Java like so:
1 2 3 | long x = 1_000_000_000_000; long y = 0b1111_1111; // 255 long z = 0xFF_FF; // 65535 |
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!
1 | public static strictfp void main( String[] args ) |
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.
1 2 3 4 | BigDecimal a = new BigDecimal( "1.0" ); BigDecimal b = new BigDecimal( "1.00" ); System.out.println( a.equals( b ) ); // false System.out.println( a.compareTo( b ) ); // 0 |
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
1 | String.join( ", ", "a", "b", "c" ) // "a, b, c" |
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 char
s at all.
Date formatting is a piece of cake. What is neat is the <
preceding parameter flag.
1 2 | System.out.printf( "%1$tB %1$te, %1$tY", new Date() ); // May 10, 2019 System.out.printf( "%tB %<te, %<tY", new Date() ); // May 10, 2019 |
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.
1 2 3 4 5 6 7 8 9 | outta_here: for(;;) { for(;;) { for(;;) { break outta_here; } } } // And we're outta here |
The labeled continue exists in the wild. For instance, in ConcurrentLinkedQueue.java
I found this:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public int size() { restartFromHead: for (;;) { int count = 0; for (Node<E> p = first(); p != null;) { if (p.item != null) if (++count == Integer.MAX_VALUE) break; // @see Collection.size() if (p == (p = p.next)) continue restartFromHead; } return count; } } |
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!
1 2 3 4 5 | int[][] triangle = { // Shorthand {1}, {1, 2}, {1, 2, 3}, // Trailing comma is okay }; |
Arrays can be printed with Arrays.toString()
or Arrays.deepToString()
for multi-dimensional arrays.
1 2 3 4 | System.out.println( Arrays.toString( triangle ) ); // [[I@9f70c54, [I@234bef66, [I@737996a0] System.out.println( Arrays.deepToString( triangle ) ); // [[1], [1, 2], [1, 2, 3]] |
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:
1 2 | var lock = new Object(); Object lock = new Object(); |
Null Checks
Java 9 introduces slicker sanity check utilities:
1 2 | Objects.requireNonNull(str, "String cannot be null"); str = Objects.requireNonNullElse(str, ""); |
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!
1 2 3 4 5 6 7 | class HelloJava6 { static { System.out.println( "Hello World" ); } } |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class Main { public static void main( String[] args ) { System.out.println( Thread.currentThread().getName() ); Runtime.getRuntime().addShutdownHook( new Thread( () -> System.out.println( Thread.currentThread().getName() ) ) ); Main obj = new Main(); Cleaner cleaner = Cleaner.create(); cleaner.register( obj, () -> System.out.println( Thread.currentThread().getName() ) ); obj = null; System.gc(); } } // main // Cleaner-0 // Thread-0 |
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.
1 2 | public static void main( String... args ) public static void main( String[] args ) |
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).
1 2 3 4 5 6 7 | System.out.println( Classes .getClass( "java.util.Date" ) .getConstructor( long.class ) .newInstance( 1557686807 * 1000L ) ); // Sun May 12 11:46:47 PDT 2019 |
Did you know that primitive types have a class? This is valid: long.class
.
1 | System.out.println( long.class.getName() ); // long |
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.
1 2 3 4 | static Class getStaticClass() { return new Object(){}.getClass().getEnclosingClass(); } |
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
):
1 2 3 4 | ArrayList<Pair<Integer, Integer>> list = new ArrayList<>( 10 ); ... // Sort the Pair objects on the second integer list.sort( Comparator.comparingInt( Pair::getSecond ) ); |
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:
1 2 3 4 5 6 7 | String[] arr = { "B", "BBB", "AAA", "A" }; Arrays.sort( arr, Comparator .comparing( String::length ) .thenComparing( String::toString ) ); System.out.println( Arrays.toString( arr ) ); // [A, B, AAA, BBB] |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public static void main( String[] args ) { class What { { System.out.print( "There" ); } class Is { { System.out.print( " is" ); } class The { { System.out.print( " no" ); } class Matrix { { System.out.println( " spoon." ); } } } } } new What().new Is().new The().new Matrix(); } // There is no spoon. |
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.
1 2 3 4 | private static final DateFormat pacficTimeFormat = new SimpleDateFormat( "HH:mm:ss", Locale.CANADA ) {{ setTimeZone( TimeZone.getTimeZone( "America/Vancouver" ) ); }}; |
Or, for safe multi-threaded awesomeness:
1 2 3 4 5 6 7 8 | // Each thread has its own instance of the DateFormat. // Access with pacficTimeFormat.get() private static final ThreadLocal<DateFormat> pacficTimeFormat = ThreadLocal.withInitial( () -> new SimpleDateFormat( "HH:mm:ss", Locale.CANADA ) {{ setTimeZone( TimeZone.getTimeZone( "America/Vancouver" ) ); }}; ); |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public static void main( String[] mArgs ) { Object[] arr = new Object[1000]; for ( int i = 0; i < arr.length; i++ ) { final Integer value = i + 1; arr[i] = Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), new Class[]{Comparable.class}, ( proxy, method, args ) -> { System.out.println( value + " vs " + args[0] ); return method.invoke( value, args ); } ); } Arrays.binarySearch( arr, 312 ); } // 500 vs 312 // 250 vs 312 // 375 vs 312 // 312 vs 312 |
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:
1 2 3 4 5 | PrintWriter pw = new PrintWriter( "out.txt", StandardCharsets.UTF_8.toString() ); try ( pw ) { pw.print( "Hello" ); } // pw.close() called here |
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:
1 | <T extends Comparable & Serializable> |
Generics with getClass()
only return the raw type. For example:
1 2 3 | ArrayList<String> strings = new ArrayList<>(); ArrayList<Integer> ints = new ArrayList<>(); System.out.println( strings.getClass() == ints.getClass() ); // true |
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:
1 2 | // Good for single-threaded apps map.put(key, map.getOrDefault(key, 0) + 1); |
To do this concurrently across threads, one way is to increment a ConcurrentHashMap
value like so:
1 2 3 | // Concurrent updates are allowed, but a new AtomicLong is created each time map.putIfAbsent(key, new AtomicInteger()); map.get(key).incrementAndGet(); |
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.
1 2 3 | // Concurrent updates are allowed map.merge( key, 1, Integer::sum ); // Or map.compute( key, ( k, v ) -> v == null ? 1 : v + 1 ); |
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…