Access modifiers are keywords used to define the visibility or scope of a class, method, or variable in a program. They are commonly used in Java, C++, and C#. Below are some of the most frequently asked interview questions on access modifiers.
Java has four access modifiers :
C++ has three access specifiers :
In Java, the default access modifier is no keyword. When you don't explicitly specify any access modifier (like public
, private
, or protected
) for a class, method, or variable, it has default access.
Key Characteristics of Default Access Modifier:
// In the 'mypackage' package:
class MyClass { // Default access (package-private)
int myVariable; // Default access (package-private)
void myMethod() { // Default access (package-private)
System.out.println("Hello from myMethod!");
}
}
// In a different package (e.g., 'anotherpackage'):
import mypackage.MyClass; // This import won't work as expected
public class AnotherClass {
public static void main(String[] args) {
MyClass obj = new MyClass(); // Error: MyClass is not accessible
obj.myMethod(); // Error: myMethod is not accessible
System.out.println(obj.myVariable); // Error: myVariable is not accessible
}
}?
Why Use Default Access?
No, you cannot use access modifiers (public, private, protected) with local variables in Java.
Why Not?
public class MyClass {
public void myMethod() {
int localVar = 10; // This is a local variable
// You cannot add access modifiers here:
// public int anotherVar = 20; // This will cause a compile-time error
// private int yetAnotherVar = 30; // This will also cause an error
}
}?
What Can You Use with Local Variables?
While you can't use access modifiers, you can use the final
keyword with local variables. final
means the local variable's value cannot be changed after it is initialized.
public void myMethod() {
final int localVar = 10; // localVar's value cannot be changed later
// localVar = 20; // This would cause a compile-time error
}
Feature | Private | Protected |
---|---|---|
Scope | Only within the class. | Within the class and its subclasses. |
Inheritance | Not accessible in child classes. | Accessible in child classes. |
Use case | Data encapsulation. | Allows controlled access in subclasses. |
No, a private method cannot be overridden in Java.
Here's why :
Visibility: The private
access modifier means that a method (or variable) is only accessible within the class in which it is declared. Subclasses, even if they are in the same package, cannot access private members of the superclass.
Overriding Mechanism: Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its superclass. The subclass method must have the same signature (name and parameter types) as the superclass method. Because a subclass cannot even see a private method of the superclass, it cannot provide an overriding implementation.
Hiding, Not Overriding: If a subclass declares a method with the same signature as a private method in the superclass, it's not overriding. It's hiding the superclass method. The subclass effectively creates its own, entirely separate method with the same name. There's no polymorphism involved; the superclass's private method remains inaccessible and unaffected.
class Superclass {
private void myMethod() {
System.out.println("Superclass's myMethod");
}
public void callMethod() {
myMethod(); // Accessing the private method within the same class
}
}
class Subclass extends Superclass {
// This is NOT overriding; it's hiding
private void myMethod() {
System.out.println("Subclass's myMethod");
}
// Even if we try to use public access:
// public void myMethod() { // Still hiding, not overriding
// System.out.println("Subclass's myMethod");
// }
public void callSubclassMethod() {
myMethod(); // Calls the Subclass's myMethod
}
}
public class Main {
public static void main(String[] args) {
Superclass superClass = new Superclass();
Subclass subclass = new Subclass();
superClass.callMethod(); // Output: Superclass's myMethod
subclass.callMethod(); // Output: Superclass's myMethod (still calls the superclass method, it's not overridden!)
subclass.callSubclassMethod(); // Output: Subclass's myMethod
}
}
As you can see, even though Subclass
has a myMethod
, it doesn't override the Superclass
's myMethod
. When subclass.callMethod()
is invoked, it still calls the myMethod
defined in Superclass
because the Subclass
's myMethod
is a completely separate, private method within the Subclass
.
In short, private methods are not part of the inheritance hierarchy in Java. They are confined to the class in which they are declared. Therefore, they cannot be overridden.
You can change the access modifier of an overridden method, but there are restrictions: you can only make the overridden method's access less restrictive, or keep it the same. You cannot make it more restrictive.
Here's a breakdown:
Valid Changes:
private
-> default
-> protected
-> public
(increasing accessibility)Invalid Changes:
public
-> protected
-> default
-> private
(decreasing accessibility)Why the Restriction?
The reason for this restriction is to maintain the principle of polymorphism and ensure that subclasses can be used interchangeably with their superclasses without causing unexpected access issues.
Let's illustrate with an example :
class Animal {
public void makeSound() {
System.out.println("Generic animal sound");
}
}
class Dog extends Animal {
// Valid: Widening access (public -> public) or keeping it the same
@Override
public void makeSound() {
System.out.println("Woof!");
}
// Invalid: Narrowing access (public -> protected or default or private)
// @Override
// protected void makeSound() { // Compile-time error!
// System.out.println("Woof!");
// }
// @Override
// void makeSound() { // Compile-time error!
// System.out.println("Woof!");
// }
// @Override
// private void makeSound() { // Compile-time error!
// System.out.println("Woof!");
// }
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Animal();
Animal myDog = new Dog(); // Polymorphism: Dog is an Animal
myAnimal.makeSound(); // Output: Generic animal sound
myDog.makeSound(); // Output: Woof! (Correctly calls Dog's makeSound)
}
}
If you were allowed to make the makeSound
method in Dog
protected
, default
, or private
, the line myDog.makeSound()
would result in a compile-time error or a runtime exception because the makeSound
method wouldn't be accessible through the Animal
reference (myDog
). This would break the principle of polymorphism, where you expect a Dog
object to behave like an Animal
object.
In essence, the access modifier of an overridden method must be at least as accessible as the access modifier of the method it overrides. This rule ensures that subclasses can be used polymorphically without violating access restrictions.
No, you cannot declare a top-level class (a class that is not nested inside another class) as private
in Java. The private
access modifier is not allowed for top-level classes.
Why Not?
The purpose of the private
access modifier is to restrict access to a member (variable or method) of a class to only within that class itself. A top-level class, by definition, is not a member of any other class. Therefore, the concept of making it private
doesn't make sense. There's no enclosing class to restrict access from.
Valid Access Modifiers for Top-Level Classes:
For top-level classes, you have only two options:
public
: A public
class is accessible from any other class, regardless of the package.
Default (no access modifier): If you don't specify any access modifier, the class has package-private or package-protected access. This means it is accessible only by other classes within the same package.
Example :
// Valid: public class
public class MyPublicClass {
// ...
}
// Valid: package-private class
class MyPackagePrivateClass { // No access modifier specified
// ...
}
// Invalid: private class (Compile-time error)
// private class MyPrivateClass { // This will cause a compile-time error
// // ...
// }
Inner Classes:
It's important to distinguish top-level classes from inner classes (nested classes). Inner classes can be declared as private
. In this case, the private
inner class is only accessible within the enclosing class. This is a valid and useful way to encapsulate implementation details within a class.
class OuterClass {
private class InnerClass { // Private inner class - perfectly valid
// ...
}
}
In summary, private
is not a valid access modifier for top-level classes. Use public
for classes you want accessible from anywhere, and use the default (no access modifier) for classes you only want accessible within the same package. private
is used for inner classes to encapsulate them within their enclosing class.
public int myVar;?
private int myVar;?
protected int myVar;?
internal int myVar;
Example (Java):
int myVar;?
protected internal int myVar;?
deposit()
and withdraw()
. To maintain security and prevent unauthorized access or manipulation of these attributes and methods, they should be declared as private.class BankAccount {
private int accountNumber;
private double balance;
private void deposit(double amount) { /*...*/ }
private void withdraw(double amount) { /*...*/ }
}?
deposit()
and withdraw()
. To ensure data encapsulation and prevent unauthorized access or modification of sensitive data (e.g., balance), we should use private access modifiers for the attributes. This restricts their direct access to only within the BankAccount class, unlike protected or public which would allow access from subclasses or any other classes respectively.class BankAccount {
private int accountNumber;
private double balance;
public void deposit(double amount) {
// Deposit logic
}
public void withdraw(double amount) {
// Withdrawal logic
}
}?
Both default
(no access modifier) and protected
access modifiers in Java deal with visibility and accessibility, but they differ in how they handle access from subclasses. Here's a breakdown:
Default (No Access Modifier):
Protected:
Key Differences Summarized :
Feature | Default (No Modifier) | Protected |
---|---|---|
Same Package | Accessible | Accessible |
Different Package | Not Accessible | Accessible by subclasses |
Subclasses | Not Accessible (if in a different package) | Accessible (even if in a different package) |
// In package 'mypackage':
class MyClass {
int defaultVariable = 10; // Default access
protected int protectedVariable = 20; // Protected access
void defaultMethod() { } // Default access
protected void protectedMethod() { } // Protected access
}
// In package 'anotherpackage':
import mypackage.MyClass;
class MySubclass extends MyClass { // MySubclass is in a different package
public void myMethod() {
// Accessing members of MyClass from a subclass in a different package:
// System.out.println(defaultVariable); // Compile-time error: defaultVariable is not accessible
System.out.println(protectedVariable); // OK: protectedVariable is accessible by subclasses
// defaultMethod(); // Compile-time error: defaultMethod is not accessible
protectedMethod(); // OK: protectedMethod is accessible by subclasses
}
}
class AnotherClass { // AnotherClass is in a different package (not a subclass)
public void anotherMethod() {
MyClass obj = new MyClass();
// Accessing members of MyClass from a class in a different package (not a subclass):
// System.out.println(obj.defaultVariable); // Compile-time error: defaultVariable is not accessible
// System.out.println(obj.protectedVariable); // Compile-time error: protectedVariable is not accessible (unless AnotherClass is also a subclass)
// obj.defaultMethod(); // Compile-time error: defaultMethod is not accessible
// obj.protectedMethod(); // Compile-time error: protectedMethod is not accessible (unless AnotherClass is also a subclass)
}
}?
When to Use Which?
No, you cannot override a method in Java and make its access modifier more restrictive. This is a fundamental rule of method overriding.
Why Not?
The reason for this restriction is to preserve the principle of polymorphism and maintain consistent behavior when using objects of different classes in an inheritance hierarchy.
Let's illustrate with an example :
class Animal {
public void makeSound() {
System.out.println("Generic animal sound");
}
}
class Dog extends Animal {
// Valid override: Access modifier is the same (public)
@Override
public void makeSound() {
System.out.println("Woof!");
}
// Invalid override: Making the method more restrictive (public -> protected)
// @Override
// protected void makeSound() { // Compile-time error!
// System.out.println("Woof!");
// }
// Invalid override: Making the method more restrictive (public -> default)
// @Override
// void makeSound() { // Compile-time error!
// System.out.println("Woof!");
// }
// Invalid override: Making the method more restrictive (public -> private)
// @Override
// private void makeSound() { // Compile-time error!
// System.out.println("Woof!");
// }
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Animal();
Animal myDog = new Dog(); // Polymorphism: Dog is an Animal
myAnimal.makeSound(); // Output: Generic animal sound
myDog.makeSound(); // Output: Woof! (Correctly calls Dog's makeSound)
}
}
The Problem:
If you were allowed to make the makeSound
method in Dog
protected
, default
, or private
, the line myDog.makeSound()
in the main
method would cause a compile-time error. This is because the Animal
reference myDog
would no longer have access to the makeSound
method in the Dog
class. This would break the fundamental principle of polymorphism, which states that you should be able to treat a Dog
object as an Animal
object without any unexpected behavior.
The Rule:
The access modifier of an overridden method must be the same as or less restrictive than the access modifier of the method being overridden. In other words, you can widen the access (e.g., private
-> default
-> protected
-> public
), but you cannot narrow it. This rule ensures that polymorphism works correctly and that subclasses can be used interchangeably with their superclasses without causing access violations.
There are several ways to restrict object creation of a class in Java, depending on the level of restriction you want to enforce. Here are the most common techniques:
1. Private Constructor:
This is the most common and effective way to prevent direct instantiation of a class. By making the constructor private
, you prevent any class outside of the class itself from creating instances.
public class MyRestrictedClass {
private MyRestrictedClass() {
// Private constructor - no one outside this class can create an instance
}
public static MyRestrictedClass getInstance() {
return new MyRestrictedClass(); // Only the class itself can create an instance
}
// Other methods...
}
public class Main {
public static void main(String[] args) {
// MyRestrictedClass obj = new MyRestrictedClass(); // Compile-time error!
MyRestrictedClass obj = MyRestrictedClass.getInstance(); // Correct way to get an instance
}
}
This pattern is often used for singleton classes (where you only want one instance of the class) or utility classes (classes with static methods and no instance variables). The getInstance()
method acts as a factory, controlling how and when instances are created (or if they are created at all).
2. Factory Class :
You can create a separate factory class whose sole purpose is to create instances of the restricted class. The restricted class can have a protected
or default constructor, allowing the factory class (which is likely in the same package) to create instances, but preventing other classes from doing so.
// Restricted class (in package 'mypackage')
class MyRestrictedClass {
MyRestrictedClass() { } // Default constructor (accessible within the package)
}
// Factory class (in package 'mypackage')
class MyRestrictedClassFactory {
public static MyRestrictedClass createInstance() {
return new MyRestrictedClass();
}
}
// Main class (in a different package, or even the same)
public class Main {
public static void main(String[] args) {
// MyRestrictedClass obj = new MyRestrictedClass(); // Compile-time error (if in a different package)
MyRestrictedClass obj = MyRestrictedClassFactory.createInstance(); // OK
}
}
3. Abstract Class:
If you declare a class as abstract
, you cannot create instances of it directly. You can only create instances of its concrete subclasses. This is useful when you want to define a common interface or behavior but don't want to allow direct instantiation of the base class.
abstract class MyAbstractClass {
// ... abstract methods ...
}
class MyConcreteClass extends MyAbstractClass {
// ... implementation of abstract methods ...
}
public class Main {
public static void main(String[] args) {
// MyAbstractClass obj = new MyAbstractClass(); // Compile-time error!
MyConcreteClass obj = new MyConcreteClass(); // OK
}
}
4. Using an Interface (with a Factory):
You can combine an interface with a factory class. The interface defines the contract, and the factory class creates instances of a class that implements the interface. This provides flexibility and hides the concrete implementation.
interface MyInterface {
// ... interface methods ...
}
class MyClass implements MyInterface {
private MyClass() {} // Private constructor
// ... implementation ...
}
class MyFactory {
public static MyInterface create() {
return new MyClass();
}
}
Choosing the Right Approach :
The private constructor approach is generally the most straightforward and commonly used when you want to completely prevent direct instantiation of a class. The other methods offer varying degrees of control and flexibility depending on your specific design requirements.
Access modifiers are a cornerstone of encapsulation in Java. They directly control the visibility and accessibility of a class's members (variables and methods), allowing you to hide internal implementation details and protect the integrity of your objects. Here's how they contribute to encapsulation:
1. Hiding Implementation Details:
private
access: When you declare a member as private
, it becomes accessible only within the class itself. No other class, not even a subclass, can directly access it. This allows you to hide the internal state and workings of your class. For example, you might have a private
variable to store data in a specific format, and you don't want external classes to directly manipulate that variable. Instead, you provide public
methods (getters and setters) to control access to the data in a controlled manner.
Example:
public class Car {
private int speed; // Private - hidden from outside access
public void accelerate() {
speed += 10; // Controlled modification within the class
}
public int getSpeed() {
return speed; // Controlled access to the speed
}
}
In this example, the speed
variable is private
. External classes cannot directly change the speed
of a Car
object. They must use the accelerate()
method, which allows the Car
class to control how the speed
is modified. This prevents external classes from setting the speed
to an invalid value (e.g., a negative speed).
2. Controlling Access :
public
access: public
members are accessible from any other class. You use public
for methods that you want other classes to be able to call and for variables that you want other classes to be able to access directly (though direct access to variables is generally discouraged for good encapsulation). public
methods define the interface of your class – how other classes interact with it.
protected
access: protected
members are accessible within the same package and by subclasses, even if those subclasses are in a different package. protected
is often used when you want to allow subclasses to access and potentially override certain members, but you don't want unrelated classes to have access.
default
access (no modifier): default
members are accessible only within the same package. This is useful for hiding members that are part of the internal implementation of a package, but you don't want to expose to other packages.
3. Data Integrity :
By using access modifiers, you can enforce rules about how data is modified. For example, you can use setters to validate input before changing the value of a private
variable. This helps to prevent data corruption and maintain the consistency of your objects.
4. Abstraction :
Encapsulation, facilitated by access modifiers, is closely related to abstraction. Abstraction means hiding complex implementation details and exposing only a simplified interface. Access modifiers help you achieve abstraction by allowing you to hide the internal workings of your class and provide only public
methods for interacting with it.
In summary : Access modifiers are essential for encapsulation because they allow you to :
private
members.public
, protected
, and default
.No, private members cannot be accessed directly in a subclass in Java because the private
access modifier restricts access to within the same class only.
Although direct access is not possible, there are a few indirect ways to use private members in a subclass:
The superclass can provide getter and setter methods to allow controlled access to private members:
class Parent {
private String secret = "Private Data";
public String getSecret() {
return secret;
}
}
class Child extends Parent {
void display() {
System.out.println("Accessing via getter: " + getSecret());
}
}
public class Test {
public static void main(String[] args) {
Child child = new Child();
child.display();
}
}
2. Use Protected Members Instead
If you want a subclass to access a member directly, use protected
instead of private
:
class Parent {
protected String secret = "Protected Data"; // Now accessible in the subclass
}
class Child extends Parent {
void display() {
System.out.println("Accessing directly: " + secret);
}
}
3. Use Constructors to Initialize Private Members
A subclass can call the superclass constructor to initialize private fields:
class Parent {
private String secret;
public Parent(String secret) {
this.secret = secret;
}
public String getSecret() {
return secret;
}
}
class Child extends Parent {
public Child(String secret) {
super(secret);
}
void display() {
System.out.println("Accessing via superclass constructor: " + getSecret());
}
}