Introduction
Imagine you're in a house with different rooms. Some rooms are open to everyone (public), some are only for family members (protected), some are just for you (private), and some follow the neighborhood rules (package-private). Access modifiers in Java work exactly like this - they control who can access your classes, methods, and variables!
Access modifiers are keywords that set the accessibility (visibility) of classes, methods, constructors, and fields. They're fundamental to encapsulation - one of the core principles of object-oriented programming.
What are Access Modifiers?
Access modifiers determine the scope and visibility of Java elements. They help you implement encapsulation by hiding internal implementation details and exposing only what's necessary.
The Four Access Levels:
| Modifier | Class | Package | Subclass | World |
|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ |
default (no modifier) | ✅ | ✅ | ❌ | ❌ |
protected | ✅ | ✅ | ✅ | ❌ |
public | ✅ | ✅ | ✅ | ✅ |
Code Explanation with Examples
Example 1: Comprehensive Access Modifier Demonstration
// File: Animal.java public class Animal { // 🎯 PUBLIC: Accessible from anywhere public String publicName; public void publicMethod() { System.out.println("Public method - anyone can call me!"); } // 🎯 PROTECTED: Accessible in package + subclasses (even in different packages) protected String protectedSpecies; protected void protectedMethod() { System.out.println("Protected method - package and subclasses only"); } // 🎯 DEFAULT (PACKAGE-PRIVATE): No modifier - accessible only in same package String defaultHabitat; // No modifier = default access void defaultMethod() { System.out.println("Default method - same package only"); } // 🎯 PRIVATE: Accessible only within the same class private String privateSecret; private void privateMethod() { System.out.println("Private method - only I can call myself!"); } // Constructor public Animal(String name, String species, String habitat) { this.publicName = name; this.protectedSpecies = species; this.defaultHabitat = habitat; this.privateSecret = "Classified information"; } // 🎯 Public method that uses private members (ENCAPSULATION) public String getPublicInfo() { return publicName + " is a " + protectedSpecies + " living in " + defaultHabitat; } // 🎯 Protected method that can access private members protected String getProtectedInfo() { return "Species: " + protectedSpecies + ", Secret: " + privateSecret; } // 🎯 Public method exposing controlled access to private data public void revealSecret() { privateMethod(); // Can call private method from within class System.out.println("Revealing secret: " + privateSecret); } // 🎯 Demonstration of access within same class public void demonstrateInternalAccess() { System.out.println("\n=== INTERNAL ACCESS DEMONSTRATION ==="); System.out.println("Accessing all fields from within the same class:"); System.out.println("Public: " + publicName); System.out.println("Protected: " + protectedSpecies); System.out.println("Default: " + defaultHabitat); System.out.println("Private: " + privateSecret); // Calling all methods from within same class publicMethod(); protectedMethod(); defaultMethod(); privateMethod(); } } // File: Dog.java (Same package as Animal) class Dog extends Animal { private String breed; public Dog(String name, String breed) { super(name, "Canine", "Domestic"); this.breed = breed; } // 🎯 Can access protected members from parent class public void displayProtectedInfo() { System.out.println("Accessing protected member from subclass: " + protectedSpecies); protectedMethod(); // Can call protected method } // 🎯 Can access default members (same package) public void displayDefaultInfo() { System.out.println("Accessing default member in same package: " + defaultHabitat); defaultMethod(); // Can call default method } // 🎯 CANNOT access private members from parent class public void tryAccessPrivate() { // ❌ This would cause compilation error: // System.out.println(privateSecret); // privateMethod(); System.out.println("Cannot access private members from subclass"); } public void demonstrateSubclassAccess() { System.out.println("\n=== SUBCLASS ACCESS DEMONSTRATION ==="); System.out.println("Public access: " + publicName); System.out.println("Protected access: " + protectedSpecies); System.out.println("Default access: " + defaultHabitat); // Private access: NOT POSSIBLE } } // File: AnimalDemo.java (Same package - demonstrating package access) public class AnimalDemo { public static void main(String[] args) { System.out.println("=== ACCESS MODIFIERS DEMONSTRATION ==="); Animal lion = new Animal("Leo", "Lion", "Savannah"); // 🎯 PUBLIC ACCESS - Available everywhere System.out.println("1. PUBLIC ACCESS:"); System.out.println("Name: " + lion.publicName); lion.publicMethod(); // 🎯 PROTECTED ACCESS - Available in same package System.out.println("\n2. PROTECTED ACCESS (same package):"); System.out.println("Species: " + lion.protectedSpecies); lion.protectedMethod(); // 🎯 DEFAULT ACCESS - Available in same package System.out.println("\n3. DEFAULT ACCESS (same package):"); System.out.println("Habitat: " + lion.defaultHabitat); lion.defaultMethod(); // 🎯 PRIVATE ACCESS - NOT available outside class System.out.println("\n4. PRIVATE ACCESS (outside class):"); // ❌ These would cause compilation errors: // System.out.println(lion.privateSecret); // lion.privateMethod(); System.out.println("Private members are not accessible outside the class"); // Using public methods to access private data (ENCAPSULATION) lion.revealSecret(); // 🎯 Demonstration with subclass System.out.println("\n" + "=".repeat(50)); Dog buddy = new Dog("Buddy", "Golden Retriever"); buddy.demonstrateSubclassAccess(); buddy.displayProtectedInfo(); buddy.displayDefaultInfo(); // 🎯 Internal access demonstration System.out.println("\n" + "=".repeat(50)); lion.demonstrateInternalAccess(); } } Output:
=== ACCESS MODIFIERS DEMONSTRATION === 1. PUBLIC ACCESS: Name: Leo Public method - anyone can call me! 2. PROTECTED ACCESS (same package): Species: Lion Protected method - package and subclasses only 3. DEFAULT ACCESS (same package): Habitat: Savannah Default method - same package only 4. PRIVATE ACCESS (outside class): Private members are not accessible outside the class Private method - only I can call myself! Revealing secret: Classified information ================================================== === SUBCLASS ACCESS DEMONSTRATION === Public access: Buddy Protected access: Canine Default access: Domestic Accessing protected member from subclass: Canine Protected method - package and subclasses only Accessing default member in same package: Domestic Default method - same package only ================================================== === INTERNAL ACCESS DEMONSTRATION === Accessing all fields from within the same class: Public: Leo Protected: Lion Default: Savannah Private: Classified information Public method - anyone can call me! Protected method - package and subclasses only Default method - same package only Private method - only I can call myself!
Example 2: Cross-Package Access Demonstration
// File: com/company/vehicle/Car.java package com.company.vehicle; public class Car { // Different access modifiers public String publicModel; protected String protectedEngine; String defaultColor; // package-private private String privateVIN; public Car(String model, String engine, String color, String vin) { this.publicModel = model; this.protectedEngine = engine; this.defaultColor = color; this.privateVIN = vin; } // Public method public void displayPublicInfo() { System.out.println("Model: " + publicModel); } // Protected method protected void displayProtectedInfo() { System.out.println("Engine: " + protectedEngine); } // Default method void displayDefaultInfo() { System.out.println("Color: " + defaultColor); } // Private method private void displayPrivateInfo() { System.out.println("VIN: " + privateVIN); } // Public method that uses private members public void getCarDetails() { System.out.println("Car: " + publicModel + ", " + protectedEngine + ", " + defaultColor); displayPrivateInfo(); // Internal access to private method } } // File: com/company/vehicle/SportsCar.java (Same package - extends Car) package com.company.vehicle; public class SportsCar extends Car { private int topSpeed; public SportsCar(String model, String engine, String color, String vin, int topSpeed) { super(model, engine, color, vin); this.topSpeed = topSpeed; } public void demonstrateAccess() { System.out.println("=== SUBCLASS IN SAME PACKAGE ==="); // ✅ PUBLIC - Always accessible System.out.println("Public: " + publicModel); displayPublicInfo(); // ✅ PROTECTED - Accessible to subclasses System.out.println("Protected: " + protectedEngine); displayProtectedInfo(); // ✅ DEFAULT - Accessible in same package System.out.println("Default: " + defaultColor); displayDefaultInfo(); // ❌ PRIVATE - Not accessible // System.out.println(privateVIN); // displayPrivateInfo(); } } // File: com/company/management/Garage.java (Different package) package com.company.management; import com.company.vehicle.Car; public class Garage { public void maintainCar(Car car) { System.out.println("=== DIFFERENT PACKAGE ACCESS ==="); // ✅ PUBLIC - Accessible from anywhere System.out.println("Public: " + car.publicModel); car.displayPublicInfo(); // ❌ PROTECTED - Not accessible from different package (unless subclass) // System.out.println(car.protectedEngine); // car.displayProtectedInfo(); // ❌ DEFAULT - Not accessible from different package // System.out.println(car.defaultColor); // car.displayDefaultInfo(); // ❌ PRIVATE - Never accessible outside class // System.out.println(car.privateVIN); // car.displayPrivateInfo(); System.out.println("Only public members are accessible from different package"); } } // File: com/company/management/FleetManager.java (Different package - extends Car) package com.company.management; import com.company.vehicle.Car; public class FleetManager extends Car { private int fleetSize; public FleetManager(String model, String engine, String color, String vin, int fleetSize) { super(model, engine, color, vin); this.fleetSize = fleetSize; } public void demonstrateCrossPackageAccess() { System.out.println("=== SUBCLASS IN DIFFERENT PACKAGE ==="); // ✅ PUBLIC - Always accessible System.out.println("Public: " + publicModel); displayPublicInfo(); // ✅ PROTECTED - Accessible to subclasses (even in different packages) System.out.println("Protected: " + protectedEngine); displayProtectedInfo(); // ❌ DEFAULT - Not accessible from different package // System.out.println(defaultColor); // displayDefaultInfo(); // ❌ PRIVATE - Never accessible outside class // System.out.println(privateVIN); // displayPrivateInfo(); } // Special case: accessing through 'this' reference public void accessThroughThis() { System.out.println("Access through 'this' reference:"); System.out.println("Public: " + this.publicModel); System.out.println("Protected: " + this.protectedEngine); // Default and private still not accessible } } // File: com/company/Main.java (Main class to demonstrate everything) package com.company; import com.company.vehicle.Car; import com.company.vehicle.SportsCar; import com.company.management.Garage; import com.company.management.FleetManager; public class Main { public static void main(String[] args) { System.out.println("=== CROSS-PACKAGE ACCESS MODIFIERS DEMO ==="); // Create instances Car myCar = new Car("Sedan", "V6", "Blue", "12345"); SportsCar sportsCar = new SportsCar("Ferrari", "V8", "Red", "67890", 200); Garage garage = new Garage(); FleetManager manager = new FleetManager("SUV", "V4", "Black", "11111", 10); // 🎯 Same package access System.out.println("\n1. SAME PACKAGE ACCESS:"); sportsCar.demonstrateAccess(); // 🎯 Different package access (non-subclass) System.out.println("\n2. DIFFERENT PACKAGE ACCESS (Non-subclass):"); garage.maintainCar(myCar); // 🎯 Different package access (subclass) System.out.println("\n3. DIFFERENT PACKAGE ACCESS (Subclass):"); manager.demonstrateCrossPackageAccess(); manager.accessThroughThis(); // 🎯 Direct access demonstration System.out.println("\n4. DIRECT ACCESS FROM MAIN:"); System.out.println("Public: " + myCar.publicModel); myCar.displayPublicInfo(); // Other access levels not available from main (different package) } } Output:
=== CROSS-PACKAGE ACCESS MODIFIERS DEMO === 1. SAME PACKAGE ACCESS: === SUBCLASS IN SAME PACKAGE === Public: Ferrari Model: Ferrari Protected: V8 Engine: V8 Default: Red Color: Red 2. DIFFERENT PACKAGE ACCESS (Non-subclass): === DIFFERENT PACKAGE ACCESS === Public: Sedan Model: Sedan Only public members are accessible from different package 3. DIFFERENT PACKAGE ACCESS (Subclass): === SUBCLASS IN DIFFERENT PACKAGE === Public: SUV Model: SUV Protected: V4 Engine: V4 Access through 'this' reference: Public: SUV Protected: V4 4. DIRECT ACCESS FROM MAIN: Public: Sedan Model: Sedan
Example 3: Real-World Encapsulation with Access Modifiers
// File: BankAccount.java public class BankAccount { // 🎯 PRIVATE: Internal implementation details (ENCAPSULATION) private String accountNumber; private double balance; private String accountHolder; private String socialSecurityNumber; private boolean isActive; // 🎯 PUBLIC: Constructor - needed to create objects public BankAccount(String accountHolder, String ssn, String accountNumber) { this.accountHolder = accountHolder; this.socialSecurityNumber = ssn; this.accountNumber = accountNumber; this.balance = 0.0; this.isActive = true; } // 🎯 PUBLIC: Methods that define the interface (what users can do) public void deposit(double amount) { if (amount > 0 && isActive) { balance += amount; System.out.println("Deposited: $" + amount); logTransaction("DEPOSIT", amount); } else { System.out.println("Invalid deposit amount or inactive account"); } } public boolean withdraw(double amount) { if (amount > 0 && balance >= amount && isActive) { balance -= amount; System.out.println("Withdrawn: $" + amount); logTransaction("WITHDRAW", amount); return true; } System.out.println("Insufficient funds or invalid amount"); return false; } public double getBalance() { return balance; } public String getAccountInfo() { return "Account: " + accountNumber + ", Holder: " + accountHolder + ", Balance: $" + balance; } // 🎯 PROTECTED: Methods for internal bank operations protected void applyInterest(double rate) { if (isActive && rate > 0) { double interest = balance * rate / 100; balance += interest; System.out.println("Interest applied: $" + interest); } } protected void freezeAccount() { isActive = false; System.out.println("Account frozen"); } protected void unfreezeAccount() { isActive = true; System.out.println("Account unfrozen"); } // 🎯 PRIVATE: Internal helper methods (implementation details) private void logTransaction(String type, double amount) { System.out.println("[LOG] " + type + ": $" + amount + " | Balance: $" + balance); } private boolean isValidAmount(double amount) { return amount > 0; } // 🎯 DEFAULT: Package-level operations void internalAudit() { System.out.println("[AUDIT] Account: " + accountNumber + ", Balance: $" + balance); } // 🎯 Demonstration of encapsulation benefits public void demonstrateEncapsulation() { System.out.println("\n=== ENCAPSULATION BENEFITS ==="); System.out.println("1. Data Protection: Balance can only be modified through controlled methods"); System.out.println("2. Validation: All operations validate input before modifying state"); System.out.println("3. Flexibility: Internal implementation can change without affecting users"); System.out.println("4. Security: Sensitive data (SSN) is completely hidden"); } } // File: SavingsAccount.java (Extends BankAccount) public class SavingsAccount extends BankAccount { private double interestRate; public SavingsAccount(String accountHolder, String ssn, String accountNumber, double interestRate) { super(accountHolder, ssn, accountNumber); this.interestRate = interestRate; } // 🎯 Can access protected method from parent public void applyAnnualInterest() { applyInterest(interestRate); // Protected method accessible to subclass System.out.println("Annual interest applied at rate: " + interestRate + "%"); } // 🎯 CANNOT access private members from parent public void tryAccessPrivate() { // ❌ These would cause compilation errors: // System.out.println(balance); // private // System.out.println(socialSecurityNumber); // private // logTransaction("TEST", 100); // private System.out.println("Cannot access private members from subclass"); } // 🎯 Override with additional functionality @Override public boolean withdraw(double amount) { // Savings account might have withdrawal limits if (amount > 1000) { System.out.println("Withdrawal limit exceeded for savings account"); return false; } return super.withdraw(amount); } } // File: Bank.java (Same package - demonstrates package access) public class Bank { private String bankName; public Bank(String bankName) { this.bankName = bankName; } // 🎯 Can access default methods in same package public void performMonthlyAudit(BankAccount account) { System.out.println("\n=== MONTHLY AUDIT for " + bankName + " ==="); account.internalAudit(); // Default method accessible in same package } // 🎯 Can access protected methods in same package public void handleFraudCase(BankAccount account) { System.out.println("\n=== FRAUD HANDLING ==="); account.freezeAccount(); // Protected method accessible in same package } public void demonstratePackageAccess() { System.out.println("\n=== PACKAGE-LEVEL ACCESS ==="); BankAccount account = new BankAccount("John Doe", "123-45-6789", "ACC123"); // Can access public, protected, and default members in same package account.deposit(1000); account.internalAudit(); // Default access account.applyInterest(2.5); // Protected access } } // File: BankDemo.java (Main demonstration class) public class BankDemo { public static void main(String[] args) { System.out.println("=== BANK ACCOUNT ENCAPSULATION DEMO ==="); // Create accounts BankAccount checking = new BankAccount("Alice Smith", "987-65-4321", "CHK001"); SavingsAccount savings = new SavingsAccount("Bob Johnson", "456-78-9012", "SAV001", 2.5); Bank cityBank = new Bank("City Bank"); // 🎯 Public interface usage System.out.println("\n1. PUBLIC INTERFACE USAGE:"); checking.deposit(1500); checking.withdraw(200); System.out.println("Balance: $" + checking.getBalance()); System.out.println(checking.getAccountInfo()); // 🎯 Savings account with protected access System.out.println("\n2. SAVINGS ACCOUNT OPERATIONS:"); savings.deposit(5000); savings.applyAnnualInterest(); // Uses protected method System.out.println("Savings balance: $" + savings.getBalance()); // 🎯 Package-level access demonstration System.out.println("\n3. PACKAGE-LEVEL OPERATIONS:"); cityBank.performMonthlyAudit(checking); cityBank.demonstratePackageAccess(); // 🎯 Encapsulation benefits System.out.println("\n4. ENCAPSULATION IN ACTION:"); checking.demonstrateEncapsulation(); // 🎯 Attempting invalid operations (prevented by encapsulation) System.out.println("\n5. INVALID OPERATIONS (PREVENTED):"); // ❌ Direct access to private fields not allowed // checking.balance = 1000000; // Compilation error // checking.socialSecurityNumber = "hacked"; // Compilation error // ✅ Must use public methods with validation checking.deposit(-100); // Invalid - rejected checking.withdraw(1000000); // Invalid - rejected // 🎯 Security demonstration System.out.println("\n6. SECURITY FEATURES:"); System.out.println("Sensitive data (SSN) is completely hidden from external access"); System.out.println("Balance can only be modified through validated methods"); System.out.println("Internal logging and auditing are hidden implementation details"); } } Output:
=== BANK ACCOUNT ENCAPSULATION DEMO === 1. PUBLIC INTERFACE USAGE: Deposited: $1500.0 [LOG] DEPOSIT: $1500.0 | Balance: $1500.0 Withdrawn: $200.0 [LOG] WITHDRAW: $200.0 | Balance: $1300.0 Balance: $1300.0 Account: CHK001, Holder: Alice Smith, Balance: $1300.0 2. SAVINGS ACCOUNT OPERATIONS: Deposited: $5000.0 [LOG] DEPOSIT: $5000.0 | Balance: $5000.0 Interest applied: $125.0 Annual interest applied at rate: 2.5% Savings balance: $5125.0 3. PACKAGE-LEVEL OPERATIONS: === MONTHLY AUDIT for City Bank === [AUDIT] Account: CHK001, Balance: $1300.0 === PACKAGE-LEVEL ACCESS === Deposited: $1000.0 [LOG] DEPOSIT: $1000.0 | Balance: $1000.0 [AUDIT] Account: ACC123, Balance: $1000.0 Interest applied: $25.0 4. ENCAPSULATION IN ACTION: === ENCAPSULATION BENEFITS === 1. Data Protection: Balance can only be modified through controlled methods 2. Validation: All operations validate input before modifying state 3. Flexibility: Internal implementation can change without affecting users 4. Security: Sensitive data (SSN) is completely hidden 5. INVALID OPERATIONS (PREVENTED): Invalid deposit amount or inactive account Insufficient funds or invalid amount 6. SECURITY FEATURES: Sensitive data (SSN) is completely hidden from external access Balance can only be modified through validated methods Internal logging and auditing are hidden implementation details
Example 4: Access Modifiers with Constructors and Static Members
// File: Student.java public class Student { // 🎯 Instance variables with different access levels public String publicName; protected int protectedAge; String defaultGrade; // package-private private String privateEmail; // 🎯 Static variables with different access levels public static String publicSchool = "Public High School"; protected static int protectedStudentCount = 0; static String defaultPrincipal = "John Smith"; // package-private private static String privateSchoolMotto = "Knowledge is Power"; // 🎯 CONSTRUCTORS with different access levels // Public constructor - anyone can create Student objects public Student(String name, int age, String grade, String email) { this.publicName = name; this.protectedAge = age; this.defaultGrade = grade; this.privateEmail = email; protectedStudentCount++; } // Protected constructor - only subclasses and same package can use protected Student(String name, int age) { this(name, age, "Unknown", "[email protected]"); } // Default constructor - only same package can use Student(String name) { this(name, 0, "Unknown", "[email protected]"); } // Private constructor - only this class can use (for singleton/factory patterns) private Student() { this("Anonymous", 0, "Unknown", "[email protected]"); } // 🎯 Static factory method using private constructor public static Student createAnonymousStudent() { return new Student(); // Can use private constructor within class } // 🎯 METHODS with different access levels // Public method public void displayPublicInfo() { System.out.println("Student: " + publicName + ", Grade: " + defaultGrade); } // Protected method protected void displayProtectedInfo() { System.out.println("Age: " + protectedAge + ", Email: " + privateEmail); } // Default method void displayDefaultInfo() { System.out.println("All info: " + publicName + ", " + protectedAge + ", " + defaultGrade); } // Private method private void displayPrivateInfo() { System.out.println("Private email: " + privateEmail); } // 🎯 Static methods with different access levels public static void displayPublicStaticInfo() { System.out.println("School: " + publicSchool + ", Students: " + protectedStudentCount); } protected static void displayProtectedStaticInfo() { System.out.println("Principal: " + defaultPrincipal + ", Motto: " + privateSchoolMotto); } static void displayDefaultStaticInfo() { System.out.println("School info: " + publicSchool + ", " + defaultPrincipal); } private static void displayPrivateStaticInfo() { System.out.println("Secret motto: " + privateSchoolMotto); } // 🎯 Public method that uses private static members public static void revealMotto() { displayPrivateStaticInfo(); } // 🎯 Demonstration method public void demonstrateAccess() { System.out.println("\n=== INTERNAL ACCESS DEMONSTRATION ==="); // Instance members System.out.println("Public: " + publicName); System.out.println("Protected: " + protectedAge); System.out.println("Default: " + defaultGrade); System.out.println("Private: " + privateEmail); // Static members System.out.println("Public Static: " + publicSchool); System.out.println("Protected Static: " + protectedStudentCount); System.out.println("Default Static: " + defaultPrincipal); System.out.println("Private Static: " + privateSchoolMotto); } } // File: CollegeStudent.java (Subclass) public class CollegeStudent extends Student { private String major; // Can use public and protected constructors public CollegeStudent(String name, int age, String grade, String email, String major) { super(name, age, grade, email); // Public constructor this.major = major; } // Can use protected constructor public CollegeStudent(String name, int age, String major) { super(name, age); // Protected constructor this.major = major; } // ❌ Cannot use default or private constructors from different package // public CollegeStudent(String name) { // super(name); // Default constructor - not accessible // } public void demonstrateSubclassAccess() { System.out.println("=== SUBCLASS ACCESS ==="); // Instance members System.out.println("Public: " + publicName); System.out.println("Protected: " + protectedAge); // System.out.println(defaultGrade); // Not accessible (different package) // System.out.println(privateEmail); // Not accessible // Static members System.out.println("Public Static: " + publicSchool); System.out.println("Protected Static: " + protectedStudentCount); // System.out.println(defaultPrincipal); // Not accessible // System.out.println(privateSchoolMotto); // Not accessible } // Can access protected methods public void showProtectedInfo() { displayProtectedInfo(); // Protected method accessible } } // File: School.java (Same package demonstration) class School { public void demonstratePackageAccess() { System.out.println("=== SAME PACKAGE ACCESS ==="); // Can use public, protected, and default constructors Student student1 = new Student("John", 16, "10th", "[email protected]"); // Public Student student2 = new Student("Jane", 15); // Protected Student student3 = new Student("Mike"); // Default // Student student4 = new Student(); // Private - not accessible // Accessing members System.out.println("Public: " + student1.publicName); System.out.println("Protected: " + student1.protectedAge); System.out.println("Default: " + student1.defaultGrade); // System.out.println(student1.privateEmail); // Not accessible // Calling methods student1.displayPublicInfo(); student1.displayProtectedInfo(); student1.displayDefaultInfo(); // student1.displayPrivateInfo(); // Not accessible // Static access System.out.println("Public Static: " + Student.publicSchool); System.out.println("Protected Static: " + Student.protectedStudentCount); System.out.println("Default Static: " + Student.defaultPrincipal); // System.out.println(Student.privateSchoolMotto); // Not accessible } } // File: AccessModifiersDemo.java (Main class) public class AccessModifiersDemo { public static void main(String[] args) { System.out.println("=== ACCESS MODIFIERS WITH CONSTRUCTORS AND STATIC ==="); // 🎯 Creating objects with different constructors Student student1 = new Student("Alice", 17, "11th", "[email protected]"); Student anonymous = Student.createAnonymousStudent(); // 🎯 Access demonstration student1.demonstrateAccess(); // 🎯 Static access demonstration System.out.println("\n=== STATIC MEMBER ACCESS ==="); System.out.println("Public static: " + Student.publicSchool); Student.displayPublicStaticInfo(); // 🎯 Subclass demonstration System.out.println("\n=== SUBCLASS ACCESS ==="); CollegeStudent collegeStudent = new CollegeStudent("Bob", 20, "A", "[email protected]", "Computer Science"); collegeStudent.demonstrateSubclassAccess(); collegeStudent.showProtectedInfo(); // 🎯 Same package demonstration System.out.println("\n=== SAME PACKAGE ACCESS ==="); School school = new School(); school.demonstratePackageAccess(); // 🎯 Constructor access summary System.out.println("\n=== CONSTRUCTOR ACCESS SUMMARY ==="); System.out.println("Public constructor: Accessible from anywhere"); System.out.println("Protected constructor: Accessible in package + subclasses"); System.out.println("Default constructor: Accessible only in same package"); System.out.println("Private constructor: Accessible only within same class"); // 🎯 Static access summary System.out.println("\n=== STATIC MEMBER ACCESS SUMMARY ==="); System.out.println("Follows the same rules as instance members"); System.out.println("Accessed through ClassName.memberName"); } } Output:
=== ACCESS MODIFIERS WITH CONSTRUCTORS AND STATIC === === INTERNAL ACCESS DEMONSTRATION === Public: Alice Protected: 17 Default: 11th Private: [email protected] Public Static: Public High School Protected Static: 2 Default Static: John Smith Private Static: Knowledge is Power === STATIC MEMBER ACCESS === Public static: Public High School School: Public High School, Students: 2 === SUBCLASS ACCESS === === SUBCLASS ACCESS === Public: Bob Protected: 20 Public Static: Public High School Protected Static: 3 Age: 20, Email: [email protected] === SAME PACKAGE ACCESS === === SAME PACKAGE ACCESS === Public: John Protected: 16 Default: 10th Student: John, Grade: 10th Age: 16, Email: [email protected] All info: John, 16, 10th Public Static: Public High School Protected Static: 6 Default Static: John Smith === CONSTRUCTOR ACCESS SUMMARY === Public constructor: Accessible from anywhere Protected constructor: Accessible in package + subclasses Default constructor: Accessible only in same package Private constructor: Accessible only within same class === STATIC MEMBER ACCESS SUMMARY === Follows the same rules as instance members Accessed through ClassName.memberName
Access Modifiers Summary Table
| Modifier | Class | Package | Subclass | World | Typical Use |
|---|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ | Internal implementation, sensitive data |
default | ✅ | ✅ | ❌ | ❌ | Package-level utilities, internal APIs |
protected | ✅ | ✅ | ✅ | ❌ | Framework hooks, subclass customization |
public | ✅ | ✅ | ✅ | ✅ | Public API, interfaces, constants |
Best Practices
- Use the most restrictive access level possible - start with
privateand increase as needed - Make fields
privateand provide public getters/setters for encapsulation - Use
publicfor API methods that users need to call - Use
protectedfor methods meant to be overridden by subclasses - Use package-private (
default) for internal implementation details - Keep constructors
privatefor singleton patterns or factory methods - Use
public static finalfor constants
Common Patterns
Encapsulation Pattern:
public class BankAccount { private double balance; public double getBalance() { return balance; } public void deposit(double amount) { if (amount > 0) balance += amount; } } Singleton Pattern:
public class Singleton { private static Singleton instance; private Singleton() {} // Private constructor public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } Factory Pattern:
public class AnimalFactory { private AnimalFactory() {} // Private constructor public static Animal createAnimal(String type) { switch (type) { case "dog": return new Dog(); case "cat": return new Cat(); default: throw new IllegalArgumentException(); } } } Conclusion
Access modifiers are fundamental to Java's object-oriented design:
- ✅ Enable encapsulation by hiding implementation details
- ✅ Provide security by controlling access to sensitive data
- ✅ Support maintainability by defining clear APIs
- ✅ Facilitate code organization through package structuring
Key Takeaways:
private- Class-level privacy (most restrictive)default- Package-level privacy (no modifier)protected- Package + subclass accesspublic- Global access (least restrictive)
Remember: Good access modifier usage is like good architecture - it creates clear boundaries, protects what's important, and makes the system more maintainable and secure. Choose your access levels thoughtfully! 🏗️🔒
Now you have a comprehensive understanding of how to control visibility and build well-encapsulated Java applications!