Some Common Java Gotchas

Overview

Java is a streamlined language with fewer features than others, yet it still has quirks that can confuse developers. Familiarity with other languages may lead to misinterpretation of Java syntax, often causing misunderstandings.

Variables are References or Primitives, Not Objects

In Java, variables represent references (for objects) or primitives, not the objects themselves. For example:

String s = "Hello";

This makes s a reference to a String, not the String itself.

Example Misunderstanding:

Q: If String is immutable, how can I change it, like in s += "!"?
A: You’re creating a new String and updating the reference, not altering the original String.

Compares References, Not Object Content

Using == compares references, not object contents. For immutable values, Java may optimize by pooling them, which can make the references equal:

String s1 = "Hi", s2 = "Hi";
Integer a = 12, b = 12;
System.out.println(s1 == s2); // true
System.out.println(a == b);   // true

However, without pooling, == returns false:

String s3 = new String(s1);
Integer c = -222, d = -222;
System.out.println(s1 == s3);      // false
System.out.println(s1.equals(s3)); // true
System.out.println(c == d);        // false
System.out.println(c.equals(d));   // true

Java Passes References by Value

When passing a reference variable to a method, Java copies the reference, not the object. Thus, changes to the object's content within the method affect the original object, but reassigning the reference itself has no effect outside the method:

public static void addWord(StringBuilder sb) {
    sb.append(" word");
    sb = null;
}

StringBuilder sb = new StringBuilder("first ");
addWord(sb);
System.out.println(sb); // "first word"

hashCode() and toString() Surprises

In Java, hashCode() doesn’t reflect an object’s memory location. Instead, it’s consistent per object lifecycle to support collections like HashSet.

toString() often surprises by printing class names and hashCode in hexadecimal. For arrays, this can be confusing:

String[] words = { "Hello", "World" };
System.out.println(Arrays.toString(words)); // [Hello, World]

Understanding hashCode() and toString() in Java

hashCode() Is Not Related to Memory Location

In most JVMs, the hashCode() for an object does not correlate to its memory location. Instead, hashCode() values must remain constant throughout an object’s lifecycle to support hash-based collections like HashSet or ConcurrentHashMap. Since JVMs may move objects around in memory, basing hashCode() on memory location would break this consistency.

In OpenJDK and HotSpot JVMs, hashCode() is generated on demand and stored in the object's header. This enables the JVM to reuse and relocate objects without affecting their hash-based behavior.

The Default toString() Output

The default behavior of toString() prints the class name and hashCode in hexadecimal. For arrays, this can be particularly confusing. For instance, a String[] array might print as:

[Ljava.lang.String;@45ee12a7

The [ indicates it’s an array, L specifies it as a non-primitive class, and the hex value is the hashCode. To make arrays more readable, use Arrays.toString():

String[] words = { "Hello", "World" };
System.out.println(Arrays.toString(words)); // Outputs: [Hello, World]

You can run this code CommonJavaGotchas.java

To see the original article here.

Comments

Popular posts from this blog

Java is Very Fast, If You Don’t Create Many Objects

System wide unique nanosecond timestamps

Comparing Approaches to Durability in Low Latency Messaging Queues