java-encapsulamiento-modificadores-acceso

Encapsulation in Java: private, public and getters

  • 3 min

The encapsulation consists of limiting access to an object’s internal state and exposing controlled operations.

In the previous article, we left our Robot objects with their attributes exposed. Any part of the program could do this:

miRobot.nivelBateria = -5000; // Breaking the logic!
miRobot.nombre = null;        // Crashing the system
Copied!

If we allow direct access to the data, we lose control. We cannot validate whether the data is correct.

This problem leads us to the second pillar of object-oriented programming: encapsulation.

Encapsulation consists of hiding the internal details of a class and exposing only what is safe and necessary.

Access Modifiers

Java provides four security levels to decide “who can see what”. They apply to classes, attributes, and methods.

ModifierSame ClassSame PackageSubclass (other package)World (Everyone)
public
protected
Default
private

This is the most permissive level. Anyone from anywhere in the project can access it.

  • Usage: Methods that are part of the public API (what you want others to use).

This is the most restrictive level. Only the class itself can see that data. Not even neighboring classes.

  • Usage: ALL attributes should be private by default.

This is an intermediate level. It allows access to:

  1. The class itself.
  2. Classes in the same package.
  3. Subclasses (children), even if they are in another package.
  • Usage: We will explore this in depth when we get to Inheritance.

If you do not write anything (neither public, nor private…), Java applies this level by default. It allows access to any class within the same package.

  • Usage: Internal utility classes that only serve to help other classes in the module.

Getters and Setters: The Bouncers

If we make all attributes private (as it should be), no one will be able to read or change our Robot’s name. The object would be a useless black box.

To allow controlled access, we create special public methods: Getters (to read) and Setters (to write).

Why do this instead of leaving it public?

The advantage of a setter is that it allows validations to be executed before saving the data.

public class Robot {
    // 1. PRIVATE ATTRIBUTE (Armored)
    private int nivelBateria;

    // 2. GETTER (Reading)
    public int getNivelBateria() {
        return this.nivelBateria;
    }

    // 3. SETTER (Controlled writing)
    public void setNivelBateria(int nuevoNivel) {
        // VALIDATION: Prevent absurd values
        if (nuevoNivel < 0) {
            System.out.println("Error: Battery cannot be negative. Setting it to 0.");
            this.nivelBateria = 0;
        } else if (nuevoNivel > 100) {
            this.nivelBateria = 100;
        } else {
            this.nivelBateria = nuevoNivel;
        }
    }
}
Copied!

Now, from the outside:

Robot r = new Robot();
// r.nivelBateria = -50; // ERROR: Does not compile, it's private.

r.setNivelBateria(-50); // The setter intercepts the error and corrects it to 0.
System.out.println(r.getNivelBateria()); // We read safely.
Copied!

Read-Only Objects (Immutability)

If you want an attribute to be readable but not modifiable once created, simply do not create the Setter.

public class DNI {
    private String numero;

    public DNI(String n) {
        this.numero = n;
    }

    // Only Getter. No one will be able to change the ID number after creation.
    public String getNumero() {
        return numero;
    }
}
Copied!