Understanding Classes, Objects, and Encapsulation in Java

Classes and Objects

Object-oriented programming organizes code around data structures called objects, while procedural programming focuses on step-by-step instructions. Rather than implementing every operation manually, OOP allows you to direct objects to perform specific tasks.

Relasionship Between Classes and Objects

Everything that exists in reality can be represented as an object.

Class

  • A class serves as a blueprint that abstracts shared attributes and behaviors from a group of similar entities
  • In Java, a class functions as a custom data type, grouping together objects with identical properties and methods
  • Essentially, a class describes what a certain type of entity looks like in the real world

Class Components

  • Attributes: Characteristics that describe the entity, such as a phone (brand, price, screen size)
  • Behaviors: Actions the entity can perform, such as a phone (make calls, send messages)

Class vs Object Relationship

  • A class is an abstract template describing a category of things
  • An object is a concrete instance that occupies memory and can be manipulated

Defining a Class

Classes consist of attributes (represented by member variables) and behaviors (represented by member methods).

Definition Steps:

  1. Declare the class
  2. Define member variables within the class body
  3. Implement member methods within the class body
public class Person {
    // Attributes: name and age
    // Member variables are declared at the class level, outside methods
    String name;
    int age;

    // Behavior: working
    // Member methods lack the static keyword
    public void work() {
        System.out.println("Working");
    }
}

Creating and Using Objects

Object Creation Syntax:

ClassName referenceName = new ClassName();

Accessing Members:

  • Read or modify variables: referenceName.memberVariable
  • Invoke methods: referenceName.memberMethod();
public class PersonDemo {
    public static void main(String[] args) {
        // Creating an object instance
        Person p = new Person();
        
        // Accessing uninitialized member variables shows default values
        System.out.println(p.name);  // null
        System.out.println(p.age);   // 0

        // Assigning values to member variables
        p.name = "Alice";
        p.age = 25;

        System.out.println(p.name);  // Alice
        System.out.println(p.age);   // 25

        // Calling member methods
        p.work();
        
        // Printing the object reference displays class name and memory address
        System.out.println(p);
    }
}

Practical Example: Calculator Class

Requirement: Define a calculator class, then create and use objects in a test class.

Analysis:

  • Member variables: brand, price
  • Member methods: add, subtract
public class Calculator {
    String brand;
    int price;

    public int add(int a, int b) {
        return a + b;
    }

    public int subtract(int a, int b) {
        return a - b;
    }
}
public class CalculatorTest {
    public static void main(String[] args) {
        // Instantiate the object
        Calculator calc = new Calculator();
        
        // Initialize member variables
        calc.brand = "TechPro";
        calc.price = 199;
        
        // Display values
        System.out.println(calc.brand + " - $" + calc.price);
        
        // Execute operations
        System.out.println("Result: " + calc.add(10, 5));
        System.out.println("Result: " + calc.subtract(10, 5));
    }
}

Object Memory Behavior

Single Object Memory Layout

When creating an object, Java allocates memory in the heap area. The reference variable exists in the stack, while the actual object data resides in the heap. Member variables receive default initial values until explicitly assigned.

Multiple Objects in Memory

Each object instance maintains its own separate copy of member variables in the heap. However, member methods are shared across all instances of the same class—only one copy exists in memory, regardless of how many objects are created.

Shared References

When multiple reference variables point to the same object, they hold identical memory addresses. Modifying data through any reference affects the single underlying object, and subsequent reads from any reference will reflect those changes.

Member Variables vs Local Variables

Aspect Member Variable Local Variable
Declaration Location Inside class, outside methods Inside methods or as method parameters
Memory Area Heap memory Stack memory
Lifecycle Exists as long as the object exists Exists only during method execution
Initial Value Assigned default values automatically No default value; must be initialized before use

Encapsulation

The private Access Modifier

Overview: Private is an access modifier restricting visibility to the declaring class only.

Characteristics: Members marked private cannot be accessed directly from other classes. To work with private data, provide public accessor and mutator methods.

Implementation Pattern:

class User {
    // Private data cannot be accessed from outside
    private String username;
    private int score;

    // Public setter with validation
    public void setScore(int value) {
        if (value < 0) {
            System.out.println("Invalid score");
        } else {
            score = value;
        }
    }

    // Public getter
    public int getScore() {
        return score;
    }

    public void display() {
        System.out.println(username + ": " + score);
    }
}

public class UserDemo {
    public static void main(String[] args) {
        User u = new User();
        u.username = "admin";
        u.setScore(100);
        u.display();
    }
}

Practical Application with Private Members

Requirement: Create a User class with private fields, provide getters/setters, include a display method, and verify functionality in a test class outputting "admin: 100".

class User {
    private String username;
    private int score;

    public void setUsername(String name) {
        username = name;
    }

    public String getUsername() {
        return username;
    }

    public void setScore(int points) {
        score = points;
    }

    public int getScore() {
        return score;
    }

    public void display() {
        System.out.println(username + ": " + score);
    }
}

public class UserDemo {
    public static void main(String[] args) {
        User u = new User();
        u.setUsername("admin");
        u.setScore(100);
        u.display();
        System.out.println(u.getUsername() + " - " + u.getScore());
    }
}

The this Keyword

Purpose: The this reference points to the current object instance and resolves naming conflicts between member variables and parameters.

When a method parameter shares the same name as a member variable, using the variable alone refers to the parameter. The this keyword is required to access the member variable.

public class Product {
    private String productName;
    private double unitPrice;

    public void setProductName(String productName) {
        // this.productName refers to the member variable
        // productName alone refers to the parameter
        this.productName = productName;
    }

    public String getProductName() {
        return productName;
    }

    public void setUnitPrice(double unitPrice) {
        this.unitPrice = unitPrice;
    }

    public double getUnitPrice() {
        return unitPrice;
    }

    public void printDetails() {
        System.out.println(productName + " - $" + unitPrice);
    }
}

this Reference Internals

The this keyword holds the memory address of whichever object invoked the current method. During method execution, this acts as an implicit parameter representing the calling object.

Principles of Encapsulation

Definition: Encapsulation is one of the three pillars of OOP (alongside inheritance and polymorphism). It models how real-world objects hide their internal complexity.

Core Principle: Hide internal state within the class and expose only controlled interfaces. Mark member variables as private and provide public getter/setter methods.

Benefits:

  • Protects data integrity by enforcing validation in setters
  • Promotes code reuse through well-defined method interfaces
  • Allows internal implemantation changes without affecting external code

Constructor Methods

Constructor Syntax and Invocation

Syntax Requirements:

  • Constructor name must exactly match the class name (including capitalization)
  • No return type declaration—not even void
  • Cannot use return statements to return values

Execution Timing: Constructors run automatically during object instantiation, executing once per object created. You cannot invoke a constructor manually.

class Product {
    private String productName;
    private int stock;

    // Default constructor
    public Product() {
        System.out.println("Default constructor executing");
    }

    public void show() {
        System.out.println(productName + ", " + stock);
    }
}

public class ProductDemo {
    public static void main(String[] args) {
        Product p = new Product();
        p.show();
    }
}

Constructor Purpose

Constructors initialize object state during creation.

public class Vehicle {
    private String model;
    private int mileage;

    // Java provides a default no-arg constructor if none is defined
    public Vehicle() {}

    // Parameterized constructor
    public Vehicle(String model, int mileage) {
        this.model = model;
        this.mileage = mileage;
        System.out.println("Parameterized constructor called");
    }

    public void display() {
        System.out.println(model + " - " + mileage + " miles");
    }
}
public class VehicleTest {
    public static void main(String[] args) {
        Vehicle v1 = new Vehicle("Sedan", 15000);
        v1.display();

        Vehicle v2 = new Vehicle();
    }
}

Constructor Rules and Recommendations

Automatic Generation:

  • If no constructor is defined, the compiler generates a default no-arg constructor
  • If any constructor is manually defined, the compiler does not generate a default constructor

Best Practice: Always explicitly define both default and parameterized constructors to ensure flexibility and prevent compilation errors when instantiating objects with different argument sets.

Complete POJO Class Implementation

public class Employee {
    private String employeeName;
    private int employeeId;

    // No-arg constructor
    public Employee() {
    }

    // Parameterized constructor
    public Employee(String employeeName, int employeeId) {
        this.employeeName = employeeName;
        this.employeeId = employeeId;
    }

    // Getters and setters
    public String getEmployeeName() {
        return employeeName;
    }

    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }

    public int getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }

    public void output() {
        System.out.println(employeeName + " - ID#" + employeeId);
    }
}
public class EmployeeTest {
    public static void main(String[] args) {
        // Create object using no-arg constructor, then set fields via setters
        Employee emp1 = new Employee();
        emp1.setEmployeeName("Bob");
        emp1.setEmployeeId(101);
        emp1.output();

        // Create object using parameterized constructor
        Employee emp2 = new Employee("Carol", 102);
        emp2.output();
    }
}

Tags: java Object-Oriented Programming Classes Objects encapsulation

Posted on Wed, 12 Aug 2026 16:11:19 +0000 by imartin