08 Objects and Encapsulation

Learn how Java classes define objects, how constructors and instance methods manage object state, and how references, aliasing, and encapsulation support safe object-oriented programs.

The -Oriented Model

-oriented Java programs model entities as objects. Each combines three ideas:

  • State: the data stored in its fields.

  • Behavior: the operations provided by its methods.

  • Identity: the fact that it is a distinct instance.

A defines the structure and behavior that objects of that type can have. An is one instance created from the . For example, BankAccount can describe what an account stores and does, while each individual account has its own owner and balance.

A reference variable identifies an ; it does not contain the 's fields directly. A reference may also contain , which means that it identifies no .

Takeaway: A is a blueprint, an is an instance, and an combines state, behavior, and identity.

State, Behavior, and this

A is a variable declared inside a , and fields represent an 's state. A BankAccount might declare private String owner and private double balance. Each normally has its own copy of each instance . Two BankAccount objects can therefore have different owners and balances; changing one 's balance does not automatically change the other's.

An describes behavior for one particular . For example, deposit changes the receiving account's balance, while getBalance reports it. Calls such as account.deposit(50.0) and account.getBalance() use the reference before the dot to identify the receiving .

Inside an or , this identifies the receiving . In the statement this.owner = owner, this.owner means the 's , while owner means the parameter.

Takeaway: Fields store state, and instance methods provide behavior for a specific .

Creating and Initializing Objects

A initializes a newly created . It has the same name as its , has no return type, and runs when new creates an . A can require values needed for valid initial state, such as an owner and a starting balance.

The expression BankAccount account = new BankAccount("Maya", 100.00) illustrates two roles: the new expression creates and initializes a BankAccount , and account stores a reference to it.

A can provide multiple constructors with different parameter lists. This is overloading. For example, a Point might provide Point() and Point(int x, int y). The no-argument can delegate to the other with this(0, 0), allowing both creation paths to establish complete state.

If a declares no , Java can provide a default no-argument . Once the declares any , a no-argument must be declared explicitly if it is still needed.

Takeaway: Constructors establish initial state, and overloaded constructors offer multiple valid creation paths.

Invoking Instance Methods

An is invoked through an reference with the dot operator. For example, BankAccount account = new BankAccount("Maya", 100.00) followed by account.deposit(40.00) changes the account, and double amount = account.getBalance() retrieves its balance.

The reference before the dot determines which 's state the method can access or modify. The same method can therefore produce different results when invoked on different objects. If a starts with a balance of 20.00 and b starts with a balance of 90.00, then a.deposit(10.00) and b.deposit(10.00) affect separate objects.

A method may return a value, such as getBalance, or return void, such as deposit. It may also accept arguments and use conditional logic to reject invalid operations.

Takeaway: The dot operator connects a method call to the particular whose behavior is being requested.

References and

occurs when multiple reference variables identify the same . Assigning one reference to another copies the reference, not the . For example, after BankAccount original = new BankAccount("Maya", 100.00) and BankAccount alias = original, both variables refer to the same account. If alias.deposit(25.00) is called, original.getBalance() observes the updated balance, 125.0.

This differs from assigning primitive values. If int first = 10, int second = first, and second = 20, then first remains 10 while second is 20, because the primitive value was copied.

To create a distinct , use another call rather than assigning an existing reference. For example, BankAccount a = new BankAccount("A", 10.00) and BankAccount b = new BankAccount("A", 10.00) create two separate objects with independent state.

Takeaway: Reference assignment shares an ; it does not make an independent copy.

Reference Safety

A reference can contain , which means it refers to no . For example, BankAccount account = creates a reference whose value is , not a BankAccount .

Calling an through , as in account.getBalance(), causes a NullPointerException because there is no receiving . Code should ensure that a reference identifies a valid before invoking its instance methods.

Declaring a reference also does not create an . BankAccount account only declares a reference; a call such as BankAccount account = new BankAccount("Maya", 100.00) is needed before the reference can be used to invoke an .

Takeaway: A declared reference and an are different things, and cannot receive a method call.

and Invariants

combines an 's data with the methods that manage it while limiting inappropriate access. Fields are usually declared private so code outside the cannot change them directly. Instead of allowing an assignment such as account.balance = -500.00, the exposes controlled methods.

An withdraw method can check that an amount is positive and does not exceed the balance before changing the . A caller can then use the method's result to determine whether the operation succeeded, rather than directly modifying the balance.

An is a condition that should remain true for every valid . For a bank account, a useful is that the balance cannot become negative. Private fields and validating methods help preserve that rule.

A does not need to provide a setter for every . If unrestricted modification could violate an , the should have no public setter or should be changed only through carefully designed operations.

Takeaway: protects invariants by hiding implementation details and exposing a controlled public interface.

Putting the Ideas Together

A complete Temperature illustrates the pattern. It can contain a private double celsius , a that assigns the initial value with this.celsius = celsius, a getCelsius method, a toFahrenheit method, and an increase method that accepts only nonnegative changes.

For a temperature of 20.0, calling temperature.increase(5.0) changes the stored Celsius value to 25.0. The Fahrenheit conversion follows

F=C×95+32F = C \times \frac{9}{5} + 32

so a Celsius value of 25.0 produces 77.0 degrees Fahrenheit.

The private stores state. The establishes the initial value, getCelsius provides controlled access, toFahrenheit computes a result from the state, and increase changes the state only when the argument is nonnegative.

The example connects the main ideas: a defines the structure, a initializes an , a reference identifies the , methods provide behavior, and private state is changed through controlled operations.

Takeaway: A well-designed makes valid operations easy and invalid state changes difficult.

Practical Checklist

Use these checks when reading or writing Java -oriented code:

  1. or ? A defines a type; an is a created instance.

  2. Reference or ? A reference identifies an but does not contain its fields.

  3. Created or merely declared? A declaration alone does not create an ; use new and a call.

  4. Same or separate objects? Assigning one reference to another creates an alias; a second call creates a separate .

  5. Valid state? Keep fields private and use methods that preserve the 's invariants.

  6. Safe receiver? Check that a reference is not before invoking an .

The central design principle is to make each responsible for protecting and managing its own state through a clear public interface.