Last 30 Days
No notifications
A class is a blueprint; an object is an instance built from it.
public class Point {
// === fields (state) ===
private double x;
private double y; // === constructor ===
public Point(double x, double y) {
this.x = x;
this.y = y;
}
// === instance methods (behaviour) ===
public double distanceTo(Point other) {
double dx = this.x - other.x;
double dy = this.y - other.y;
return Math.sqrt(dx * dx + dy * dy);
}
// === getters / setters (encapsulation) ===
public double getX() { return x; }
public double getY() { return y; }
}
// usage
Point a = new Point(0, 0);
Point b = new Point(3, 4);
System.out.println(a.distanceTo(b)); // 5.0
public record Point(double x, double y) {}Point p = new Point(3, 4);
p.x(); // accessor (no get prefix)
System.out.println(p); // Point[x=3.0, y=4.0] (auto toString)
A record auto-generates: constructor, accessors, equals, hashCode, toString, and is implicitly final & immutable.
Hide internal state behind methods so the class controls how it's read/written.
public class BankAccount {
private double balance; // hidden
public void deposit(double a) {
if (a <= 0) throw new IllegalArgumentException();
balance += a;
}
public double getBalance() { return balance; }
}public class Student {
// === Fields ===
private String name;
private int age;
private static int totalStudents = 0; // shared by all instances // === Constructors ===
public Student(String name, int age) {
this.name = name;
this.age = age;
totalStudents++;
}
public Student(String name) {
this(name, 18); // delegate to the other constructor
}
// === Instance methods ===
public String greet() {
return "Hi, I'm " + name + " (" + age + ")";
}
// === Static method ===
public static int howMany() { return totalStudents; }
// === Getters / setters ===
public String getName() { return name; }
public int getAge() { return age; }
public void setAge(int age) {
if (age < 0) throw new IllegalArgumentException();
this.age = age;
}
// === toString — for debugging / logging ===
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}
new & the Object LifecycleStudent s = new Student("Asha", 19);1. Allocate memory on the heap.
2. Set fields to their defaults (0, null, false).
3. Run the constructor (with any explicit field initialisers first).
4. Return the reference.
When no references point to the object, the garbage collector reclaims its memory.
thisthis is the implicit reference to the current instance. Used:
this.x = x.this(args) — must be the first statement.return this.this(...).public Box() { this(0, 0, 0); }
public Box(int s) { this(s, s, s); }
public Box(int w, int h, int d) { /* full ctor */ }| Modifier | Class | Package | Subclass | World |
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ✅ | ❌ |
| (default) | ✅ | ✅ | ❌ | ❌ |
private | ✅ | ❌ | ❌ | ❌ |
Default rule: fields private, methods public unless you have a reason.
public class MathUtil {
public static final double PI = 3.14159;
public static double square(double x) { return x * x; }
}
MathUtil.square(5); // no instance neededstatic fields are shared across all instances (and live forever).static methods can't access this or non-static fields.Without encapsulation:
public class Counter {
public int count;
}
c.count = -50; // breaks invariant: counts shouldn't be negativeWith encapsulation:
public class Counter {
private int count;
public void inc() { count++; }
public int get() { return count; }
}The class now controls how its state changes — it can validate, log, fire events.
> Convention: getters return the value, setters validate and assign. Records remove this boilerplate when state should be immutable.
toString, equals, hashCodeOverride toString() for readable logging:
@Override
public String toString() {
return "Point(" + x + ", " + y + ")";
}For value equality, override BOTH equals and hashCode — they go together. IntelliJ / VS Code generate them for you (Alt+Insert / right-click → Generate). Or use a record.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point p)) return false; // pattern match (Java 16+)
return Double.compare(p.x, x) == 0 && Double.compare(p.y, y) == 0;
}@Override
public int hashCode() {
return Objects.hash(x, y);
}
public record Point(double x, double y) {}
public record User(String name, int age, List<String> tags) {}A record is:
final.private final.x() not getX()), equals, hashCode, toString.public record Email(String value) {
public Email {
if (value == null || !value.contains("@"))
throw new IllegalArgumentException();
value = value.trim().toLowerCase(); // normalise
}
}> Use records for immutable data carriers (DTOs, return values, keys). Use full classes when you need mutable state, framework hooks, or inheritance.
public class Outer {
private int value = 10; class Inner { int read() { return value; } } // inner — needs Outer instance
static class Nested { int compute() { return 42; } } // static — independent
}
Plus records, enums, anonymous classes, and (Java 16+) local records inside methods. Deeper coverage is the OOP topic.
| Bug | Fix | |||
Forgot new | Use new ClassName(args) (or factory method) | |||
| Static method using instance field | Make method non-static or pass the instance | |||
this(...) not first in constructor | Move it to the first line | |||
| Mutable field accessible publicly | Make it private + provide controlled accessors | |||
| Records used for mutable state | Records are immutable by design — use a regular class | |||
Defining equals without hashCode | Always override both (they're a contract) | Cheat-Sheet | Need | Code |
| Instance | new ClassName(args) | |||
| Field access | obj.field (if visible) | |||
| Method call | obj.method(args) | |||
| Static call | ClassName.method(args) | |||
| Constructor chain | this(args) (first stmt) | |||
| Disambiguate | this.field = field | |||
| Hide internals | private fields, public accessors | |||
| Immutable record | record Foo(int a, int b) {} | |||
| Validate in record | compact constructor | |||
| Override toString | @Override public String toString() |
You can now design your own types. Phase 2 done. Phase 3 next: inheritance and the OOP toolkit.