# Encapsulation

Course: [Java](https://www.devtutorials.ai/learn/java) · Section: Object-Oriented Programming
Source: https://www.devtutorials.ai/learn/java/object-oriented-programming/encapsulation

> Protect object data with private fields, getters and setters, validation and read-only patterns, and see how encapsulation differs from data hiding.

Think about a bank locker.

Your valuables sit inside a locked box. You cannot reach in and take something out. You go to the counter, show your ID, and the bank checks it. Only then do you get access.

The bank does not stop you from using your locker. It just makes sure every visit goes through a proper check.

Encapsulation works the same way. An object locks its data away, and other code can only reach that data through the object's own methods. Those methods can check every request.

In simple words: encapsulation keeps an object's data private and lets other code use it only through safe methods.

## What You Will Learn

In this lesson, you will learn:

1. What encapsulation is, and why it protects your objects.
2. How data hiding with `private` works.
3. How access modifiers control what others can reach.
4. How to write getters and setters.
5. How setters can reject invalid values.
6. How to make data read-only.
7. The benefits of encapsulation.
8. How encapsulation differs from data hiding.

## Start With a Working Example

Press **Run**.

```java runnable title="The account refuses a bad withdrawal"
public class Main {
    public static void main(String[] args) {
        BankAccount acc = new BankAccount("Asha", 1000);

        acc.deposit(500);
        acc.withdraw(300);
        acc.withdraw(5000);
        acc.deposit(-200);

        System.out.println("Final balance: " + acc.getBalance());
    }
}

class BankAccount {
    private String owner;
    private int balance;

    BankAccount(String owner, int balance) {
        this.owner = owner;
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public void deposit(int amount) {
        if (amount <= 0) {
            System.out.println("Deposit refused: amount must be positive.");
            return;
        }
        balance += amount;
        System.out.println("Deposited " + amount);
    }

    public void withdraw(int amount) {
        if (amount > balance) {
            System.out.println("Withdrawal refused: not enough money.");
            return;
        }
        balance -= amount;
        System.out.println("Withdrew " + amount);
    }
}
```

Expected output:

```text
Deposited 500
Withdrew 300
Withdrawal refused: not enough money.
Deposit refused: amount must be positive.
Final balance: 1200
```

`balance` is `private`. The only ways to change it are `deposit` and `withdraw`, and both check the amount first. So the balance can never go below zero, and nobody can deposit a negative amount.

### Try This

In `main`, add the line `acc.balance = 9999999;`. The program no longer compiles. Outside code cannot touch the balance directly.

## Introduction to Encapsulation

**Encapsulation** means bundling an object's **data** and the **methods** that work on that data into one class, and controlling access from outside.

It is one of the four main ideas of object-oriented programming:

```text
Encapsulation = Protect data
Abstraction   = Hide details
Inheritance   = Is a
Polymorphism  = Many behaviours
```

Without encapsulation, any code anywhere could change any field to anything. One wrong line, like `student.age = -5;`, would put an object into an impossible state. Finding that line in a big program is hard.

With encapsulation, every change goes through the object's own methods. The object can say "no" to bad values. So the object always stays valid.

## Data Hiding

**Data hiding** means making fields `private`, so code outside the class cannot read or change them directly.

```java runnable expect-error title="Outside code cannot reach a private field"
public class Main {
    public static void main(String[] args) {
        Student s = new Student();
        s.age = -5;
        System.out.println(s.age);
    }
}

class Student {
    private int age;
}
```

The compiler reports `age has private access in Student`.

The impossible age `-5` never gets in. The mistake is caught before the program even runs.

Hiding the data is the first step. The next step is giving outside code a safe way in.

## Access Control

You already know the four access levels. In encapsulation, each one has a typical job:

| Modifier | Who can use it | Typical use in encapsulation |
|---|---|---|
| `private` | Only this class | Fields, and helper methods |
| default (none) | Classes in the same package | Helpers shared inside one package |
| `protected` | Same package, plus child classes | Members child classes need to customise |
| `public` | Everyone | The methods other code is meant to call |

The usual pattern is simple:

- **Fields:** `private`.
- **Methods other code needs:** `public`.
- **Internal helper methods:** `private`.

**Easy Way to Remember:** **data private, actions public**. Open up a member only when another class has a real need for it.

## Getters and Setters

If a field is `private`, how does other code read or change it? Through two kinds of public methods:

- A **getter** returns a field's value. Its name starts with `get`, such as `getName()`.
- A **setter** changes a field's value. Its name starts with `set`, such as `setName(String name)`.

For a `boolean` field, the getter usually starts with `is`, such as `isActive()`.

```java runnable title="Read and change private fields through methods"
public class Main {
    public static void main(String[] args) {
        Product p = new Product();

        p.setName("Wireless Mouse");
        p.setPrice(899);
        p.setInStock(true);

        System.out.println(p.getName());
        System.out.println("Price: " + p.getPrice());
        System.out.println("In stock? " + p.isInStock());
    }
}

class Product {
    private String name;
    private int price;
    private boolean inStock;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public boolean isInStock() {
        return inStock;
    }

    public void setInStock(boolean inStock) {
        this.inStock = inStock;
    }
}
```

Expected output:

```text
Wireless Mouse
Price: 899
In stock? true
```

Every field is `private`. `main` reads and changes them only through the getters and setters.

At first, this can look like extra work for no gain. The gain comes in the next section: once every change goes through a setter, the setter can check it.

| Field | Getter | Setter |
|---|---|---|
| `private String name;` | `public String getName()` | `public void setName(String name)` |
| `private int price;` | `public int getPrice()` | `public void setPrice(int price)` |
| `private boolean inStock;` | `public boolean isInStock()` | `public void setInStock(boolean inStock)` |

## Property Validation

**Validation** means checking a value before accepting it. A setter is the perfect place, because it is the only door into the field.

A setter can:

- reject a bad value and keep the old one;
- correct a value, such as trimming spaces;
- throw an error, so the caller knows they made a mistake.

```java runnable title="Setters reject values that make no sense"
public class Main {
    public static void main(String[] args) {
        Student s = new Student();

        s.setName("  Meena  ");
        s.setAge(16);
        s.setMarks(88);

        s.setAge(-3);
        s.setMarks(140);
        s.setName("");

        System.out.println(s.getName() + ", age " + s.getAge() + ", marks " + s.getMarks());
    }
}

class Student {
    private String name = "Unknown";
    private int age;
    private int marks;

    public String getName() { return name; }
    public int getAge() { return age; }
    public int getMarks() { return marks; }

    public void setName(String name) {
        if (name == null || name.isBlank()) {
            System.out.println("Name refused: it cannot be empty.");
            return;
        }
        this.name = name.trim();
    }

    public void setAge(int age) {
        if (age < 0 || age > 120) {
            System.out.println("Age refused: " + age);
            return;
        }
        this.age = age;
    }

    public void setMarks(int marks) {
        if (marks < 0 || marks > 100) {
            System.out.println("Marks refused: " + marks);
            return;
        }
        this.marks = marks;
    }
}
```

Expected output:

```text
Age refused: -3
Marks refused: 140
Name refused: it cannot be empty.
Meena, age 16, marks 88
```

The three bad values were all refused. The object kept its last good values.

`setName` also trimmed the extra spaces from `"  Meena  "`. Callers did not have to remember to do that.

### Throwing an Error Instead

Printing a message is fine for learning. In real programs, a setter usually **throws an exception**, so the mistake cannot be ignored:

```text
public void setAge(int age) {
    if (age < 0 || age > 120) {
        throw new IllegalArgumentException("Age must be 0 to 120");
    }
    this.age = age;
}
```

`throw` stops the current method with an error. You will learn how to handle errors in the Exception Handling chapter.

### Validate in the Constructor Too

A constructor is another door into the object. If it sets fields directly, it can let bad values in. A simple fix is to have the constructor call the setters.

```text
Student(String name, int age) {
    setName(name);
    setAge(age);
}
```

## Read-Only Data Patterns

Some data should be set once and never changed. An order ID, a date of birth or an account number are good examples.

There are three common ways to make data read-only.

### 1. Getter With No Setter

Provide a getter, but no setter. Outside code can read the value, but cannot change it.

### 2. final Fields Set in the Constructor

Mark the field `final`. It must be set in the constructor, and it can never be changed again, even by the class's own methods.

### 3. A Record

A record makes **every** field final and read-only automatically. You learned records in the Enums lesson.

```java runnable title="An ID that can be read but never changed"
public class Main {
    public static void main(String[] args) {
        Order o = new Order("ORD-2041", "Asha");

        System.out.println("Order ID: " + o.getId());
        System.out.println("Customer: " + o.getCustomer());

        o.setStatus("SHIPPED");
        System.out.println("Status: " + o.getStatus());

        Receipt r = new Receipt("ORD-2041", 1499);
        System.out.println("Receipt: " + r.orderId() + " paid " + r.amount());
    }
}

class Order {
    private final String id;
    private final String customer;
    private String status = "PLACED";

    Order(String id, String customer) {
        this.id = id;
        this.customer = customer;
    }

    public String getId() { return id; }
    public String getCustomer() { return customer; }

    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
}

record Receipt(String orderId, int amount) {
}
```

Expected output:

```text
Order ID: ORD-2041
Customer: Asha
Status: SHIPPED
Receipt: ORD-2041 paid 1499
```

`id` and `customer` are `final` and have only getters. They are fixed for the life of the order.

`status` has a getter and a setter, because an order's status really does change.

`Receipt` is a record, so both of its values are read-only with no extra code.

### Protecting Arrays and Lists

A getter that returns an array hands out a **reference** to the original. Outside code could then change its contents.

To stay safe, return a **copy**:

```text
public int[] getMarks() {
    return Arrays.copyOf(marks, marks.length);
}
```

Now outside code gets its own copy. Changing it does not change the object's data.

`final` on an array field only stops the field from pointing to a **different** array. The values **inside** the array can still be changed. Return copies from getters to keep the contents safe.

| Pattern | Who can change the value | Example |
|---|---|---|
| Getter and setter | Anyone, through the setter's checks | `status` |
| Getter only | Only the class's own methods | A running total |
| `final` field with a getter | Nobody, after the constructor | An order ID |
| Record component | Nobody, ever | A receipt |

Not every field needs a setter. Add a setter only when outside code has a real reason to change the value. Fewer setters means fewer ways for an object to end up in a bad state.

## Benefits of Encapsulation

### Valid Objects

Setters and methods check every change. An object can never end up with a negative age or a balance below zero.

### Freedom to Change the Inside

Other code only uses the public methods. You can change how data is stored, as long as the methods still work the same way.

For example, a class could switch from storing `age` to storing `dateOfBirth`. Its `getAge()` method would just work out the age. Nobody using the class would notice.

### Easier Debugging

If a value goes wrong, there is only one place it can be changed: the setter or method. You put one check or one print statement there.

### Control Over Access

You decide what is read-only, what is changeable and what is completely hidden.

### Clear, Simple Use

Other programmers see a small set of public methods. They do not need to understand the inner details.

## Encapsulation vs Data Hiding

The two terms are often used as if they were the same. They are closely related, but they are not identical.

- **Data hiding** is one technique: making fields `private`, so outside code cannot reach them.
- **Encapsulation** is the bigger idea: grouping data with the methods that manage it, and controlling access through those methods. Data hiding is one part of it.

| | Data hiding | Encapsulation |
|---|---|---|
| What it is | A technique | A design principle |
| Main focus | Keeping data out of reach | Bundling data with the methods that protect it |
| Achieved with | `private` fields | `private` fields plus public getters, setters and methods |
| Answers | "Who can see this field?" | "How should this object's data be used?" |
| Relationship | One part of encapsulation | Includes data hiding |

Think of a medicine capsule. The shell keeps the medicine hidden: that is data hiding. The capsule as a whole, a sealed package with the right dose inside, designed to be taken in one safe way, is encapsulation.

## Common mistakes

The following mistakes are common when hiding fields behind getters and setters. Each row shows what you see, why it happens and how to fix it.

| Symptom | Cause | Fix |
|---|---|---|
| Fields hold impossible values | Fields are `public`, or setters skip checks | Make fields `private` and validate in setters |
| A setter seems to do nothing | `name = name;` without `this` | Write `this.name = name;` |
| `has private access` | Outside code used a private field | Use the getter or setter |
| `cannot assign a value to final variable` | A setter tried to change a `final` field | Remove the setter |
| An object's array changes unexpectedly | A getter returned the original array | Return a copy |
| Bad values get in through `new` | The constructor sets fields directly | Call the setters from the constructor |

## What to Remember

- Encapsulation bundles data with the methods that manage it, and controls access from outside.
- Data hiding makes fields `private`. It is one part of encapsulation.
- Getters read private fields. Setters change them. Boolean getters usually start with `is`.
- Setters are the place to validate values, so objects always stay valid.
- Make data read-only with a getter and no setter, a `final` field, or a record. Return copies of arrays.
- Encapsulation keeps objects valid, makes code easier to change and debug, and gives you control over access.

Next: Exception Handling
