exit lab
Backend Engineering
Level 1·java·guide·beginner

Java Backend Fundamentals

Review modern Java types, object design, collections, exceptions, JVM behavior, and concurrency through one backend domain example.

55 minutes Examples compile on Java 21; JDK 25 API and language evolution noted Updated 2026-07-16
Java 21JVMJUnit

Learning objectives

  • Choose Java types and collections from domain invariants
  • Explain JVM memory and concurrency risks relevant to request processing

What it is

Java backend fundamentals connect language syntax to durable domain behavior: values and identity, equality, collections, failure boundaries, resource management, JVM execution, and safe concurrency. Framework annotations cannot compensate for weak understanding here.

Why it matters

A Spring service runs ordinary Java code on a JVM under concurrent requests. Incorrect equality can lose map entries, mutable objects can leak state, swallowed exceptions can commit partial work, and unbounded executors can exhaust memory or downstream capacity.

How it works

AreaBackend question
TypesIs this value nullable, mutable, bounded, or identified?
OOPWhich invariant belongs inside the domain and which dependency belongs behind an interface?
CollectionsDo order, uniqueness, lookup, equality, and concurrency matter?
ExceptionsWhich layer can recover, translate, retry, or terminate?
JVMWhere are objects allocated and which roots keep them reachable?
ConcurrencyWhich state is shared and what establishes atomicity or ordering?

Simple example

Voucher.java
public record VoucherCode(String value) {
    public VoucherCode {
        value = Objects.requireNonNull(value, "value").trim();
        if (!value.matches("[A-Z0-9]{12}")) {
            throw new IllegalArgumentException("Invalid voucher code format");
        }
    }
}

enum VoucherStatus { ISSUED, REDEEMED, EXPIRED, CANCELLED }

sealed interface RedemptionResult
        permits RedemptionResult.Accepted, RedemptionResult.Rejected {
    record Accepted(long redemptionId) implements RedemptionResult {}
    record Rejected(String reasonCode) implements RedemptionResult {}
}

The types make invalid format and unknown result variants harder to represent. They do not by themselves make redemption atomic; the database transaction still owns that invariant.

Backend example

VoucherRules.java
final class VoucherRules {
    private final Set<String> permittedMerchantIds;

    VoucherRules(Set<String> permittedMerchantIds) {
        this.permittedMerchantIds = Set.copyOf(permittedMerchantIds);
    }

    RedemptionResult evaluate(Voucher voucher, String merchantId, Instant now) {
        Objects.requireNonNull(now, "now");
        if (!permittedMerchantIds.contains(merchantId)) {
            return new RedemptionResult.Rejected("MERCHANT_NOT_ALLOWED");
        }
        if (!voucher.expiresAt().isAfter(now)) {
            return new RedemptionResult.Rejected("VOUCHER_EXPIRED");
        }
        return new RedemptionResult.Accepted(voucher.id());
    }
}

Production example

JVM execution model

javac
Bytecode
Class loader
Interpreter + JIT
Threads
Heap objects
Garbage collector
Text alternative: The Java compiler produces bytecode. The JVM loads classes, interprets and JIT-compiles hot code, runs work on threads, allocates objects on the heap, and reclaims unreachable objects with garbage collection.
Tool/evidenceUse
Thread dumpBlocked, waiting, deadlocked, and runnable thread state
Heap dumpRetained objects and suspected memory leaks
GC logs/JFRAllocation, pause, CPU, lock, and I/O evidence
Pool metricsWhether application concurrency exceeds downstream capacity

Common mistakes

  • Using == instead of equals for object value comparison
  • Overriding equals without a consistent hashCode
  • Returning mutable internal collections
  • Catching Exception and continuing without a recovery policy
  • Using Optional for fields or parameters as a default design
  • Creating unbounded threads or CompletableFuture work on an unsuitable shared executor

Best practices

  • Prefer composition and small cohesive types
  • Make invariants explicit in constructors or factories
  • Use try-with-resources for closeable resources
  • Translate exceptions at stable boundaries and preserve the cause
  • Choose collections by semantics before performance
  • Measure JVM and concurrency behavior under realistic downstream limits

Trade-offs

ChoiceUse whenRisk
Checked exceptionCallers are expected to handle a recoverable conditionPropagation boilerplate and accidental wrapping
Runtime exceptionInvariant or boundary failure cannot be handled locallyUndocumented failure contracts
InheritanceA true substitutable subtype relationship existsTight coupling and fragile hierarchies
CompositionBehavior can be delegated behind a focused interfaceMore small objects and wiring

Interview questions

  • What is the contract between equals and hashCode?
  • How do stack frames and heap objects differ?
  • Why is a record only shallowly immutable?
  • What causes a Java memory leak despite garbage collection?
  • When are virtual threads appropriate?
  • How would you diagnose a deadlock?

Hands-on task

  1. 1

    Model

    Create voucher value types, lifecycle states, and sealed redemption outcomes.

  2. 2

    Test

    Write parameterized tests for format, expiry, merchant rules, equality, and mutable collection defenses.

  3. 3

    Stress

    Run concurrent evaluation tasks, capture a thread dump, and explain which shared state is safe.

References

  • Oracle: JDK 25 Documentation — https://docs.oracle.com/en/java/javase/25/
  • Oracle: The Java Virtual Machine Specification, Java SE 25 — https://docs.oracle.com/javase/specs/jvms/se25/html/
  • Oracle: Thread API — Virtual Threads — https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Thread.html

Retrieval check

Before you continue

  • Explain the main idea in your own words without rereading the page.
  • Name one production problem this knowledge helps prevent.
  • Describe where you would apply it in a Spring Boot project.

Source metadata

Primary references checked on 2026-07-16. Version scope is stated at the top of this page.