Some Common Java Gotchas and How to Avoid Them

Advanced Java Questions

These questions delve into Java’s more intricate behaviours and are often too advanced for typical interviews, as they might be discouraging for candidates. However, they are excellent for deepening your understanding of Java’s core workings in your own time.

Myth 1: System.exit(0) Prevents finally Block Execution

Consider the following code:

System.setSecurityManager(new SecurityManager() {
    @Override
    public void checkExit(int status) {
        throw new ThreadDeath();
    }
});

try {
    System.exit(0);
} finally {
    System.out.println("In the finally block");
}

This code will output:

In the finally block

Explanation: The System.exit(0) call triggers the checkExit method in the custom SecurityManager. By throwing a ThreadDeath exception instead of terminating, the finally block is allowed to execute, explaining the "In the finally block" output. Since ThreadDeath is not an error that generates a stack trace by default, no stack trace is printed.

Myth 2: String str = "Hello"; Creates a String Object

Unlike C++, Java variables can only be primitives or references, not actual objects. For instance:

String str = "Hello";
String text = "Bye";

str == text; // Compares two references, not their contents.
str = text;  // Assigns the reference held by text to str.

This distinction between objects and references can sometimes be subtle, but it often causes confusion in cases like this:

final StringBuilder sb = new StringBuilder();
sb.append("Hello"); // The reference sb is final, but the instance it references is not.
method(sb);         // The method can alter the instance but cannot change the reference.

Explanation: In this example, sb is a final reference to a StringBuilder instance. The final modifier only applies to the reference, meaning sb cannot be reassigned, but the object it references (the StringBuilder instance) can be modified, such as by appending text. This is a critical distinction to grasp for understanding immutability and reference manipulation in Java.

Myth 3: Java Has Memory Leaks Like C++

According to Wikipedia, a memory leak is defined as: "In computer science, a memory leak occurs when a computer program incorrectly manages memory allocations. In object-oriented programming, a memory leak may happen when an object is stored in memory but cannot be accessed by the running code."

In Java, however, memory leaks differ from the traditional C++ understanding. Java’s garbage collector automatically cleans up objects that are no longer strongly referenced, meaning objects that are inaccessible to the running code are eventually removed. Still, memory leaks can occur in Java when objects are unintentionally retained, often in collections, long after they are needed, leading to an undesirable increase in retained memory.

Myth 4: Multi-Threading Is Hard

Multi-threading can indeed be challenging—if not approached with discipline. Creating a multi-threaded program without a clear structure or organisation often leads to complexity and hard-to-resolve issues.

However, with a disciplined approach, multi-threading can be made manageable. By limiting thread creation to only those required, controlling interactions between threads, and adopting well-understood patterns, multi-threaded programming can be straightforward. The key challenge often lies in ensuring that the entire team consistently follows these best practices.

Myth 5: I Don’t Need to Understand the Relative Performance of Different Operations to Care About Performance

Performance optimisation isn’t just about making everything "faster"—it’s about understanding the relative cost of different operations. For example:

int a = 5 + 10; // Fast operation
System.out.println(a); // Slower operation

In some cases, developers focus on speeding up faster operations while ignoring slower bottlenecks, such as I/O or memory access. Effective optimisation targets the most expensive operations to improve overall performance.

Myth 6: Random Numbers Always Look Random

It’s a common misconception that random numbers always appear random. Specific combinations of random numbers are just as likely as those that seem to follow a pattern. This is due to the nature of pseudo-random number generators, which may produce sequences that look non-random over short intervals but are statistically valid.

Myth 7: Floating Point Should Be Avoided Because It Has Random Errors

Floating point arithmetic does not produce random errors; the errors are predictable and repeatable for the same operations. This predictability makes floating point operations manageable when precautions like rounding are applied.

Floating point is also significantly faster and more memory-efficient than BigDecimal, making it suitable for most use cases where high performance is required.

Myth 8: Timezones Are Timeless

Timezones are not fixed—they change over time. For example:

  • Europe/London observed Daylight Saving Time (DST) at the epoch (January 1, 1970), making it GMT+1 instead of GMT.

  • Moscow changed from GMT+4 to GMT+3 on 27 March 2011.

Historical anomalies, such as February 30th in Sweden (1721) or George Washington’s birthday change due to calendar adoption, highlight the complexities of timezone handling.

Myth 9: When You Read a Non-Volatile Value in One Thread, You Will Eventually See an Updated Value

The Java JIT compiler may optimise code by inlining non-volatile fields, potentially preventing updates made by other threads from being visible. Using the volatile keyword ensures that changes to a variable are visible across threads.

Myth 10: Most Content on Java Interview Questions Is Accurate

A significant portion of Java interview questions found online is outdated or incorrect. Many questions are based on practices or behaviours from older Java versions and no longer apply to modern Java.

Key Points

  • Understand the distinction between object references and actual objects in Java.

  • Recognise that Java’s garbage collector manages memory differently from C++.

  • Approach multi-threading with disciplined best practices to mitigate complexity.

  • Focus optimisation efforts on operations that significantly impact performance.

  • Acknowledge the predictable nature of floating point arithmetic errors.

  • Stay informed about timezone changes and their historical contexts.

  • Use the volatile keyword to ensure the visibility of variable updates in the thread.

  • Rely on current and accurate resources for Java interview preparations.

A Simple AI-Generated Quiz

Try It Yourself

Experiment with the myths discussed:

  • Implement a custom SecurityManager to observe System.exit(0) behaviour.

  • Compare reference equality versus object content equality using == and .equals().

  • Create scenarios that lead to memory leaks in Java and observe garbage collection.

  • Develop multi-threaded applications following best practices to manage complexity.

  • Benchmark different operations to identify performance bottlenecks.

About the Author

As the CEO of Chronicle Software, Peter Lawrey leads the development of cutting-edge, low-latency solutions trusted by 8 out of the top 11 global investment banks. With decades of experience in the financial technology sector, he specialises in delivering ultra-efficient enabling technology that empowers businesses to handle massive volumes of data with unparalleled speed and reliability. Follow Peter on BlueSky or Mastodon.

Comments

Popular posts from this blog

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

System wide unique nanosecond timestamps

What does Chronicle Software do?