Skip to content
Theory · Classes and objects · lesson 5 of 10

Methods and encapsulation

Imagine that any passer-by could walk into your house and rearrange the furniture. Normally this does not happen: a guest rings the doorbell, and the owner decides what to hand over and what to allow. An object needs such a door too. Let's look at a bank account that does not have one yet:

JAVA
public class Main {
    public static void main(String[] args) {
        Account acc = new Account();
        acc.deposit(100);
        System.out.println(acc.balance); // 100
    }
}

class Account {
    int balance;
    void deposit(int amount) {
        balance += amount;
    }
}

Let's go through the program step by step:

  1. The class Account describes an account with one field, balance.
  2. The method deposit is declared without static. You call it through a dot on a particular account: acc.deposit(100).
  3. Inside deposit, the name balance means the field of the account the method was called on.
  4. println prints 100.

So far everything works. But the field balance is open, and anything can be written into it:

JAVA
acc.balance = -500;
System.out.println(acc.balance); // -500

The account shows minus 500, although nobody took money out of it. The method deposit could check the amount, but the outside code goes around it and writes straight into the field.

An object's methods

Until now, you have written only static methods, and main called them by name. A method without static belongs to the object: it is an action of a particular house, not of the blueprint. You have already called such methods on strings: word.length(). What matters about such a method:

  • you call it through an object: acc.deposit(100);
  • inside it there is this, a reference to the object the method was called on;
  • a field name without this. means a field of this object: balance in deposit is this.balance;
  • two accounts have their own fields, so a.deposit(100) does not change the account b.

The word private

Let's close the field to other code. An access modifier is a word before a field or a method that decides which code may reach it. The modifier private opens a field only to the code inside its class: private int balance;. Now the line acc.balance = -500; in main does not compile:

TEXT
error: balance has private access in Account

The ban covers reading too: System.out.println(acc.balance) gives the same error. The boundary is the class, not the file: Main and Account sit in one file, Main.java, but Main is another class. The compiler catches the violation, so such a program never gets to run.

A getter and a check

If the field is closed, how do you find out the balance? The class itself opens what is needed, through its own methods:

JAVA
class Account {
    private int balance;
    int getBalance() {
        return balance;
    }

    void deposit(int amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
}

Let's see what changed:

  1. private int balance; makes the field visible only inside Account.
  2. getBalance() returns the value of the field. Such a method is called a getter, and by tradition its name starts with get.
  3. deposit checks the amount: a negative or zero deposit does not change the balance.
  4. There is no method that would write any number into balance, and that is on purpose.

A method that writes a new value into a field is called a setter, and its name starts with set: setOwner(String owner). But a setter that accepts anything in effect opens the field.

This approach is called encapsulation: the fields are closed, and from outside only operations that keep the object's rules are available. The account itself decides which deposit to accept, and it can never go below zero. Interviewers often ask you to explain encapsulation with an example. The account with deposit makes a good answer: the point is not a getter and a setter for every field, but that the object protects its data.

Here the door picture is not exact. Each house has its own door, but private closes a field to other classes, not to other objects. A method of the class Account can read the balance of another account if it gets a reference to it.

Four access levels

Java has four access levels, from the most closed to the most open:

  • private: only the code inside its own class;
  • no modifier: the classes of the same package, that is, the same group of classes. All classes of a program in one file sit in one package, which is why any of your code has seen such fields so far;
  • protected: the same as no modifier, plus subclasses in other packages;
  • public: any code, as with the class Main and the method main.

A common interview question: what does a class member without a modifier mean? It is not public: only the classes of its package see it.

Common mistake

An object's method is called through the class name:

JAVA
Account.deposit(100); // ✗
TEXT
error: non-static method deposit(int) cannot be referenced from a static context

The program does not compile. deposit changes the balance of a particular account, and Account.deposit(100) does not say which one. Create an object first, then call the method on it: Account acc = new Account();, then acc.deposit(100);.

Key points

  • A method without static belongs to the object: you call it through the object, and balance inside it is that object's field.
  • private closes a field to every other class, even in the same file: acc.balance from main does not compile.
  • Encapsulation means closed fields and open operations that keep the object's rules: a getter and a deposit with a check.
  • The access levels from closed to open: private, no modifier (package), protected, public.