Java is one of the most in-demand programming languages in the world — and for good reason. It powers Android apps, enterprise backends, and systems that handle millions of requests a day. This guide covers all 257 Java topics on TheCodeForge, from your first Hello World to advanced concurrency and Spring Boot.
Java's reputation lags its reality by about a decade. The language people describe — verbose, ceremonious, stuck on version 8 — is not the language you would write today. Records removed most DTO boilerplate, sealed interfaces and pattern matching made exhaustive domain modelling pleasant, text blocks fixed embedded SQL and JSON, and virtual threads changed the economics of blocking I/O so thoroughly that a thread per request is viable again.
What has not changed is where Java engineers actually lose time, and it is a consistent list: collections behaviour that depends on equals and hashCode being right, concurrency where visibility and atomicity get conflated, memory problems that are not all heap problems, and Spring proxies behaving in ways the annotation does not advertise.
The most consequential rule in everyday Java is that equals and hashCode must agree. Override one without the other and hash-based collections stop working — not with an exception, but by silently failing to find entries you just inserted, because lookup goes to a different bucket.
The second-order version of the same rule: never mutate a field that participates in hashCode while the object is a key in a HashMap or an element of a HashSet. The object is now in the wrong bucket and is effectively invisible — including to remove, which is how collections develop entries that cannot be deleted.
| Need | Use | Why not the obvious choice |
|---|---|---|
| Lookup by key | HashMap | TreeMap costs O(log n) and you rarely need the ordering |
| Keys in sorted order or range queries | TreeMap | HashMap gives no ordering guarantee at all |
| Insertion order preserved | LinkedHashMap | It also gives you an LRU cache for free via removeEldestEntry |
| Frequent insert or remove at both ends | ArrayDeque | LinkedList is almost always slower in practice — cache locality beats the complexity table |
| Concurrent map | ConcurrentHashMap | Collections.synchronizedMap locks the whole map and compound operations are still not atomic |
| A small fixed set | EnumSet / EnumMap | Bitset-backed and dramatically faster than a HashSet of enums |
volatile guarantees that a write becomes visible to other threads and restricts reordering around it. It does not make an operation atomic, which is why count++ on a volatile field is still a race — it is a read, an add and a write, and two threads can interleave between them.
Getting this distinction wrong produces the worst category of bug: one that passes every test, appears under load weeks later, and cannot be reproduced on demand. The practical guidance is to reach for the concurrency utilities rather than for primitives — AtomicInteger, ConcurrentHashMap, ExecutorService — because they encode the correct memory semantics for you.
The message after OutOfMemoryError names the region, and the region determines the fix. Java heap space is the familiar one — too much retained, or a leak. Metaspace is class metadata, usually a classloader leak in an application server or repeated dynamic proxy generation. Unable to create native thread is an OS limit or thread leak, not heap at all. Direct buffer memory is off-heap NIO buffers, which -Xmx does not govern.
In containers the most common version is none of these: the JVM sized its heap against the container limit with no headroom, and the kernel's OOM killer terminated the process because total memory — heap plus metaspace plus thread stacks plus native — exceeded the cgroup limit. Nothing appears in the application log, which is the giveaway.
Spring implements @Transactional, @Async, @Cacheable and the rest by wrapping your bean in a proxy. Everything surprising about those annotations follows from that one implementation detail.
The most important consequence: calling an annotated method from another method of the same class bypasses the proxy entirely, so the annotation does nothing. No warning, no error — the transaction simply does not start. It is the single most common real Spring bug, and the fix is to move the annotated method to another bean or to inject a self-reference deliberately.
The second consequence: by default only unchecked exceptions trigger a rollback. A checked exception escaping a @Transactional method commits the transaction unless you declare rollbackFor.
rollbackFor. The method was called from within the same bean, so the proxy was bypassed. Or the exception was caught inside the method and never propagated, so Spring never saw a failure at all.Every tutorial starts with a plain-English analogy — then real code, then interview questions.
Browse Java Tutorials →