Java Modifiers

Keywords that control access and behavior

🔐 What are Modifiers?

Modifiers are keywords that control access levels and behavior of classes, methods, and attributes. They determine who can access your code and how it behaves, providing security and structure to your programs.


public class MyClass {
    private String secret;      // Only this class can access
    public String name;         // Anyone can access
    static int count;           // Belongs to class, not object
}
                                    

Types of Modifiers

🌐

public

Accessible from anywhere

public String name;
🔒

private

Only accessible within same class

private int age;
🏠

protected

Accessible within package and subclasses

protected double salary;

static

Belongs to class, not object instance

static int count;

🔹 Access Modifiers

Access modifiers control who can access your classes, methods, and attributes:

// Student.java
public class Student {
    // Public - accessible from anywhere
    public String name;
    public int studentId;
    
    // Private - only accessible within this class
    private double gpa;
    private String socialSecurityNumber;
    
    // Protected - accessible within package and subclasses
    protected String email;
    protected String phoneNumber;
    
    // Package-private (no modifier) - accessible within same package
    String address;
    String emergencyContact;
    
    // Constructor
    public Student(String name, int id, double gpa) {
        this.name = name;
        this.studentId = id;
        this.gpa = gpa;  // Can access private attribute within same class
    }
    
    // Public method - anyone can call
    public void displayPublicInfo() {
        System.out.println("Name: " + name);
        System.out.println("ID: " + studentId);
    }
    
    // Private method - only this class can call
    private boolean validateGPA(double gpa) {
        return gpa >= 0.0 && gpa <= 4.0;
    }
    
    // Public method to access private data (getter)
    public double getGPA() {
        return gpa;
    }
    
    // Public method to modify private data (setter)
    public void setGPA(double newGpa) {
        if (validateGPA(newGpa)) {  // Can call private method within same class
            this.gpa = newGpa;
            System.out.println("GPA updated to: " + gpa);
        } else {
            System.out.println("Invalid GPA value");
        }
    }
    
    // Protected method
    protected void displayContactInfo() {
        System.out.println("Email: " + email);
        System.out.println("Phone: " + phoneNumber);
    }
}

// StudentExample.java
public class StudentExample {
    public static void main(String[] args) {
        Student student = new Student("Alice Johnson", 12345, 3.8);
        
        // Accessing public attributes and methods
        System.out.println("Student Name: " + student.name);  // OK - public
        System.out.println("Student ID: " + student.studentId);  // OK - public
        student.displayPublicInfo();  // OK - public method
        
        // Accessing private attributes - THESE WOULD CAUSE ERRORS!
        // System.out.println(student.gpa);  // ERROR - private
        // student.validateGPA(3.5);  // ERROR - private method
        
        // Using public methods to access private data
        System.out.println("GPA: " + student.getGPA());  // OK - public getter
        student.setGPA(3.9);  // OK - public setter
        
        // Accessing package-private attributes
        student.address = "123 Main St";  // OK - same package
        student.emergencyContact = "John Doe";  // OK - same package
        
        System.out.println("Address: " + student.address);
    }
}

Output:

Student Name: Alice Johnson
Student ID: 12345
Name: Alice Johnson
ID: 12345
GPA: 3.8
GPA updated to: 3.9
Address: 123 Main St

🔹 Static Modifier

The static modifier makes attributes and methods belong to the class rather than individual objects:

// Counter.java
public class Counter {
    // Static attribute - shared by all objects
    private static int totalCount = 0;
    
    // Instance attribute - unique to each object
    private int instanceCount = 0;
    private String name;
    
    // Constructor
    public Counter(String name) {
        this.name = name;
        totalCount++;  // Increment static counter
        System.out.println("Counter '" + name + "' created. Total counters: " + totalCount);
    }
    
    // Instance method - works with specific object
    public void increment() {
        instanceCount++;
        System.out.println(name + " count: " + instanceCount);
    }
    
    // Static method - belongs to class, not object
    public static int getTotalCount() {
        return totalCount;
    }
    
    // Static method to reset all counters
    public static void resetTotalCount() {
        totalCount = 0;
        System.out.println("Total count reset to 0");
    }
    
    // Instance method to get instance count
    public int getInstanceCount() {
        return instanceCount;
    }
    
    // Instance method to display info
    public void displayInfo() {
        System.out.println("=== Counter Info ===");
        System.out.println("Name: " + name);
        System.out.println("Instance Count: " + instanceCount);
        System.out.println("Total Counters: " + totalCount);
        System.out.println();
    }
}

// CounterExample.java
public class CounterExample {
    public static void main(String[] args) {
        // Check initial total count (using static method)
        System.out.println("Initial total count: " + Counter.getTotalCount());
        
        // Create counter objects
        Counter counter1 = new Counter("Counter A");
        Counter counter2 = new Counter("Counter B");
        Counter counter3 = new Counter("Counter C");
        
        // Use instance methods
        counter1.increment();
        counter1.increment();
        counter1.displayInfo();
        
        counter2.increment();
        counter2.increment();
        counter2.increment();
        counter2.displayInfo();
        
        counter3.increment();
        counter3.displayInfo();
        
        // Check total count using static method
        System.out.println("Final total count: " + Counter.getTotalCount());
        
        // Reset total count using static method
        Counter.resetTotalCount();
        System.out.println("After reset: " + Counter.getTotalCount());
    }
}

Output:

Initial total count: 0
Counter 'Counter A' created. Total counters: 1
Counter 'Counter B' created. Total counters: 2
Counter 'Counter C' created. Total counters: 3
Counter A count: 1
Counter A count: 2
=== Counter Info ===
Name: Counter A
Instance Count: 2
Total Counters: 3

Counter B count: 1
Counter B count: 2
Counter B count: 3
=== Counter Info ===
Name: Counter B
Instance Count: 3
Total Counters: 3

Counter C count: 1
=== Counter Info ===
Name: Counter C
Instance Count: 1
Total Counters: 3

Final total count: 3
Total count reset to 0
After reset: 0

🔹 Final Modifier

The final modifier makes variables constant and prevents method overriding:

// MathConstants.java
public class MathConstants {
    // Final attributes - constants that cannot be changed
    public static final double PI = 3.14159;
    public static final int MAX_STUDENTS = 30;
    public static final String SCHOOL_NAME = "Java Academy";
    
    // Final instance variable - must be initialized in constructor
    private final String studentId;
    private final int enrollmentYear;
    
    // Regular variables that can be changed
    private String name;
    private double gpa;
    
    // Constructor
    public MathConstants(String studentId, int enrollmentYear, String name) {
        this.studentId = studentId;  // Final variable can be set once
        this.enrollmentYear = enrollmentYear;  // Final variable can be set once
        this.name = name;
    }
    
    // Final method - cannot be overridden in subclasses
    public final void displayStudentId() {
        System.out.println("Student ID: " + studentId);
        System.out.println("Enrollment Year: " + enrollmentYear);
    }
    
    // Regular method that can be overridden
    public void displayInfo() {
        System.out.println("Name: " + name);
        System.out.println("GPA: " + gpa);
        displayStudentId();  // Call final method
    }
    
    // Static method using final constants
    public static double calculateCircleArea(double radius) {
        return PI * radius * radius;  // Using final constant
    }
    
    // Method to demonstrate final variables
    public void demonstrateFinal() {
        final int localConstant = 100;  // Final local variable
        
        System.out.println("Constants:");
        System.out.println("PI: " + PI);
        System.out.println("Max Students: " + MAX_STUDENTS);
        System.out.println("School: " + SCHOOL_NAME);
        System.out.println("Local Constant: " + localConstant);
        
        // These would cause compilation errors:
        // PI = 3.14;  // ERROR - cannot modify final variable
        // studentId = "NEW123";  // ERROR - cannot modify final variable
        // localConstant = 200;  // ERROR - cannot modify final variable
    }
    
    // Getters and setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public double getGpa() { return gpa; }
    public void setGpa(double gpa) { this.gpa = gpa; }
    
    // Getters for final variables (no setters - they can't be changed)
    public String getStudentId() { return studentId; }
    public int getEnrollmentYear() { return enrollmentYear; }
}

// FinalExample.java
public class FinalExample {
    public static void main(String[] args) {
        // Create student with final values
        MathConstants student = new MathConstants("STU001", 2023, "John Doe");
        
        // Use final constants
        System.out.println("School: " + MathConstants.SCHOOL_NAME);
        System.out.println("Max Students: " + MathConstants.MAX_STUDENTS);
        
        // Calculate area using final constant
        double area = MathConstants.calculateCircleArea(5.0);
        System.out.println("Circle area (radius 5): " + area);
        
        // Display student info
        student.setGpa(3.7);
        student.displayInfo();
        
        System.out.println();
        
        // Demonstrate final variables
        student.demonstrateFinal();
        
        // Can change non-final attributes
        student.setName("Jane Doe");
        student.setGpa(3.9);
        System.out.println("\nAfter changes:");
        student.displayInfo();
        
        // Cannot change final attributes
        System.out.println("Student ID remains: " + student.getStudentId());
        System.out.println("Enrollment year remains: " + student.getEnrollmentYear());
    }
}

Output:

School: Java Academy
Max Students: 30
Circle area (radius 5): 78.53975
Name: John Doe
GPA: 3.7
Student ID: STU001
Enrollment Year: 2023

Constants:
PI: 3.14159
Max Students: 30
School: Java Academy
Local Constant: 100

After changes:
Name: Jane Doe
GPA: 3.9
Student ID: STU001
Enrollment Year: 2023
Student ID remains: STU001
Enrollment year remains: 2023

🔹 Modifier Summary

Access Modifiers:

  • public: Accessible everywhere
  • private: Only within same class
  • protected: Within package and subclasses
  • package-private (no modifier): Within same package

Non-Access Modifiers:

  • static: Belongs to class, not object
  • final: Cannot be changed/overridden
  • abstract: Must be implemented in subclass
  • synchronized: Thread-safe method

Best Practices:

  • Use private for internal data
  • Use public for methods that others need
  • Use static for utility methods and constants
  • Use final for constants and unchangeable data

🧠 Test Your Knowledge

Which modifier makes a variable accessible only within the same class?