Beginner 25 min read

Access Modifiers

By the end of this lesson, you'll be able to:

  • Explain what public, private, and protected control
  • Choose an appropriate access modifier for a field or method
  • Recognize private as the safer default for internal implementation details

Prerequisites: "Properties: get and set"

The main three: private, public, protected

public class BankAccount {
    private Decimal balance; // only visible inside this class

    public Decimal getBalance() { // visible to any code that has a BankAccount
        return balance;
    }
}
  1. public class BankAccount { — the class itself is public, so any code in the org can reference it.
  2. private Decimal balance; — private means this field is visible only inside this class — the safest default for internal details.
  3. (blank line)
  4. public Decimal getBalance() { — public means this method is visible to any code that has a BankAccount.
  5. return balance; — returns the private value through a controlled, public door.
  6. } — closes the method.
  7. } — closes the class.
  • private — visible only inside the same class.
  • public — visible to any code, anywhere in the org.
  • protected — visible inside the class and any class that extends it (covered in a later module on inheritance).

Why hide balance behind private?

BankAccount account = new BankAccount();
// account.balance = 1000000;   // compile error — balance is private
System.debug(account.getBalance()); // fine — getBalance() is public
  1. BankAccount account = new BankAccount(); — creates an account.
  2. // account.balance = 1000000; — commented out because this line would not compile — balance is private, so code outside BankAccount cannot touch it directly.
  3. System.debug(account.getBalance()); — this works fine — getBalance() is public.

If balance were public, any code anywhere could set it directly to anything, bypassing whatever business rules should govern it (like "never allow a negative balance"). Making it private and exposing controlled access through a public method — a pattern called encapsulation — forces every change to go through logic you control. A later module covers this principle in depth.

A real business example: Legal (Case File Access)

public class CaseFile {
    private String confidentialNotes;

    public String caseNumber { get; set; }

    public String getSummaryForClient() {
        return 'Case ' + caseNumber + ': in progress.';
    }
}
  1. public class CaseFile { — the class.
  2. private String confidentialNotes; — stays private — internal only, never exposed outside this class.
  3. (blank line)
  4. public String caseNumber { get; set; } — a public auto-property — safe to expose.
  5. (blank line)
  6. public String getSummaryForClient() { — a public method offering a controlled, client-safe view.
  7. return 'Case ' + caseNumber + ': in progress.'; — builds a summary using only the safe, public field — never confidentialNotes.
  8. } — closes the method.
  9. } — closes the class.

This mirrors a real access-control need: some data (like privileged legal notes) should never be exposed outside the class that manages it.

A sensible default

When unsure, start with private and only widen access (to public or protected) once something outside the class genuinely needs it. It's far easier to loosen access later than to discover — after other code already depends on it — that a field should never have been exposed.

Bringing it together: rebuilding the Calculator project as a class

Back in Module 8's "Project: Calculator," the exact same calculation logic was written as plain, standalone statements — no method, no class, because neither had been taught yet. Now that both have been, here's that same logic, properly wrapped:

public class Calculator {
    public String calculateAndFormat(Decimal a, Decimal b, String operatorSymbol) {
        Decimal result;

        switch on operatorSymbol {
            when '+' {
                result = a + b;
            }
            when '-' {
                result = a - b;
            }
            when '*' {
                result = a * b;
            }
            when '/' {
                if (b == 0) {
                    return 'Error: cannot divide by zero.';
                }
                result = a / b;
            }
            when else {
                return 'Error: unrecognized operator "' + operatorSymbol + '".';
            }
        }

        return a + ' ' + operatorSymbol + ' ' + b + ' = ' + result.setScale(2);
    }
}
  1. public class Calculator { — new: the logic now lives inside a class instead of as loose statements.
  2. public String calculateAndFormat(Decimal a, Decimal b, String operatorSymbol) { — new: it's wrapped in a reusable public method taking a, b, and operatorSymbol as parameters. 3-26. The switch on operatorSymbol block and its when branches — identical logic to Module 8's "Project: Calculator", already explained line by line there; nothing about the calculation itself changed, only where it lives.
  3. } — closes the method.
  4. } — closes the class.
Calculator calc = new Calculator();
System.debug(calc.calculateAndFormat(12, 5, '+')); // "12 + 5 = 17.00"
System.debug(calc.calculateAndFormat(10, 0, '/')); // "Error: cannot divide by zero."
  1. Calculator calc = new Calculator(); — new: a Calculator object is created once...
  2. System.debug(calc.calculateAndFormat(12, 5, '+')); — ...then calculateAndFormat is called on it, callable as many times as needed instead of retyping the whole block for every calculation.
  3. System.debug(calc.calculateAndFormat(10, 0, '/')); — the same object handles a second, different calculation. This exact class is what the Apex Testing module writes real automated tests against.

Exercise

Rewrite the Book class so pageCount is private, with a public method getPageCount() that returns it.

Show hint

private Integer pageCount; public Integer getPageCount() { return pageCount; }

Salesforce: ○ Not connected Connect
APEX

Execution Results

Run your code to see the result.

Access Modifiers Quiz

1. Which access modifier is visible only inside the same class?

2. A sensible default is to start with private and widen access only when something outside the class genuinely needs it.

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

Access modifiers control who is allowed to see or use a field, method, or class — choosing the most restrictive one that still works is a core habit of writing maintainable code.