10 Surprising Java Language Features You Might Not Know
This article reveals ten little‑known quirks of the Java language—such as the illusion of checked exceptions, overloads with differing return types, bridge methods, confusing array syntax, type‑use annotations, compound assignment nuances, hidden goto‑like control flow, type aliases, undecidable generic relationships, and type intersections—illustrated with concrete code examples.
Java contains many hidden quirks that even seasoned developers may overlook, from the fact that the JVM does not enforce checked exceptions to surprising method‑overload and type‑system behaviours.
1. No real checked exceptions – The JVM knows nothing about checked exceptions; they exist only in the Java language. The following code compiles and throws a SQLException without declaring throws :
public class Test {
// method without throws
public static void main(String[] args) {
doThrow(new java.sql.SQLException());
}
static void doThrow(Exception e) {
Test.
doThrow0(e);
}
@SuppressWarnings("unchecked")
static
void doThrow0(Exception e) throws E {
throw (E) e;
}
}2. Overloads can differ only by return type – While the Java language forbids two methods with identical signatures except for return type, the JVM generates bridge methods to preserve binary compatibility, allowing covariant returns.
3. All these array declarations are the same – Methods declared as int[][] a() , int[] b()[] or int c()[][] all return a two‑dimensional int array, and similar variable declarations are equivalent.
4. Conditional operator performs numeric promotion – The ternary operator may apply primitive widening, leading to surprising results such as Object o = true ? i : d; throwing a NullPointerException when i is null.
5. Compound assignment operators are not simple rewrites – An expression like i += j is equivalent to i = (T)((i) + (j)) where T is the type of i , and the left‑hand side is evaluated only once, which can affect overflow and casting.
6. Random‑looking Integer values – By abusing the Integer cache via reflection and auto‑boxing, a loop printing (Integer)i can produce seemingly random numbers.
7. Java reserves the keyword goto – Although goto is a reserved word and cannot be used as an identifier, labelled break and continue can emulate goto‑style jumps, as shown by the bytecode examples.
8. Type aliases via generics – While Java lacks top‑level type aliases, generic type parameters can act as aliases, e.g., class Test<I extends Integer> { <L extends Long> void x(I i, L l) { … } } .
9. Some generic type relationships are undecidable – Complex recursive generic declarations such as class C implements Type<Type<? super C>> {} lead to compiler crashes and undecidable subtype checks.
10. Type intersections – Java allows a type variable to extend multiple interfaces, e.g., class Test<T extends Serializable & Cloneable> {} , which is useful for casting lambdas to multiple functional types.
These quirks illustrate how Java’s design balances language‑level convenience with JVM‑level flexibility, often resulting in surprising but legal code patterns.
Qunar Tech Salon
Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.