Popular Searches
Popular Course Categories
Popular Courses

Applying Access Modifiers to Classes, Fields, and Methods

Applying Access Modifiers to Classes, Fields, and Methods

Access Modifiers, Static Members, and Final Keyword

Applying Access Modifiers to Classes, Fields, and Methods in Java

Course: Core Java Training

Topic: Applying Access Modifiers to Classes, Fields, and Methods

Course Link: Core Java Training Course

Demo Link: Register for a Free Course Demo

Website: JustAcademy Official Website


1. Introduction to Access Modifiers in Java

Access modifiers are keywords in Java that control the visibility and accessibility of classes, fields, constructors, and methods. They help developers decide which parts of a program can access specific classes and members.

In object-oriented programming, controlling access is important for protecting data, preventing unintended modifications, and designing maintainable applications. For example, a bank account should not allow external code to change its balance directly. Instead, access can be controlled through methods such as deposit() and withdraw().

Java provides four access levels: public, protected, package-private (no modifier), and private. Each level has different accessibility rules.

Learning Objectives

  • Understand the purpose of access modifiers in Java.
  • Differentiate between public, protected, package-private, and private access.
  • Apply access modifiers to classes, fields, methods, and constructors.
  • Understand how packages and inheritance affect accessibility.
  • Use access modifiers to implement encapsulation and data hiding.
  • Identify common access-related compilation errors.

2. What Are Access Modifiers?

An access modifier specifies where a class or class member can be accessed from. It defines the boundary between accessible and restricted parts of a Java program.

Consider a class named Student that contains a student's name, roll number, and marks. If every field is public, any accessible code could change those values directly. By using private fields and public methods, the class can control how its data is read and updated.

class Student {
    private String name;
    private int marks;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

In this example, the fields are private, while the methods are public. Code outside the class cannot directly access name or marks, but it can use the public methods provided by the class.

Why Are Access Modifiers Important?

  • Data protection: Prevent direct access to sensitive fields.
  • Encapsulation: Keep data and the operations that manage it together.
  • Maintainability: Allow internal implementation details to change without exposing them unnecessarily.
  • Controlled access: Provide only the operations that other classes need.
  • Reusable design: Define clear interfaces between classes and packages.

3. Types of Access Modifiers in Java

Java provides four access levels. The fourth level, package-private, is the default access level when no modifier is written.

Access Level Keyword Same Class Same Package Subclass in Another Package Unrelated Class in Another Package
Private private Yes No direct access No direct access No direct access
Package-private No keyword Yes Yes No No
Protected protected Yes Yes Yes, subject to inheritance access rules No
Public public Yes Yes Yes, subject to other language and module restrictions Yes, subject to other language and module restrictions

Important: A public member is not automatically usable everywhere if its enclosing class is inaccessible, its package is not available, or Java module rules prevent access.


4. Public Access Modifier

The public modifier provides the broadest normal accessibility. A public class or member can be accessed from other packages when the declaring type is accessible and the required dependencies are available.

4.1 Applying Public to a Class

package school;

public class Student {
    public String name = "Aarav";

    public void display() {
        System.out.println("Student Name: " + name);
    }
}

The Student class can be imported and used by code in another package because it is public.

4.2 Applying Public to Fields and Methods

public class Calculator {
    public int result;

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

Other accessible classes can call add() and access result. In production code, fields are often kept private even when methods are public, so the class can control how values change.

4.3 Example of Public Access

class PublicDemo {
    public void showMessage() {
        System.out.println("Public method accessed");
    }
}

public class Main {
    public static void main(String[] args) {
        PublicDemo demo = new PublicDemo();
        demo.showMessage();
    }
}

Output:

Public method accessed

Explanation: The showMessage() method is public, so it can be called from the Main class. Both classes are in the same package in this example.

When Should You Use Public?

  • For methods intended to form part of a class's public API.
  • For classes that must be accessible from other packages.
  • For operations that other components are expected to call.

Avoid making internal fields public without a clear reason, because doing so exposes implementation details and makes validation harder.


5. Private Access Modifier

The private modifier restricts access to the declaring top-level class's body. Other classes cannot directly access private fields or methods, including subclasses.

Private members are commonly used to implement encapsulation and hide internal details.

5.1 Applying Private to Fields

public class Employee {
    private int employeeId;
    private double salary;

    public Employee(int employeeId, double salary) {
        this.employeeId = employeeId;
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }
}

Here, employeeId and salary are private. Code outside the class cannot directly read or assign them, but the public getter allows controlled reading of the salary.

5.2 Applying Private to Methods

public class LoginService {
    public void login(String username, String password) {
        if (isValid(username, password)) {
            System.out.println("Login successful");
        } else {
            System.out.println("Invalid credentials");
        }
    }

    private boolean isValid(String username, String password) {
        return username != null
                && !username.isBlank()
                && password != null
                && !password.isBlank();
    }
}

The isValid() method is private because it is an internal helper operation. External classes should use the public login() operation rather than calling the validation helper directly.

5.3 Example of Private Access

public class BankAccount {
    private double balance;

    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    public double getBalance() {
        return balance;
    }

    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        account.deposit(1500);
        System.out.println(account.getBalance());
    }
}

Output:

1500.0

Explanation: The balance is private, so it cannot be changed directly by ordinary code outside the class. The deposit() method checks that the amount is positive before updating it.

Important Rules for Private

  • A private top-level class is not allowed. A top-level class can be public or package-private.
  • A nested class can be private.
  • Private fields and methods are not directly accessible to subclasses.
  • Private methods are not overridden by subclasses in the normal polymorphic sense.

6. Package-Private Access Modifier

Package-private access applies when no access modifier is specified. It allows access from classes in the same package but prevents direct access from classes in other packages.

Package-private access is useful when several classes within one package need to cooperate, but the functionality should not become part of the public API.

6.1 Example of Package-Private Access

package school;

class Attendance {
    int totalDays = 30;

    void displayAttendance() {
        System.out.println("Total Days: " + totalDays);
    }
}

The class Attendance, its field totalDays, and its method displayAttendance() are package-private because no access modifiers are specified.

Another class in the school package can use them:

package school;

public class AttendanceReport {
    public static void main(String[] args) {
        Attendance attendance = new Attendance();
        attendance.displayAttendance();
    }
}

Output:

Total Days: 30

What Happens in Another Package?

If a class in a different package tries to import and use school.Attendance, compilation fails because the class is not public. Even if the class were public, package-private members would remain inaccessible from outside the package.

When Should You Use Package-Private?

  • For internal helper classes and methods.
  • For implementation details shared among classes in one package.
  • When you do not want to expose functionality as part of a public API.

7. Protected Access Modifier

The protected modifier allows access within the same package and also provides access to subclasses in other packages, subject to Java's inheritance access rules.

It is often used when a parent class provides functionality or state that subclasses are intended to use or customize.

7.1 Example Within the Same Package

package animals;

public class Animal {
    protected String name = "Animal";

    protected void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

Other classes in the animals package can access the protected field and method.

7.2 Example Using Inheritance Across Packages

Suppose the parent class is in package animals, while the subclass is in package pets.

// File: animals/Animal.java
package animals;

public class Animal {
    protected String name = "Dog";

    protected void displayName() {
        System.out.println(name);
    }
}
// File: pets/Dog.java
package pets;

import animals.Animal;

public class Dog extends Animal {
    public void showDetails() {
        System.out.println(name);
        displayName();
    }
}

The Dog subclass can access the inherited protected field and method through inheritance. However, outside the declaring package, protected access is more restricted than public access: code cannot freely access a protected member through any arbitrary parent-class instance. The access must satisfy Java's subclass rules.

When Should You Use Protected?

  • When subclasses need access to a member.
  • When designing a base class intended for extension.
  • When the member should remain hidden from unrelated classes in other packages.

Use protected carefully. Making too many fields protected can tightly couple subclasses to the internal implementation of a parent class.


8. Applying Access Modifiers to Classes

Access rules for classes depend on whether the class is declared at the top level or nested inside another class.

8.1 Top-Level Classes

A top-level class can use either public or package-private access. It cannot be declared private or protected.

public class Student {
    // Accessible from other packages when the type is available.
}

class Teacher {
    // Accessible only within the same package.
}

In a typical Java source file, a public top-level class must have the same name as the file. For example, Student is normally declared in Student.java.

8.2 Nested Classes

A nested class is declared inside another class. Unlike top-level classes, nested classes can use all four access levels.

public class University {
    private class InternalRecord {
        void display() {
            System.out.println("Internal record");
        }
    }

    public static class Department {
        public void display() {
            System.out.println("Department details");
        }
    }
}

The private nested class InternalRecord is restricted to the enclosing class's access scope, while the public static nested class Department can be accessed through the public enclosing type, provided the type is otherwise accessible.


9. Applying Access Modifiers to Fields

Fields store an object's or class's data. Their access level determines which code can directly read or change that data.

Modifier Typical Use Example
private Internal data protected by methods private double balance;
No modifier Data shared inside one package int count;
protected Data intended for package access and subclasses protected String name;
public Data intentionally exposed to callers public static final int MAX_SIZE = 100;

Recommended Approach

Fields are usually declared private, while controlled access is provided through public methods when required.

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

    public Product(String productName, double price) {
        this.productName = productName;
        setPrice(price);
    }

    public String getProductName() {
        return productName;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        if (price >= 0) {
            this.price = price;
        }
    }
}

The setter validates the price before updating it. This approach prevents ordinary callers from assigning a negative price directly to the private field.


10. Applying Access Modifiers to Methods

Methods define the operations that an object can perform. Their access modifiers determine who can call them.

public class ReportService {
    public void generateReport() {
        loadData();
        formatReport();
        System.out.println("Report generated");
    }

    private void loadData() {
        System.out.println("Data loaded");
    }

    private void formatReport() {
        System.out.println("Report formatted");
    }
}

The public method generateReport() is the operation exposed to callers. The private methods perform internal steps and can be changed without requiring callers to change their code.

Output when generateReport() is called:

Data loaded
Report formatted
Report generated

Choosing a Method's Access Level

  • Use public for operations intended for external callers.
  • Use private for internal helper operations.
  • Use package-private for operations shared only within a package.
  • Use protected when subclasses need access as part of an inheritance design.

11. Applying Access Modifiers to Constructors

Constructors initialize new objects. Their access modifiers control where an object can be created directly.

11.1 Public Constructor

public class Customer {
    public Customer() {
        System.out.println("Customer created");
    }
}

A public constructor allows accessible code to instantiate the class from other packages.

11.2 Private Constructor

public class AppConfig {
    private AppConfig() {
    }

    public static void showConfig() {
        System.out.println("Application configuration");
    }
}

The private constructor prevents ordinary code outside the class from directly creating an AppConfig object. This pattern can be useful for utility classes or designs that control instance creation.

11.3 Protected and Package-Private Constructors

A protected constructor permits access from the same package and, under Java's protected-constructor rules, from appropriate subclasses outside the package. A package-private constructor can be called by code in the same package but not by unrelated code in another package.


12. Access Modifiers and Encapsulation

Encapsulation is the practice of combining data and behavior in a class while controlling access to internal state. Access modifiers support encapsulation by limiting which fields and methods can be used directly.

public class BankAccount {
    private String accountHolder;
    private double balance;

    public BankAccount(String accountHolder) {
        this.accountHolder = accountHolder;
        this.balance = 0;
    }

    public String getAccountHolder() {
        return accountHolder;
    }

    public double getBalance() {
        return balance;
    }

    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    public boolean withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }
}

Example usage:

public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount("Aarav");
        account.deposit(2000);
        account.withdraw(500);

        System.out.println(account.getAccountHolder());
        System.out.println(account.getBalance());
    }
}

Output:

Aarav
1500.0

The private balance cannot be modified directly by ordinary external code. The public methods define how deposits and withdrawals are performed, and the withdrawal method prevents withdrawing more than the available balance.


13. Access Modifiers and Inheritance

Inheritance allows a subclass to extend a superclass. Access modifiers determine which superclass members the subclass can use.

Member Type Subclass in Same Package Subclass in Different Package
Public Accessible Accessible when the declaring type is accessible
Protected Accessible Accessible through the subclass inheritance rules
Package-private Accessible Not directly accessible
Private Not directly accessible Not directly accessible

Private members still exist as part of the superclass's implementation and can be used by its own methods, but a subclass cannot directly reference them by name. A superclass may expose controlled access through public or protected methods.

Example

class Vehicle {
    private int speed = 60;
    protected String brand = "Toyota";
    public void displaySpeed() {
        System.out.println("Speed: " + speed);
    }
}

class Car extends Vehicle {
    public void displayCar() {
        System.out.println("Brand: " + brand);
        displaySpeed();
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.displayCar();
    }
}

Output:

Brand: Toyota
Speed: 60

Explanation: The subclass can access the protected field brand. It cannot directly read the private field speed, but it can call the public method displaySpeed(), which accesses that field inside the declaring class.


14. Access Modifiers and Method Overriding

When a subclass overrides an inherited instance method, it cannot reduce the visibility of that method. The overriding method must have the same or broader access.

class Parent {
    protected void display() {
        System.out.println("Parent display");
    }
}

class Child extends Parent {
    @Override
    public void display() {
        System.out.println("Child display");
    }
}

This is valid because public is more accessible than protected.

The following example is invalid:

class Parent {
    public void display() {
        System.out.println("Parent display");
    }
}

class Child extends Parent {
    // Compilation error: weaker access privileges
    protected void display() {
        System.out.println("Child display");
    }
}

Key rule: An overriding method may widen visibility, but it cannot narrow the visibility of the inherited method. Private methods are not overridden in the usual sense because they are not inherited as accessible methods by subclasses.


15. Practical Example: Employee Management System

This example demonstrates access modifiers in a small employee management application.

public class Employee {
    private int id;
    private String name;
    private double salary;

    public Employee(int id, String name, double salary) {
        this.id = id;
        this.name = name;
        setSalary(salary);
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        if (salary >= 0) {
            this.salary = salary;
        }
    }

    protected void displayInternalDetails() {
        System.out.println("Employee ID: " + id);
        System.out.println("Employee Name: " + name);
    }

    public void displayDetails() {
        displayInternalDetails();
        System.out.println("Salary: " + salary);
    }
}

Test class:

public class Main {
    public static void main(String[] args) {
        Employee employee = new Employee(101, "Riya", 45000);
        employee.displayDetails();

        employee.setSalary(50000);
        System.out.println("Updated Salary: " + employee.getSalary());
    }
}

Output:

Employee ID: 101
Employee Name: Riya
Salary: 45000.0
Updated Salary: 50000.0

Concepts demonstrated:

  • Private fields protect the employee's internal data.
  • Public methods provide controlled access to employee details.
  • The salary setter validates the value before updating it.
  • The protected method can support access from subclasses and same-package code.
  • The public display method provides a simple operation for callers.

16. Common Mistakes When Using Access Modifiers

Mistake 1: Accessing a Private Field Directly

class Student {
    private int marks = 90;
}

class Main {
    public static void main(String[] args) {
        Student student = new Student();
        // Compilation error:
        // System.out.println(student.marks);
    }
}

Solution: Add an appropriate public getter if outside code needs to read the value.

Mistake 2: Declaring a Top-Level Class Private

// Invalid:
private class Student {
}

Solution: Declare the top-level class as public or use package-private access by omitting the modifier.

Mistake 3: Assuming Protected Means Public

A protected member is not accessible to every unrelated class in every package. Cross-package access must satisfy the subclass rules.

Solution: Use public only when broad access is intended. Otherwise, keep protected members within a deliberate inheritance design.

Mistake 4: Reducing Visibility When Overriding

A public superclass method cannot be overridden with a protected or private method.

Solution: Preserve or widen the original method's visibility.

Mistake 5: Making Every Field Public

Public fields let other classes change internal data without validation. This can lead to inconsistent object state.

Solution: Prefer private fields and expose necessary operations through methods.

Mistake 6: Confusing Default Access with Public Access

Omitting an access modifier does not make a class or member public. Package-private members are restricted to the declaring package.

Solution: Explicitly declare public members when they are intended for use outside the package.


17. Best Practices for Using Access Modifiers

  • Follow least privilege: Give each class or member only the visibility it needs.
  • Keep fields private: Prefer controlled methods over unrestricted direct field access.
  • Expose a clear public API: Keep public methods focused on the operations callers need.
  • Use package-private for internal collaboration: Avoid exposing implementation details across packages unnecessarily.
  • Use protected deliberately: Expose members to subclasses only when inheritance is part of the intended design.
  • Validate changes: Use methods to enforce business rules before changing important data.
  • Review nested types: Choose their access levels based on whether they are internal helpers or reusable types.
  • Understand inheritance: Check visibility rules before accessing superclass members or overriding methods.
  • Avoid unnecessary getters and setters: Provide methods that reflect the class's intended behavior rather than automatically exposing every field.
  • Document public APIs: Use clear names and documentation for methods intended for other developers.

18. Hands-On Exercises

Exercise 1: Student Class

  • Create a Student class with private fields for name, roll number, and marks.
  • Add public methods to read the values.
  • Add a method to update marks only when the value is between 0 and 100.
  • Test valid and invalid marks.

Exercise 2: Package-Private Access

  • Create two classes in the same package.
  • Declare one field and one method without access modifiers.
  • Access them from the second class.
  • Move the second class to another package and observe the compilation errors.

Exercise 3: Protected Inheritance

  • Create a superclass with a protected field and method.
  • Create a subclass in the same package and access those members.
  • Move the subclass to a different package and test access through inheritance.

Exercise 4: Employee Salary

  • Declare salary as private.
  • Provide a public method to update the salary after validation.
  • Try to assign salary directly from another class and observe the compilation error.
  • Explain why private fields improve encapsulation.

Mini Project: Library Management System

Create a basic library application using access modifiers correctly.

  • Create a public Book class.
  • Declare the book ID, title, and availability status as private fields.
  • Provide public methods to view book details and borrow or return a book.
  • Use private helper methods for internal validation.
  • Use package-private helpers for operations shared only within the library package.
  • Test that invalid operations do not change the book's state.

19. Interview Questions and Answers

Q1. What are access modifiers in Java?

Answer: Access modifiers control the visibility of classes and members. Java provides public, protected, private, and package-private access.

Q2. How many access modifiers does Java provide?

Answer: Java has three explicit access-modifier keywords: public, protected, and private. Including package-private, which is represented by no keyword, there are four access levels.

Q3. What is the difference between public and private?

Answer: Public members can be accessed from any location where the declaring type and other access requirements permit. Private members are directly accessible only within the declaring top-level class's body.

Q4. What is package-private access?

Answer: Package-private access applies when no access modifier is specified. The class or member is accessible from code in the same package.

Q5. What is the purpose of protected?

Answer: Protected access allows use within the same package and by appropriate subclasses in other packages, subject to Java's inheritance access rules.

Q6. Can a top-level class be private?

Answer: No. A top-level class can be public or package-private. Nested classes can use private and protected access as well.

Q7. Why are fields usually private?

Answer: Private fields protect internal state from uncontrolled changes. Public methods can validate input and provide controlled access.

Q8. Can a private method be overridden?

Answer: No, not in the normal overriding sense. A private method is not inherited as an accessible method by subclasses. A subclass may declare a separate method with the same signature, but that does not override the private method.

Q9. Can an overriding method have broader access?

Answer: Yes. An overriding method may widen visibility, such as changing protected access to public, but it cannot reduce visibility.

Q10. Can constructors have access modifiers?

Answer: Yes. Constructors can be public, protected, private, or package-private. Their access levels control where instances can be created directly.

Q11. Can a public method access private fields of its own class?

Answer: Yes. Methods declared within the same class can access that class's private fields and methods.

Q12. What is the relationship between access modifiers and encapsulation?

Answer: Access modifiers support encapsulation by restricting direct access to internal state and exposing only the operations that callers need.


20. Quick Revision Notes

  • public: broadest normal access, subject to type, package, and module accessibility.
  • private: direct access restricted to the declaring top-level class's body.
  • Package-private: no modifier; accessible within the same package.
  • protected: same-package access plus qualified subclass access across packages.
  • Top-level classes can be public or package-private, not private or protected.
  • Nested classes can use all four access levels.
  • Private fields help protect object state.
  • Public methods can provide controlled operations and validation.
  • Protected members should be designed carefully when inheritance is intended.
  • An overriding method cannot reduce the visibility of the method it overrides.
  • Constructor visibility determines where a class can be instantiated directly.
  • Prefer the least permissive access level that satisfies the design.

21. Conclusion

Applying access modifiers correctly is an essential part of writing secure, maintainable, and reusable Java programs. Public access supports operations intended for external callers, private access protects implementation details, package-private access enables collaboration within a package, and protected access supports package-level use and inheritance.

By choosing appropriate access levels for classes, fields, methods, and constructors, developers can implement encapsulation, reduce unwanted dependencies, and create clearer APIs. Practice these rules with small programs and multiple-package examples to build a strong foundation in Core Java and object-oriented programming.

Explore more learning opportunities through the JustAcademy Core Java Training Course, register for a course demo, or visit the JustAcademy website.

Previous Next
whatsapp