Understanding Static, Access Modifiers, and Encapsulation in Java

1. Static in Java

The static keyword in Java is used to indicate that a member (variable, method, or block) belongs to the class rather than an instance of the class. This means static members are shared across all objects of the class and can be accessed without creating an instance.

Key Points about Static

  • Static Variables: Shared across all instances of the class. Useful for constants or counters.

  • Static Methods: Can be called without creating an object. Often used for utility methods.

  • Static Blocks: Used to initialize static variables when the class is loaded.

  • Limitations: Static methods cannot access non-static members directly, as they don’t require an instance.

Example: Static Variable and Method

public class Counter {
    // Static variable shared across all instances
    static int count = 0;

    // Constructor increments the static count
    Counter() {
        count++;
    }

    // Static method to get the count
    public static int getCount() {
        return count;
    }

    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        System.out.println("Total instances: " + Counter.getCount()); // Output: Total instances: 2
    }
}

Use Cases

  • Utility Classes: Methods like Math.sqrt() are static.

  • Constants: public static final variables for immutable constants.

  • Singletons: Managing a single instance of a class.


2. Access Modifiers in Java

Access modifiers control the visibility and accessibility of class members (fields, methods, constructors, etc.) in Java. They ensure proper encapsulation and security by restricting access to sensitive parts of the code.

Types of Access Modifiers

  1. public: The member is accessible from everywhere.

  2. protected: The member is accessible within the same package and also in subclasses (even in different packages).

  3. default (package-private): If no modifier is specified, the member is accessible only within the same package.

  4. private: The member is accessible only within the same class.

Access Modifier Table

ModifierClassPackageSubclassGlobal
publicYesYesYesYes
protectedYesYesYesNo
defaultYesYesNoNo
privateYesNoNoNo

Example: Access Modifiers

package com.example;

public class AccessDemo {
    public int publicVar = 1;
    protected int protectedVar = 2;
    int defaultVar = 3; // Package-private
    private int privateVar = 4;

    public void display() {
        System.out.println("Public: " + publicVar);
        System.out.println("Protected: " + protectedVar);
        System.out.println("Default: " + defaultVar);
        System.out.println("Private: " + privateVar);
    }
}

class TestAccess {
    public static void main(String[] args) {
        AccessDemo demo = new AccessDemo();
        demo.display();
        // Accessing variables
        System.out.println(demo.publicVar); // Accessible
        System.out.println(demo.protectedVar); // Accessible in same package
        System.out.println(demo.defaultVar); // Accessible in same package
        // System.out.println(demo.privateVar); // Error: privateVar is not accessible
    }
}

Best Practices

  • Use private for sensitive data to enforce encapsulation.

  • Use public for methods and fields that form the class’s public API.

  • Use protected for members that subclasses need to access.

  • Use default when access is limited to the package.


3. Encapsulation in Java

Encapsulation is one of the four pillars of Object-Oriented Programming (OOP). It involves bundling data (fields) and methods that operate on that data within a single unit (class) and restricting direct access to the data. This is achieved using private fields and public getter/setter methods.

Key Benefits of Encapsulation

  • Data Hiding: Protects the internal state of an object from unintended modifications.

  • Flexibility: Allows changes to the internal implementation without affecting external code.

  • Maintainability: Simplifies debugging by controlling access to data.

Steps to Achieve Encapsulation

  1. Declare fields as private.

  2. Provide public getter and setter methods to access and modify the fields.

  3. Add validation logic in setters, if needed.

Example: Encapsulation

public class Employee {
    // Private fields
    private String name;
    private int age;
    private double salary;

    // Public constructor
    public Employee(String name, int age, double salary) {
        this.name = name;
        setAge(age); // Use setter for validation
        this.salary = salary;
    }

    // Public getter for name
    public String getName() {
        return name;
    }

    // Public setter for name
    public void setName(String name) {
        if (name != null && !name.isEmpty()) {
            this.name = name;
        } else {
            throw new IllegalArgumentException("Name cannot be null or empty");
        }
    }

    // Public getter for age
    public int getAge() {
        return age;
    }

    // Public setter for age with validation
    public void setAge(int age) {
        if (age >= 18 && age <= 65) {
            this.age = age;
        } else {
            throw new IllegalArgumentException("Age must be between 18 and 65");
        }
    }

    // Public getter for salary
    public double getSalary() {
        return salary;
    }

    // Public setter for salary
    public void setSalary(double salary) {
        if (salary >= 0) {
            this.salary = salary;
        } else {
            throw new IllegalArgumentException("Salary cannot be negative");
        }
    }

    public static void main(String[] args) {
        Employee emp = new Employee("Alice", 30, 50000);
        System.out.println("Name: " + emp.getName());
        System.out.println("Age: " + emp.getAge());
        System.out.println("Salary: " + emp.getSalary());

        // Update using setters
        emp.setSalary(60000);
        System.out.println("Updated Salary: " + emp.getSalary());

        // This will throw an exception
        // emp.setAge(15); // IllegalArgumentException
    }
}

Output

Name: Alice
Age: 30
Salary: 50000.0
Updated Salary: 60000.0

Why Use Encapsulation?

  • Control: Setters allow validation before updating fields.

  • Security: Private fields prevent unauthorized access.

  • Modularity: Changes to the class’s internal logic don’t break external code.


Combining Static, Access Modifiers, and Encapsulation

Let’s create a practical example that combines all three concepts: a BankAccount class with static counters, access modifiers, and encapsulation.

Example: BankAccount Class

public class BankAccount {
    // Static variable to track total accounts
    private static int totalAccounts = 0;

    // Private instance variables (encapsulation)
    private String accountNumber;
    private double balance;
    private String owner;

    // Public constructor
    public BankAccount(String accountNumber, String owner, double initialBalance) {
        this.accountNumber = accountNumber;
        this.owner = owner;
        setBalance(initialBalance); // Use setter for validation
        totalAccounts++; // Increment static counter
    }

    // Public getter for accountNumber
    public String getAccountNumber() {
        return accountNumber;
    }

    // Public getter for balance
    public double getBalance() {
        return balance;
    }

    // Public setter for balance with validation
    public void setBalance(double balance) {
        if (balance >= 0) {
            this.balance = balance;
        } else {
            throw new IllegalArgumentException("Balance cannot be negative");
        }
    }

    // Public getter for owner
    public String getOwner() {
        return owner;
    }

    // Public setter for owner
    public void setOwner(String owner) {
        if (owner != null && !owner.isEmpty()) {
            this.owner = owner;
        } else {
            throw new IllegalArgumentException("Owner cannot be null or empty");
        }
    }

    // Public method to deposit money
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        } else {
            throw new IllegalArgumentException("Deposit amount must be positive");
        }
    }

    // Public method to withdraw money
    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
        } else {
            throw new IllegalArgumentException("Invalid withdrawal amount");
        }
    }

    // Static method to get total accounts
    public static int getTotalAccounts() {
        return totalAccounts;
    }

    public static void main(String[] args) {
        BankAccount acc1 = new BankAccount("12345", "Bob", 1000);
        BankAccount acc2 = new BankAccount("67890", "Alice", 2000);

        acc1.deposit(500);
        acc2.withdraw(300);

        System.out.println("Account 1 Balance: " + acc1.getBalance()); // Output: 1500.0
        System.out.println("Account 2 Balance: " + acc2.getBalance()); // Output: 1700.0
        System.out.println("Total Accounts: " + BankAccount.getTotalAccounts()); // Output: 2
    }
}

Explanation

  • Static: totalAccounts tracks the number of accounts created and is shared across all instances.

  • Access Modifiers: Private fields (accountNumber, balance, owner) ensure data hiding, while public getters/setters provide controlled access.

  • Encapsulation: Private fields with public methods (deposit, withdraw, getters, setters) enforce validation and protect the internal state.


Conclusion

  • Static: Use for class-level members that don’t depend on instance state, like counters or utility methods.

  • Access Modifiers: Choose the appropriate modifier (public, protected, default, private) to control access and ensure security.

  • Encapsulation: Protect data by making fields private and exposing them through public getters/setters with validation.

By mastering these concepts, you can write Java code that is modular, secure, and easy to maintain. The BankAccount example demonstrates how these concepts work together to create robust applications.

0
Subscribe to my newsletter

Read articles from Prathamesh Karatkar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Prathamesh Karatkar
Prathamesh Karatkar