انتقل إلى المحتوى
أمين أبوزيد
منشورBEG-021-L01

لماذا نُخفي البيانات

  • الدرس 1 من 6
  • 11 دقيقة قراءة
  • مبتدئ
في هذا الدرس 11 موضوعات

هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.

Lesson Overview

In the last module you wrote your first classes: fields for the data, constructors to set it up, and methods to act on it. Every field you wrote so far could be read and changed by any code that held the object. That is convenient, and it is also the root of a whole family of bugs.

This lesson introduces encapsulation: keeping an object's data private and letting only the object's own methods change it. We will use a bank as the picture. A bank has a vault (the data) and a teller window (the methods). Customers ask the teller; they never walk into the vault.

Why this matters: backend systems are built from many classes written by many people over many years. A class that protects its own data can be used safely by people who never read its code, and can be rewritten inside without breaking them.

This lesson builds on "Writing your first class" and "Creating and using objects" from the previous module. It only uses the Account idea you already know: an owner and a balance.

Learning Objectives

By the end of this lesson you will be able to:

  • Define encapsulation in one sentence.
  • Show how a public field lets any code create an invalid object.
  • Explain why repeating a rule in every caller fails.
  • Make a field private and read the compiler error that outside access now produces.
  • Explain how hiding data lets you change a class's inside without changing the code that uses it.
  • Name the public methods of a class as its "window" to the rest of the program.

Written Explanation

The problem: a field anyone can write

Here is an Account with plain, package-visible fields, the way a first class often looks:

java
class Account {
    String owner;
    double balance;
}

Any code that holds an Account can do this:

java
Account acc = new Account();
acc.owner = "Sara";
acc.balance = 100;
acc.balance = -500;

The last line compiles, runs, and leaves a bank account with a negative balance. Nothing in the language objects, because from Java's point of view -500 is a perfectly good double. The rule "an account never has a negative balance" exists only in your head.

Rules in every caller do not survive

You might answer: "I will check before I assign." So every piece of code that touches balance must remember to write the check. In a real program that is dozens of places: the deposit screen, the transfer code, the monthly interest job, the import tool a colleague writes next year. One forgotten check, in one place, and the rule is broken.

The problem is structural. The data lives in one class, but the rule is scattered over many. Whenever a rule and its data are far apart, the rule loses.

Encapsulation: data and its rules together

Encapsulation means two things that work together:

  1. The data is hidden: fields are private, so only code inside the class can read or write them.
  2. The only way in is through the class's methods, which apply the rules.

In bank terms, the balance is in the vault. The customer talks to the teller. The teller will happily process a deposit or a withdrawal, but will refuse to withdraw more than the account holds. The customer cannot bypass the teller, so the rule is enforced in exactly one place.

The Java keyword that builds the vault wall is private. The Java tutorial on controlling access says a private member can be used only inside the class that declares it, and recommends using the most restrictive level that makes sense, with public fields avoided except for constants.

What the compiler does for you

Make the fields private and the dangerous line stops compiling:

java
class Account {
    private String owner;
    private double balance;

    Account(String owner, double balance) {
        this.owner = owner;
        this.balance = balance;
    }
}

public class Main {
    public static void main(String[] args) {
        Account acc = new Account("Sara", 100);
        acc.balance = -500;
    }
}
text
Main.java:14: error: balance has private access in Account
        acc.balance = -500;
           ^
1 error

This is the best kind of error. It appears at compile time, before the program ever runs, and it appears for every caller, including the one written next year by somebody who never heard of your rule. The class has not yet enforced the rule (the constructor above still accepts -5; the next lessons fix that), but it has taken away the easy way around it.

The window: public methods

If everything is hidden, how does anyone use the object? Through methods you choose to make visible. The set of those methods is the class's public interface (not to be confused with the Java keyword interface, which you will meet in a later module). Think of it as the teller window: a small, deliberate opening in the wall.

A good public interface is small. Every method you expose is a promise you must keep. Every field you expose is a bigger promise: callers can read it, write it, and depend on its exact type.

Hiding lets you change your mind

There is a second benefit, easy to miss. When callers cannot see the fields, you can change how the data is stored, and the callers do not notice.

Suppose the account stores its balance as a double. Later you learn that decimals like 0.1 cannot be stored exactly in a double, and you decide to store whole cents in a long instead. If balance were public, every line of code anywhere that read acc.balance would break. With the field private, you change the class and nothing else:

java
class Account {
    private String owner;
    private long cents;   // the representation changed

    Account(String owner, double balance) {
        this.owner = owner;
        this.cents = Math.round(balance * 100);
    }

    void withdraw(double amount) {
        long c = Math.round(amount * 100);
        if (c <= 0 || c > cents) {
            System.out.println("Withdrawal refused");
            return;
        }
        cents -= c;
    }

    void show() {
        System.out.println(owner + " has " + cents / 100.0);
    }
}

The calling code, new Account("Sara", 100), withdraw(...), show(), is identical before and after. The window stayed the same; the vault was rebuilt behind it.

Hiding is not secrecy

private does not hide anything from people: anyone can read your source. It is a rule the compiler enforces about which code may touch which data. The goal is not secrecy but control: the class decides how its data may change.

Examples

Example 1: a public field breaks a rule (simple)

java
class Account {
    String owner;
    double balance;
}

public class Main {
    public static void main(String[] args) {
        Account acc = new Account();
        acc.owner = "Sara";
        acc.balance = 100;
        acc.balance = -500;
        System.out.println(acc.owner + " has " + acc.balance);
    }
}

Output:

text
Sara has -500.0

The program is "correct" as far as Java is concerned, and the bank is now wrong.

Example 2: the class guards its own data (practical)

Same account, fields private, with a method that applies the rule:

java
class Account {
    private String owner;
    private double balance;

    Account(String owner, double balance) {
        this.owner = owner;
        this.balance = balance;
    }

    void withdraw(double amount) {
        if (amount <= 0 || amount > balance) {
            System.out.println("Withdrawal refused");
            return;
        }
        balance -= amount;
    }

    void show() {
        System.out.println(owner + " has " + balance);
    }
}

public class Main {
    public static void main(String[] args) {
        Account acc = new Account("Sara", 100);
        acc.withdraw(500);
        acc.withdraw(30);
        acc.show();
    }
}

Output:

text
Withdrawal refused
Sara has 70.0

Printing "Withdrawal refused" is a stop-gap: the next lessons replace it with a proper exception. The important point is where the rule lives: inside Account, once, next to the data.

Example 3: changing the inside without touching callers (realistic)

This is the long cents version from the explanation above, combined with the same main as Example 2. Output:

text
Withdrawal refused
Sara has 70.0

The output is identical, yet the field type, the arithmetic, and the storage all changed. In a real codebase, "identical for the callers" might mean hundreds of files you did not have to open. That is the payoff of a narrow window.

Backend Insight

A backend service is a pile of classes that call each other: a controller calls a service, the service calls a repository, and so on. Teams change those classes constantly. If every class exposes its fields, any change to a field ripples into every class that touched it, and bugs appear far from their cause.

Encapsulation is also the reason framework authors can improve their libraries without breaking your programs: you depend on their public methods, not their internals. Later in this course you will see the same idea at a larger scale, where a class is used only through an interface, and again when an API (a set of operations a service offers to other programs) hides the database behind it. It begins with private fields.

Common Mistakes

Mistake 1: Public fields "for convenience"

  • Incorrect: public double balance; because it saves writing methods.
  • Correct: private double balance; plus the methods callers genuinely need.

Convenience now is paid for later, when you cannot change the field without breaking everyone.

Mistake 2: Thinking private alone protects the rules

Making a field private only closes the front door. If the class then offers a method that accepts any value without checking, for example one that assigns any number it is given, the rule is still unprotected. Hiding the data is step one; checking in the methods is step two (lessons 3 and 4).

Mistake 3: Believing private means "secret"

Private data can still be seen by anyone reading the source or running a debugger. It is protection against accidental or careless misuse by other code, not against curious people.

Mistake 4: Hiding the data but exposing everything else

A class with private fields and a public method for every possible operation, including ones nobody needs, has a window as wide as the wall. Expose only what callers need.

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. In one sentence, define encapsulation.

أظهر الحل A1

A1. Encapsulation means keeping an object's data private and letting only the object's own methods change it, so the object can enforce its rules.

A2. In the bank picture, what are the vault and the teller window in Java terms?

أظهر الحل A2

A2. The vault is the private fields. The teller window is the public methods (the public interface) through which callers ask the object to do things.

A3. A class has int age; with no modifier. Name one line of outside code that would make the object invalid in real life.

أظهر الحل A3

A3. For example person.age = -40; or person.age = 900;. Java accepts any int, so nothing stops it.

Level B: Application

B1. A Thermostat class has a field private int target;. Outside code contains t.target = 99;. Does it compile? What does the compiler report?

أظهر الحل B1

B1. It does not compile. The compiler reports an error of the form target has private access in Thermostat, as in the Account example.

B2. Write a Thermostat with a private target, a constructor, a method raise(int degrees) that ignores non-positive degrees and refuses to go above 30, and a show() method. Use it with a starting target of 21, then raise(5), then raise(10).

أظهر الحل B2

B2.

java
class Thermostat {
    private int target;

    Thermostat(int target) {
        this.target = target;
    }

    void raise(int degrees) {
        if (degrees > 0 && target + degrees <= 30) {
            target += degrees;
        }
    }

    void show() {
        System.out.println("Target: " + target);
    }
}

public class Main {
    public static void main(String[] args) {
        Thermostat t = new Thermostat(21);
        t.raise(5);
        t.raise(10);
        t.show();
    }
}

Output:

text
Target: 26

raise(5) takes 21 to 26. raise(10) would give 36, above 30, so it is ignored.

B3. Three different classes in a program each contain if (acc.balance < 0) checks before using an account. Name two problems with this design.

أظهر الحل B3

B3. (1) The rule is repeated, so any place that forgets it breaks the program. (2) If the rule changes, every copy must be found and updated. (A third: the checks only detect a problem; they do not stop other code from assigning a bad value.)

Level C: Challenge

C1. The Thermostat currently stores whole degrees. You must switch it to store tenths of a degree in a long. What must change in the code that uses Thermostat if the field is private? What would have to change if it were public?

أظهر الحل C1

C1. With a private field, only Thermostat changes (constructor, raise, show); the calling code stays the same as long as the public methods keep their signatures. With a public field, every line anywhere that read or wrote target would have to be found and converted.

C2. Someone says: "Private fields make the program more secure against hackers." Correct this claim in two or three sentences.

أظهر الحل C2

C2. private is a rule the compiler checks about which classes may access a field; it does not stop anyone from reading the source or attacking a running system. Its purpose is to prevent accidental misuse by other code and to let the class control its own data. Security needs other tools.

Mini Challenge

Take the following class and list, in a short note, every way outside code could put a Student into a bad state (for example a negative number of credits, or a null name). Then rewrite the class with private fields and a constructor, and add one method that changes credits safely.

java
class Student {
    String name;
    int credits;
}

Check your work: no field is directly reachable from outside, and your method refuses at least one bad input. Do not worry yet about how to refuse (printing a message is fine for now).

Summary & Next Lesson

Key takeaways

  • Encapsulation keeps data private and lets only the object's methods change it.
  • A public field lets any code create an invalid object, and a rule repeated in every caller will eventually be forgotten.
  • private turns outside access into a compile-time error, for every caller.
  • The public methods are the class's window; keep it small and deliberate.
  • Hidden data can be re-stored in a better form later without touching callers.

Next lesson

private is one of four visibility levels. The next lesson, "Access modifiers", compares private, package-private, protected, and public, and gives a rule for choosing among them.

Self-check

  1. What is the main benefit of making fields private?
  2. Why is "check before you assign" in every caller a weak design?
  3. When does the error for acc.balance = -500; appear if balance is private: compile time or run time?
  4. Does making a field private enforce the rule "no negative balance" by itself?
  5. Give one benefit of private fields other than protecting rules.
أظهر الإجابات
  1. Only the class can change its own data, so the class can protect its rules.
  2. The rule is spread over many places, and one forgotten check breaks it; changing the rule means finding every copy.
  3. Compile time.
  4. No. It stops outside code from assigning directly, but the class's own methods and constructor must still check values.
  5. You can change how the data is stored without changing the code that uses the class.

References

  • Oracle, Controlling Access to Members of a Class. It supports that private members are accessible only within their own class, and the advice to use the most restrictive level and to avoid public fields except for constants.
  • Oracle, Java Language Specification SE 21, chapter 6. Its introduction supports that a declaration without an access modifier has package access in most cases, and lists public, protected, and private as the other possibilities.
  • The bank-vault analogy, the long cents redesign example, and the claim that double cannot store 0.1 exactly are this course's own teaching points; the double claim was not verified from a fetched source in this lesson.

التطبيق

طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.

المختبر والتمارين الاختبار

تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.

اكتب للبحث في المستويات والمراحل والوحدات والدروس.