fields, constructors, methods, static members - where each lives in memory
Constructor chaining
this(...) eliminates duplication; telescoping constructors vs builder
this reference
disambiguation, chaining, passing self as argument
Static members
class-level state and behaviour; initialisation order
Object lifecycle
allocation on heap, GC eligibility, finalize is dead - use Cleaner
equals / hashCode contract
symmetry, transitivity, consistency; why violating it breaks HashMap
toString
always override it - the debugger and logs will thank you
Records (Java 16+)
immutable data carriers; compact constructor for validation
var with objects
type inference at declaration site
Memory Layout
Stack Heap
───── ────────────────────────────────────────
main() frame ┌─────────────────────┐
p ──────────────────────► │ Person object │
│ name: ──────────────┼──► "Alice" (String pool)
│ age: 30 │
│ email: ─────────────┼──► "alice@x.com"
└─────────────────────┘
Two variables, one object:
a ──────────────────────► ┌───────────────┐
b ───────────────────────►│ same object │
└───────────────┘
a == b → true (same reference)
a.equals(b) → true (same content, if properly implemented)
equals / hashCode Contract
┌──────────────────────────────────────────┐
│ THE CONTRACT │
│ │
Reflexive ──► │ x.equals(x) == true │
Symmetric ──► │ x.equals(y) == y.equals(x) │
Transitive ──► │ x.equals(y) && y.equals(z) → x.equals(z)│
Consistent ──► │ same result across multiple calls │
Null-safe ──► │ x.equals(null) == false │
│ │
│ CRITICAL LINK: │
│ x.equals(y) → x.hashCode()==y.hashCode()│
│ (converse NOT required) │
└──────────────────────────────────────────┘
Violation consequence:
HashSet<Point> set = new HashSet<>();
set.add(new Point(1, 2));
set.contains(new Point(1, 2)); // FALSE if hashCode broken!
Static Initialisation Order
Class loaded
│
▼
1. static fields (in declaration order)
2. static initialiser blocks (in declaration order)
│
▼
new MyClass()
│
▼
3. instance fields (in declaration order)
4. instance initialiser blocks
5. constructor body
Record vs Class
// class - 30+ lines boilerplate
class Point {
private final int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int x() { return x; }
public int y() { return y; }
@Override public boolean equals(Object o) { ... }
@Override public int hashCode() { ... }
@Override public String toString() { ... }
}
// record - 1 line, same semantics
record Point(int x, int y) {}
// compact constructor - add validation
record Point(int x, int y) {
Point { // no parameter list here
if (x < 0 || y < 0)
throw new IllegalArgumentException("negative coordinate");
// x and y are implicitly assigned after the block
}
}
Source Files
File
What it Demonstrates
ClassAnatomy.java
Fields, constructors, this, static members, initialisation order
EqualsHashCode.java
Correct equals/hashCode with all five contract rules, HashMap/HashSet behaviour
RecordsDemo.java
Records, compact constructors, record patterns (Java 21), with-style copying
BankAccount.java
Full class design: encapsulation, invariants, equals/hashCode, toString, static factory, object lifecycle
Running
cd module-07-oop-classes
mvn test# compile + run all JUnit 5 tests
mvn compile exec:java -Dexec.mainClass=com.javatraining.oop.ClassAnatomy