Object-oriented programming means organising code around things that hold data and know how to act on it, rather than around loose functions.
public class BankAccount {
private double balance; // the data
public void deposit(double amount) { // what it can do
balance += amount;
}
}The data and the operations on it live together. That is the whole idea; everything below is a consequence of it.
Class and object#
A class is the definition. An object is one made from it.
public class BankAccount {
private String owner;
private double balance;
public BankAccount(String owner, double opening) { // constructor
this.owner = owner;
this.balance = opening;
}
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Deposit must be positive");
}
balance += amount;
}
public double getBalance() {
return balance;
}
}BankAccount a = new BankAccount("Ada", 100);
BankAccount b = new BankAccount("Sam", 50);
a.deposit(25);
System.out.println(a.getBalance()); // 125.0
System.out.println(b.getBalance()); // 50.0One class, two independent objects. this.owner distinguishes the field from the parameter of the same name.
Encapsulation#
Fields are private so nothing outside the class can put them into an invalid state.
// If balance were public:
account.balance = -5000; // nothing stops this
// Because it is private, changes go through a method that can check:
account.deposit(-5000); // throws IllegalArgumentExceptionThat is the actual point of getters and setters — not ceremony, but a place to put the rules. If a setter does nothing but assign, ask whether the field should be public or the object immutable instead.
Inheritance#
public class SavingsAccount extends BankAccount {
private double rate;
public SavingsAccount(String owner, double opening, double rate) {
super(owner, opening); // must be the first line
this.rate = rate;
}
public void addInterest() {
deposit(getBalance() * rate);
}
}SavingsAccount gets everything BankAccount has and adds to it. Use it only for a genuine “is a” relationship — a savings account is a bank account.
Polymorphism#
The word means “many shapes”. In practice: different objects respond to the same method call in their own way, and the calling code does not need to know which is which.
public abstract class Shape {
public abstract double area();
}
public class Circle extends Shape {
private final double r;
public Circle(double r) { this.r = r; }
public double area() { return Math.PI * r * r; }
}
public class Square extends Shape {
private final double side;
public Square(double side) { this.side = side; }
public double area() { return side * side; }
}List<Shape> shapes = List.of(new Circle(2), new Square(3));
double total = 0;
for (Shape s : shapes) {
total += s.area(); // each calculates its own way
}The loop knows nothing about circles or squares. Add a triangle later and the loop does not change — that is the payoff.
Interfaces#
An interface is a promise about what an object can do, with no implementation:
public interface Payable {
double calculatePay();
}
public class Employee implements Payable {
public double calculatePay() { return 3000; }
}
public class Contractor implements Payable {
public double calculatePay() { return hours * rate; }
}A class can implement many interfaces but extend only one class, which is another reason to prefer interfaces plus composition over deep inheritance trees.
toString, equals and hashCode#
@Override
public String toString() {
return "BankAccount{owner='" + owner + "', balance=" + balance + "}";
}Without toString, printing an object gives you something like BankAccount@1b6d3586. If you override equals, you must override hashCode too, or the object will misbehave in a HashMap or HashSet.
For simple data holders, a record (Java 16+) generates all three for you:
public record Point(int x, int y) {}Questions people ask#
What does static mean?
It belongs to the class rather than any object, so you call it as Math.max(1, 2) without creating anything. main is static because nothing exists yet when the program starts.
Abstract class or interface?
An interface when you only need to state what something can do. An abstract class when you also want to share code between the implementations.
What is final?
On a field, it cannot be reassigned after construction. On a method, it cannot be overridden. On a class, it cannot be extended.
Why is everything a class in Java?
A deliberate design choice from the language’s origins. It costs a few extra lines for small programs and gives a consistent structure on large ones.