Encapsulation
Protect object data with private fields and expose it safely through public getters and setters.
Overview
Key Points
- •private fields can only be accessed from inside the class
- •public getters (getName()) provide read access
- •public setters (setName(String name)) provide write access with validation
- •Keeping fields private prevents external code from putting objects in invalid states
- •Encapsulation also lets you refactor internal implementation without changing the public API
- •By convention: getter = getFieldName(), setter = setFieldName(type value)
- •boolean getters often use "is": isActive(), isValid()
Examples
private fields with getters and setters
class Person {
private String name;
private int age;
// Constructor
Person(String name, int age) {
this.name = name;
setAge(age); // use setter so validation runs
}
// Getter
public String getName() { return name; }
// Setter with validation
public void setName(String name) {
if (name == null || name.isEmpty()) {
System.out.println("Name cannot be empty.");
return;
}
this.name = name;
}
public int getAge() { return age; }
public void setAge(int age) {
if (age < 0 || age > 150) {
System.out.println("Invalid age: " + age);
return;
}
this.age = age;
}
@Override
public String toString() {
return name + " (age " + age + ")";
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person("Alice", 28);
System.out.println(p);
p.setAge(-5); // Invalid age: -5
p.setAge(32); // OK
System.out.println(p); // Alice (age 32)
// p.age = 200; // compile error - age is private
}
}p.age = 200 would be a compile error. External code must go through setAge(), which validates the value.
Read-only field (no setter)
class Circle {
private final double radius; // can't change after construction
Circle(double radius) {
if (radius <= 0) throw new IllegalArgumentException("Radius must be positive");
this.radius = radius;
}
public double getRadius() { return radius; }
public double area() { return Math.PI * radius * radius; }
public double circumference() { return 2 * Math.PI * radius; }
}
public class Main {
public static void main(String[] args) {
Circle c = new Circle(5.0);
System.out.printf("Radius: %.1f%n", c.getRadius());
System.out.printf("Area: %.2f%n", c.area());
// c.radius = 10; // compile error
// new Circle(-1); // throws IllegalArgumentException
}
}final + private + no setter = effectively immutable field. The class enforces that radius is always positive.
Computed property - getter that calculates
class Rectangle {
private int width;
private int height;
Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getWidth() { return width; }
public int getHeight() { return height; }
// Computed - no corresponding field
public int getArea() { return width * height; }
public int getPerimeter() { return 2 * (width + height); }
public boolean isSquare() { return width == height; }
}
public class Main {
public static void main(String[] args) {
Rectangle r = new Rectangle(4, 4);
System.out.println("Area: " + r.getArea());
System.out.println("Square? " + r.isSquare());
}
}Getters don't have to return stored fields - they can compute values on the fly. Callers don't need to know the difference.
Common Mistakes
- Making setters that just assign without any validation - misses the point of encapsulation
- Returning a mutable object from a getter (e.g., an array) - the caller can modify it, bypassing encapsulation; return a copy instead
- Forgetting to use the setter in the constructor - if the constructor sets the field directly, validation in the setter is bypassed
Note
As a rule of thumb, make all fields private by default. Only add getters/setters for fields that genuinely need to be accessible, and only add setters if the field needs to change after construction.
Exercises
Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.
Completed: 0 / 4 exercises
Knowledge Check
Question 1 of 4