What Are Access Modifiers?

Access modifiers are keywords in Java that define the visibility and accessibility of classes, methods, and fields. They help enforce encapsulation and data hiding, ensuring that only certain parts of your program can access specific members. The four primary access modifiers are:

1. public

  • Visibility: Any class, method, or field declared as public can be accessed from anywhere in the program, including outside the class or package.
  • When to use: Use public when you want a class or method to be globally accessible.

2. private

  • Visibility: Members declared as private can only be accessed within the same class. They cannot be accessed from outside the class.
  • When to use: Use private for encapsulation to hide implementation details, allowing access only through getter/setter methods.

3. protected

  • Visibility: Members declared as protected are accessible within the same package and by subclasses, even if they are in a different package.
  • When to use: Use protected when you want a member to be accessible by derived classes but still restrict access to non-related classes.

4. Default (No Modifier)

  • Visibility: If no access modifier is specified, it means the member has package-private access. It can only be accessed by classes within the same package.
  • When to use: Use when the member should be accessible only within the same package and not by subclasses outside the package.

Why Do We Need Access Modifiers?

Access modifiers are essential to implement encapsulation and data security:

  • Data Protection: Prevent unauthorized access to sensitive data and functionality.
  • Modular Design: Help in organizing code, making classes and methods more reusable and less error-prone.
  • Code Readability: Clearly defines what parts of the class are intended to be used externally (public) and which are meant for internal use only (private).

Inheritance in Java

Inheritance is a fundamental concept in Object-Oriented Programming (OOP) that allows one class to acquire properties and behaviors (fields and methods) of another class. The class that is inherited from is called the superclass (or parent), and the class that inherits is called the subclass (or child).

  • Syntax: class Child extends Parent

Why Use Inheritance?

  • Code Reusability: A subclass can reuse the code from the superclass, avoiding duplication.
  • Method Overriding: A subclass can override superclass methods to provide specialized behavior.
  • Is-A Relationship: The subclass is a specialized version of the superclass (e.g., a Dog is an Animal).

When to Use Inheritance

  • To model hierarchical relationships where one class is a more specific type of another (e.g., Car inherits from Vehicle).
  • When you need to extend the behavior of a class by adding or modifying methods.

How Inheritance Works

class A {
    void display() {
        System.out.println("Class A display");
    }
}

class B extends A {
    void display() {
        System.out.println("Class B display");
    }
}

public class Test {
    public static void main(String[] args) {
        B obj = new B();
        obj.display();  // Prints "Class B display"
    }
}
  • Class B inherits the display() method from class A but can override it to provide its own implementation.

Issues with Constructor Chaining and Default Constructors in Inheritance

Inheritance with Constructors

If the parent class has a parameterized constructor, the child class must call super(...) to invoke it. However, if both classes have default constructors, you should avoid calling super() without arguments in the child class, as it would fail to invoke the parent constructor properly if it's not available.

Example of Constructor Issue:

Let’s assume:

  • Class A has a constructor that takes an argument.
  • Class B inherits A and has a default constructor.
class A {
    A(int num) {
        System.out.println("Parent class constructor: " + num);
    }
}

class B extends A {
    B() {
        super(10);  // Explicit call to parent constructor with argument
        System.out.println("Child class constructor");
    }
}

public class Test {
    public static void main(String[] args) {
        B obj = new B();
    }
}
  • Correct: The child class explicitly calls super(10) in its constructor to invoke the parent constructor with arguments.

If both the parent and child classes have parameterized constructors but you don't call super(...) properly, you will get a compilation error.

What Happens if Both Parent and Child Have Default Constructors?

If the parent class has a default constructor and the child class doesn’t explicitly call it, Java will automatically invoke the parent class’s default constructor. However, if the parent class only has a parameterized constructor, the child class must call it explicitly using super(...).

  • Parent Class with Default Constructor:
class A {
      A() { System.out.println("Parent class default constructor"); }
  }
  • Child Class with Default Constructor:
class B extends A {
      B() {
          super();  // This is optional if A has a default constructor.
          System.out.println("Child class constructor");
      }
  }

Important Points

  1. Inheritance lets you reuse code from a superclass. You can override or extend methods in the subclass.
  2. Access modifiers help in controlling visibility to maintain encapsulation and security.
  3. Constructor chaining must be carefully used, especially in inheritance, ensuring that the superclass constructor is properly invoked using super(...).

Day 15 Key Takeaways:

  • Access Modifiers help manage access control to class members. Use public, private, protected, and default wisely.
  • Inheritance allows code reusability and extension of behaviors. You should use it to model relationships where one class is a specialized version of another.
  • Constructor issues in inheritance arise if default constructors are not properly handled, and a super(...) call is mandatory when the parent class constructor has parameters.