SwadhyAI Notes

All Java notes

Java

Java OOP

›Short versionOpen if you only have a minute.
  • A class describes a kind of thing. An object is one live instance, with its own field values.
  • An invariant is a rule that stays true between public method calls, such as a balance that only changes through deposit and withdraw.
  • Encapsulation keeps fields private so the class methods are the only way to change that state.
  • Inheritance fits when every instance of the subclass is an instance of the superclass for its whole life.
  • Composition fits when one object contains another as a part. A statement has an account. A statement is not an account.
  • Polymorphism lets a caller use a superclass or interface type while the runtime object supplies the overriding method body.

A transfer screen shows a balance of 80. Somewhere else in the program, a line writes balance = -40 because a refund was applied twice. The number on the screen is now a lie, and every later line that trusted the account has to discover the lie for itself.

Object-oriented programming in Java is the habit of putting that number, and the rule about it, in the same . An is one account. Its methods are the only operations that may change the balance, and they leave an standing when they return.

Own the account rule

1

Describe

A class names fields and the methods that may touch them.

2

Hide

Private fields force callers through your methods.

3

Enforce

Each public method restores the invariant before it returns.

4

Relate

Extend when is-a holds; compose when has-a is honest.

Encapsulation first

  • Private balance means no distant line can assign -40 directly.
  • deposit and withdraw are the public surface; they reject illegal amounts at the call.

Inheritance vs composition

  • SavingsAccount extends Account when every savings account is an account for its whole life.
  • Statement holds an Account field when the statement has an account but is not one.

Pick the relationship that matches the sentence

SentenceToolWhy
Every savings account is an accountextends AccountSubclass can stand in for Account
Every statement has an accountprivate Account fieldOuter type is not an account
Caller knows Account, object is SavingsAccountPolymorphismRuntime runs the override
balance only changes in deposit/withdrawInvariant + encapsulationCallers rely on the rule
withdraw keeps the invariant at the boundary
Diagram text: the exact Mermaid definition below preserves the nodes, labels, and connections. Enable JavaScript to view the rendered diagram.
Read the diagram definition
sequenceDiagram
  participant Caller
  participant Account
  participant Balance as private balance
  Caller->>Account: withdraw(20)
  Account->>Account: amount legal?
  alt amount too large
    Account-->>Caller: IllegalArgumentException
  else ok
    Account->>Balance: subtract amount
    Account-->>Caller: return
  end
  Note over Account,Balance: invariant holds until next call

The account owns the rule

The class below is small on purpose. balance is private. Callers can deposit, withdraw, or read the current amount. They cannot assign the field. That is : the data and the operations travel together, and the operations are the public surface.

Account.java
public class Account {
    private int balance;

    public Account(int opening) {
        if (opening < 0) {
            throw new IllegalArgumentException("opening balance");
        }
        balance = opening;
    }

    public void deposit(int amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("amount");
        }
        balance += amount;
    }

    public void withdraw(int amount) {
        if (amount <= 0 || amount > balance) {
            throw new IllegalArgumentException("amount");
        }
        balance -= amount;
    }

    public int balance() {
        return balance;
    }
}

After new Account(80), the invariant holds: the balance is non-negative, and it changes only inside these methods. withdraw refuses an amount larger than the balance, so the caller hears about the mistake at the call, not three screens later.

Terminal
$ javac Account.java
$ java Account

A kind of account, or a thing that has one

is the right tool when the new type is a more specific account. A savings account is an account. It can live in an Account variable, and a caller can still withdraw. If savings accounts add interest, that method can be overridden. The rest of the program keeps talking to Account. That substitution is .

java
class SavingsAccount extends Account {
    public SavingsAccount(int opening) {
        super(opening);
    }

    public void addInterest(int rateBasisPoints) {
        deposit(balance() * rateBasisPoints / 10_000);
    }
}

A monthly statement is a different relationship. The statement lists an account, a period, and a closing number. It is not an account. Handing a statement to a method that expects an Account would be a type lie. Give the statement a field. That is .

java
class Statement {
    private final Account account;
    private final String period;

    public Statement(Account account, String period) {
        this.account = account;
        this.period = period;
    }

    public int closingBalance() {
        return account.balance();
    }
}

The practical test is a sentence you can say without flinching. "Every savings account is an account" survives. "Every statement is an account" does not. When the sentence fails, the field is the honest model, and the outer class calls the part.

What the caller is allowed to assume

Abstraction, in this setting, means the caller depends on a small promise. "I can ask an account for its balance and I can withdraw a legal amount." The caller does not depend on whether the balance is an int, a long, or a later type that tracks currency. The methods are the promise. The fields are an implementation detail.

Overloading and overriding both reuse a method name, and they are chosen at different times. Overloading is static dispatch: the compiler picks deposit(int) or deposit(String) from the argument types it can see. Overriding is dynamic dispatch: account.withdraw(20) runs the method body of the object account points at, which may be a subclass.

A Car class needs an engine. Should Car extend Engine?

Car contains an engine. A car is not an engine, so a method written for Engine should not receive a Car. Declare private final Engine engine and construct the engine inside the car, or accept one that the car owns. That is composition. Extend a class only when the subclass can stand in for the superclass without breaking its rules.

Simple rule of thumb

Put state and the rule about that state in one class. Callers talk to methods, not fields. Extend only when the subtype can honor every superclass promise for the object's entire lifetime; otherwise hold the part in a field.

Good default: private fields, a constructor that rejects bad input, public methods that restore the invariant, and composition until you can say every instance of the subclass is an instance of the superclass without flinching.