Backend EngineeringJavaProduction Ready12 lessons

Java Core

Master the language, runtime, memory model, and object design principles behind production Java systems.

Java Core

Overview

Java is a general-purpose, strongly typed language designed for portability, reliability, and long-running applications. It remains a core technology in banking, payments, e-commerce, logistics, healthcare, telecommunications, and cloud platforms.

Java source code is compiled into platform-neutral bytecode and executed by the Java Virtual Machine. This separation gives engineering teams a stable runtime, automatic memory management, mature diagnostics, and a broad production ecosystem.

Why Java remains a backend standard

  • Predictable performance for long-running services
  • Strong compile-time type checking
  • Mature concurrency, networking, and security APIs
  • Excellent profiling and observability tools
  • Broad support for databases, messaging, and cloud platforms
  • A stable ecosystem with long-term support releases
Backend Engineering Learning Path
Architecture
Java Core β”œβ”€β”€ Language Fundamentals β”œβ”€β”€ Object-Oriented Design β”œβ”€β”€ Collections and Generics β”œβ”€β”€ Exceptions and Concurrency β”œβ”€β”€ JVM Internals └── Backend Frameworks β”œβ”€β”€ Spring Framework β”œβ”€β”€ Spring Boot └── Microservices

Java Core provides the runtime and design foundation for every later backend topic.

Learning outcome

After completing Java Core, you should be able to design production-quality Java code, explain how it executes inside the JVM, choose appropriate language constructs, and diagnose common correctness and performance problems.

Java Core

JDK, JRE and JVM

The JDK, JRE, and JVM describe different layers of the Java development and execution environment. Their responsibilities matter when configuring builds, containers, runtime images, and production diagnostics.

JDK: Java Development Kit

The JDK contains the tools required to develop Java applications.

  • javac compiles source files into bytecode
  • java starts a Java application
  • jar packages classes and resources
  • javadoc generates API documentation
  • jcmd, jstack, and jmap support runtime diagnostics

JRE: Java Runtime Environment

The JRE is the environment required to run Java applications. Historically it was distributed separately and contained the JVM plus standard runtime libraries. Modern deployments often create application-specific runtime images with jlink.

JVM: Java Virtual Machine

The JVM loads, verifies, and executes bytecode. It manages class loading, memory, garbage collection, thread scheduling, security checks, and runtime optimization through Just-In-Time compilation.

Java Platform Architecture
Architecture
JDK β”œβ”€β”€ Compiler (javac) β”œβ”€β”€ Development Tools └── JRE β”œβ”€β”€ Standard Libraries └── JVM β”œβ”€β”€ Class Loader β”œβ”€β”€ Bytecode Verifier β”œβ”€β”€ Execution Engine β”œβ”€β”€ JIT Compiler └── Garbage Collector
Compilation and Execution Flow
Architecture
Main.java β”‚ β”‚ javac β–Ό Main.class (Bytecode) β”‚ β”‚ Class Loader + Verifier β–Ό JVM Execution Engine β”‚ β”œβ”€β”€ Interpreter └── JIT Compiler β”‚ β–Ό Native Machine Code

Bytecode targets the JVM specification rather than one operating system or CPU.

Java Core

Variables

A variable associates a name with a value or object reference. Java is statically typed, so every variable has a type known at compile time.

Variable categories

  • Local variables exist inside methods or blocks and must be initialized before use
  • Instance fields belong to an object and receive default values
  • Static fields belong to a class rather than an individual object
  • Parameters receive values when a method or constructor is invoked
javapublic final class Account { private static final String DEFAULT_CURRENCY = "USD"; private final long accountId; private BigDecimal balance; public Account(long accountId, BigDecimal openingBalance) { this.accountId = accountId; this.balance = openingBalance; } public void deposit(BigDecimal amount) { BigDecimal updatedBalance = balance.add(amount); balance = updatedBalance; } }

DEFAULT_CURRENCY is a class constant, accountId and balance are instance fields, amount is a parameter, and updatedBalance is a local variable.

Variable Scope
Architecture
Account Class β”œβ”€β”€ Static State β”‚ └── DEFAULT_CURRENCY └── Account Object β”œβ”€β”€ accountId β”œβ”€β”€ balance └── deposit(amount) └── updatedBalance

Java Core

Data Types

Java types are divided into primitive types and reference types. The distinction affects memory representation, nullability, equality, method calls, and generic collections.

Primitive types

  • byte: 8-bit signed integer
  • short: 16-bit signed integer
  • int: 32-bit signed integer
  • long: 64-bit signed integer
  • float: 32-bit IEEE 754 floating point
  • double: 64-bit IEEE 754 floating point
  • char: 16-bit UTF-16 code unit
  • boolean: logical true or false

Reference types

Classes, interfaces, arrays, records, enums, and strings are reference types. A reference can point to an object or contain null. Java always passes arguments by value, including object references.

Java Type System
Architecture
Java Types β”œβ”€β”€ Primitive Types β”‚ β”œβ”€β”€ Integer: byte, short, int, long β”‚ β”œβ”€β”€ Floating Point: float, double β”‚ β”œβ”€β”€ Character: char β”‚ └── Logical: boolean └── Reference Types β”œβ”€β”€ Classes and Records β”œβ”€β”€ Interfaces β”œβ”€β”€ Arrays β”œβ”€β”€ Enums └── Strings

Java Core

Strings

String represents an immutable sequence of characters. Operations that appear to modify a string create a new value.

Immutability makes strings safe to share, suitable as map keys, and easier to reason about across threads. It also enables string pooling and cached hash codes.

String pool and object identity

javaString first = "java"; String second = "java"; String third = new String("java"); System.out.println(first == second); // true System.out.println(first == third); // false System.out.println(first.equals(third)); // true

The == operator compares references. The equals method compares string content.

String Pool
Architecture
Heap Memory β”œβ”€β”€ String Pool β”‚ └── "java" β”‚ β”œβ”€β”€ first β”‚ └── second └── Regular Heap Object └── new String("java") └── third

Java Core

Arrays

An array stores a fixed number of elements of one component type. Arrays provide constant-time indexed access and low overhead, making them useful for buffers, lookup tables, protocol payloads, and performance-sensitive code.

javaint[] responseTimes = {120, 95, 143, 87}; int firstResponseTime = responseTimes[0]; responseTimes[1] = 101;

Complexity

  • Read or update by index: O(1)
  • Linear search: O(n)
  • Insert or remove in the middle: O(n)
Array Memory Model
Architecture
Stack Heap β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ responseTimes│─────────────▢│ 120 β”‚ 95 β”‚ 143 β”‚ 87 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 0 1 2 3

The local variable stores a reference; the array object stores indexed values.

Java Core

OOP Basics

Object-oriented design groups state and behavior into objects with explicit responsibilities. Good Java design uses objects to model domain rules, protect invariants, and isolate change.

Encapsulation

Encapsulation hides representation details and exposes controlled behavior. Fields should usually be private, while methods should express valid operations.

Abstraction

Abstraction exposes what a component does while hiding how it does it. Interfaces are valuable at boundaries where behavior genuinely varies.

Polymorphism

Polymorphism allows code to depend on a contract while runtime objects provide different implementations.

Inheritance

Inheritance models an is-a relationship but creates strong coupling to the parent class. Use it only when the subtype contract is genuine and stable.

Object-Oriented Design
Architecture
OOP β”œβ”€β”€ Encapsulation β”‚ └── Protect state and invariants β”œβ”€β”€ Abstraction β”‚ └── Expose essential behavior β”œβ”€β”€ Polymorphism β”‚ └── One contract, many implementations └── Inheritance └── Specialized is-a relationship

Java Core

Object Class

Every Java class directly or indirectly extends java.lang.Object. Its methods form a basic contract used by the language, collections, testing tools, logging systems, and frameworks.

Important methods

  • equals(Object) defines logical equality
  • hashCode() provides a hash value consistent with equality
  • toString() returns a diagnostic representation
  • getClass() returns runtime type information
  • wait(), notify(), and notifyAll() support monitor coordination
  • clone() is a legacy copying mechanism and is generally avoided
Universal Object Contract
Architecture
java.lang.Object β”œβ”€β”€ equals(Object) β”œβ”€β”€ hashCode() β”œβ”€β”€ toString() β”œβ”€β”€ getClass() β”œβ”€β”€ wait() / notify() └── clone() β”‚ └── Every Java Class

Java Core

equals vs hashCode

equals determines whether two objects are logically equal. hashCode maps an object to an integer used by hash-based collections such as HashMap and HashSet.

Required contract

  • Equal objects must return the same hash code
  • Unequal objects may return the same hash code
  • Equality must be reflexive, symmetric, transitive, and consistent
  • x.equals(null) must return false
  • Equality fields should remain stable while used as a hash key
HashMap Lookup Flow
Architecture
Key β”‚ β–Ό hashCode() β”‚ β–Ό Bucket Selection β”‚ β–Ό equals(candidate) β”‚ β”œβ”€β”€ true ──▢ Matching Entry └── false ──▢ Continue Search
javapublic final class UserId { private final long value; @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof UserId userId)) return false; return value == userId.value; } @Override public int hashCode() { return Long.hashCode(value); } }

Java Core

Production Notes

Production Java engineering requires understanding behavior under load, failure, concurrency, and constrained resources. Code that works in a unit test can still fail when traffic, data volume, or dependency latency increases.

JVM Memory at Runtime
Architecture
JVM Memory β”œβ”€β”€ Heap β”‚ β”œβ”€β”€ Objects β”‚ β”œβ”€β”€ Arrays β”‚ └── Collections β”œβ”€β”€ Thread Stacks β”‚ β”œβ”€β”€ Method Frames β”‚ β”œβ”€β”€ Local Variables β”‚ └── References β”œβ”€β”€ Metaspace β”‚ └── Class Metadata └── Native Memory β”œβ”€β”€ Thread Structures └── Direct Buffers

Observability

Use structured logs, metrics, distributed traces, and health signals. Include correlation identifiers while avoiding sensitive data. Measure latency distributions rather than averages alone.

Profile before optimizing. Java Flight Recorder, heap dumps, thread dumps, async profilers, and production metrics provide stronger evidence than assumptions.

Java Core

Interview Questions

  1. What is the difference between the JDK, JRE, and JVM?
  2. Why is Java considered platform independent?
  3. How does JIT compilation improve performance?
  4. What is the difference between stack and heap memory?
  5. How do local variables, instance fields, and static fields differ?
  6. What is the difference between primitive and reference types?
  7. What risks can autoboxing and unboxing introduce?
  8. Why should BigDecimal be used for money?
  9. Why is String immutable?
  10. What is the difference between == and equals?
  11. When should StringBuilder be used?
  12. What are the time complexities of common array operations?
  13. Why should a class make defensive copies of arrays?
  14. Explain the four pillars of object-oriented programming.
  15. Why is composition often preferred over inheritance?
  16. Which important methods are defined by Object?
  17. What contract must equals and hashCode satisfy?
  18. What happens when a mutable map key changes after insertion?
  19. How can object allocation affect application performance?
  20. Which tools investigate high CPU, memory growth, or blocked threads?

Java Core

Best Practices

Professional Java code should make correctness visible, keep responsibilities clear, and remain diagnosable in production.

Design

  • Model business concepts with meaningful types and names
  • Keep classes cohesive and methods focused
  • Protect invariants through constructors and domain methods
  • Use immutable objects when practical
  • Introduce interfaces at meaningful boundaries

Correctness

  • Validate input at system boundaries
  • Use BigDecimal for exact decimal calculations
  • Override equals and hashCode together
  • Define ownership for mutable data
  • Preserve exception causes

Reliability

  • Bound queues, pools, retries, and caches
  • Configure timeouts for remote dependencies
  • Close resources with try-with-resources
  • Write tests for behavior and failure paths
  • Profile before optimizing
Production-Ready Java
Architecture
Production Quality β”œβ”€β”€ Correctness β”œβ”€β”€ Readability β”œβ”€β”€ Testability β”œβ”€β”€ Observability β”œβ”€β”€ Reliability β”œβ”€β”€ Security └── Measured Performance

The goal is not to use every Java feature. The goal is to build software whose behavior is easy to understand, verify, operate, and evolve.