Master OOP in Java & C++ in Minutes

Master OOP in Java & C++ in Minutes

Preparing for a Java interview and have no time? You've come to the right place! This guide is designed to help you quickly revise the key concepts of Object-Oriented Programming (OOP) in Java and C++, perfect for those last-minute recap before stepping into the interview room. We won't dive deep into the theory; instead, we'll focus on the essential points you need to remember to confidently answer OOP questions. Let's get started!

Summary:

This guide serves as a quick revision tool for Java and C++ Object-Oriented Programming (OOP) concepts, focusing on key elements like classes, objects, access modifiers, constructors, shallow vs. deep copy, types of inheritance, polymorphism, abstraction, and important keywords. It also covers the SOLID principles, providing a comprehensive overview to help you confidently handle OOP interview questions.


Classes and Objects

  • Classes can be considered as group of entities and a blueprint of object.

  • Objects are entities in real world and instances of class.

  • In Java Class always start with a Capital letter

  • method names start with small letter -> camelCase

Java:

class ClassName{
// properties
//methods
}

public class Example{
  public static void main(String[] args){
     ClassName object = new ClassName();
  }
}
  • Java: Class names start with a capital letter, and method names use camelCase.

  • C++: Class names follow PascalCase or camelCase convention, and methods can use snake_case or camelCase.

C++:

class ClassName {
    // properties
    // methods
};

int main() {
    ClassName object;
    return 0;
}

Access Modifiers

access modifiers are used for abstraction. it limits the access of properties of a class.

once the property or methods are declared with access modifiers they can be accessed within:

Java Access Modifiers:

Access modifierwithin classwithin packageoutside package by subclassoutside package
privateyesnonono
defaultyesyesnono
protectedyesyesyesno
publicyesyesyesyes

C++ Access Modifiers:

ModifierWithin ClassDerived ClassOutside Class
privateYesNoNo
protectedYesYesNo
publicYesYesYes

Java:

class BankAccount{
   public String username;
   private String password;
   public void setPassword(String password){
     this.password = password;
   }
}

this keyword is used to to refer to the current object.

C++:

class BankAccount {
public:
    std::string username;
private:
    std::string password;
public:
    void setPassword(std::string password) {
        this->password = password;
    }
};   //semicolon used after class definition

Four pillars of OOP

1. Encapsulation:

"Wrapping up of data and methods under a single unit. also implements data hiding"

Constructor

"A special method of class which is invoked automatically at the time of object creation."

  • same name as class or structure

  • don't have any return type

  • only called once at object creation

memory allocation for class happens when constructor is called

types of constructor

  1. non parameterized constructor

    Java:

     public class Example{
       public Example(){
        // some operation
       }
     }
    

    Default Constructor (Non-Parameterized Constructor) in C++

     class Example {
     public:
         Example() {  // Default Constructor
             cout << "Default Constructor called!" << endl;
         }
     };
    
  2. parameterized constructor

    Java:

     public class Example{
       public Example(int parameter){
        // some operation
       }
     }
    

    C++:

     class Example {
         int value;
     public:
         Example(int val) {  // Parameterized Constructor
             value = val;
             cout << "Value initialized to: " << value << endl;
         }
     };
    

copy constructor

Java:

public class Example{
  public Example( Example e){
   // some operation
  }
}

C++:

#include <iostream>
using namespace std;

class Example {
    int value;
public:
    Example(int val) {  // Parameterized Constructor
        value = val;
    }

    Example(const Example &obj) {  // Copy Constructor
        value = obj.value;
        cout << "Copy Constructor called! Copied value: " << value << endl;
    }
};

Shallow Vs Deep copy

In shallow copy we copy reference of instance from one object to other. The changes in one object reflect in other. In below example if marks of Student s1 changes then marks of s2 also changes. because both s1 and s2 refer to single marks instance.

Java:

public class Student{
   public String name;
   public int[] marks;
   public Student(Student s){
      this.name=s.name;
      this.marks=s.marks;
   }

   public static void main(String[] args{
      Student s1 = new Student();
      s1.name="omkar";
      s1.marks=[80,90,87,94,97];
      Student s2= new Student(s1);
   }
}

C++:

#include <iostream>
using namespace std;
class Student {
public:
    string name;
    int* marks;

    Student(string n, int m) {
        name = n;
        marks = new int(m); // Dynamically allocated memory
    }
    // Shallow Copy Constructor
    Student(const Student &s) {
        name = s.name;
        marks = s.marks;  // Only the pointer is copied
    }
    void display() {
        cout << "Name: " << name << ", Marks: " << *marks << endl;
    }
};

int main() {
    Student s1("Omkar", 95);
    Student s2 = s1; // Shallow copy
    s1.display();
    s2.display();
    // Modifying s1's marks affects s2 as well (shared memory)
    *s1.marks = 100;
    cout << "After modifying s1's marks:" << endl;
    s1.display();
    s2.display(); // Notice that s2's marks also changed

    return 0;
}

In Deep copy we create totally different reference for new object and just copy content from one object to other. hence changes in one object doesn't reflect in other.

Java:

public class Student{
   public String name;
   public int[] marks;
   public Student(Student s){
      this.name=s.name;
      for(int i=0; i<s.marks.length; i++){
         this.marks[i]=s.marks[i];
      }
   }

   public static void main(String[] args{
      Student s1 = new Student();
      s1.name="omkar";
      s1.marks=[80,90,87,94,97];
      Student s2= new Student(s1);
   }
}

C++:

#include <iostream>
using namespace std;

class Student {
public:
    string name;
    int* marks;
    Student(string n, int m) {
        name = n;
        marks = new int(m); // Dynamically allocated memory
    }
    // Deep Copy Constructor
    Student(const Student &s) {
        name = s.name;
        marks = new int(*s.marks);  // Allocating new memory and copying value
    }    
    void display() {
        cout << "Name: " << name << ", Marks: " << *marks << endl;
    }

    ~Student() { // Destructor to free allocated memory
        delete marks;
    }
};

int main() {
    Student s1("Omkar", 95);
    Student s2 = s1; // Deep copy
    s1.display();
    s2.display();
    // Modifying s1's marks does NOT affect s2
    *s1.marks = 100; 
    cout << "After modifying s1's marks:" << endl;
    s1.display();
    s2.display(); // s2's marks remain unchanged

    return 0;
}

2. Inheritance:

"Properties and Methods of base class are passed on to a derived class"

there are four types of inheritance in java

  1. Single Inheritance: single base class is derived from single parent class.

    Java:

     class Parent {
         void show() {
             System.out.println("This is Parent class");
         }
     }
     class Child extends Parent {
         void display() {
             System.out.println("This is Child class");
         }
     }
     public class Main {
         public static void main(String[] args) {
             Child obj = new Child();
             obj.show();    // Inherited method
             obj.display();
         }
     }
    

    C++:

     #include <iostream>
     using namespace std;
     class Parent {
     public:
         void show() {
             cout << "Parent class method" << endl;
         }
     };
    
     class Child : public Parent {
     public:
         void display() {
             cout << "Child class method" << endl;
         }
     };
    
     int main() {
         Child obj;
         obj.show();
         obj.display();
         return 0;
     }
    
  2. Multilevel Inheritance: a base class is derived from another derived class.

    Java:

     class GrandParent {
         void grandParentMethod() {
             System.out.println("GrandParent Method");
         }
     }
     class Parent extends GrandParent {
         void parentMethod() {
             System.out.println("Parent Method");
         }
     }
     class Child extends Parent {
         void childMethod() {
             System.out.println("Child Method");
         }
     }
     public class Main {
         public static void main(String[] args) {
             Child obj = new Child();
             obj.grandParentMethod();
             obj.parentMethod();
             obj.childMethod();
         }
     }
    

    C++:

     #include <iostream>
     using namespace std;
     class GrandParent {
     public:
         void grandParentMethod() {
             cout << "GrandParent Method" << endl;
         }
     };
    
     class Parent : public GrandParent {
     public:
         void parentMethod() {
             cout << "Parent Method" << endl;
         }
     };
    
     class Child : public Parent {
     public:
         void childMethod() {
             cout << "Child Method" << endl;
         }
     };
    
     int main() {
         Child obj;
         obj.grandParentMethod();
         obj.parentMethod();
         obj.childMethod();
         return 0;
     }
    
  3. Hierarchical Inheritance: two base classes are derived from single parent class.

    Java:

     class Parent {
         void show() {
             System.out.println("Parent class method");
         }
     }
     class Child1 extends Parent {
         void display1() {
             System.out.println("Child1 method");
         }
     }
     class Child2 extends Parent {
         void display2() {
             System.out.println("Child2 method");
         }
     }
     public class Main {
         public static void main(String[] args) {
             Child1 obj1 = new Child1();
             obj1.show();
             obj1.display1();
    
             Child2 obj2 = new Child2();
             obj2.show();
             obj2.display2();
         }
     }
    

    C++:

     #include <iostream>
     using namespace std;
    
     class Parent {
     public:
         void show() {
             cout << "Parent class method" << endl;
         }
     };
    
     class Child1 : public Parent {
     public:
         void display1() {
             cout << "Child1 method" << endl;
         }
     };
    
     class Child2 : public Parent {
     public:
         void display2() {
             cout << "Child2 method" << endl;
         }
     };
    
     int main() {
         Child1 obj1;
         obj1.show();
         obj1.display1();
    
         Child2 obj2;
         obj2.show();
         obj2.display2();
    
         return 0;
     }
    
  4. Hybrid Inheritance: combination of above two or more types of inheritance. ex. two derived classed inherited from another derived class.

  5. Multiple Inheritance: in Java It is not directly implemented using extend keyword, but can be implemented using interface. it simply means implementing a subclass from two or more parent classes.

    Java:

     interface A {
         void methodA();
     }
     interface B {
         void methodB();
     }
     class C implements A, B {
         public void methodA() {
             System.out.println("Method A");
         }
         public void methodB() {
             System.out.println("Method B");
         }
     }
     public class Main {
         public static void main(String[] args) {
             C obj = new C();
             obj.methodA();
             obj.methodB();
         }
     }
    

    Multiple inheritance is directly supported in C++.

     #include <iostream>
     using namespace std;
    
     class A {
     public:
         void methodA() {
             cout << "Method A" << endl;
         }
     };
    
     class B {
     public:
         void methodB() {
             cout << "Method B" << endl;
         }
     };
    
     class C : public A, public B {
     public:
         void methodC() {
             cout << "Method C" << endl;
         }
     };
    
     int main() {
         C obj;
         obj.methodA();
         obj.methodB();
         obj.methodC();
         return 0;
     }
    

Ways of Inheritance Using Access Modifiers in C++

In C++, inheritance can be of three types based on access modifiers:

Each type determines how the base class members are accessible in the derived class.

1. Public Inheritance

class Base {
public:
    int x = 10;
protected:
    int y = 20;
private:
    int z = 30;
};

class Derived : public Base {
    // x remains public
    // y remains protected
    // z is inaccessible, not inherited
};

2. Protected Inheritance

class Derived : protected Base {
    // x becomes protected
    // y remains protected
    // z is inaccessible
};

3. Private Inheritance

class Derived : private Base {
    // x becomes private
    // y becomes private
    // z is inaccessible
};

3. Polymorphism:

"Polymorphism means different behavior of methods in different situation. we can use single function for different operations"

Polymorphism TypeConceptJavaC++
Compile-time PolymorphismMethod Overloading / Operator Overloading✅ Supported (Method Overloading) ❌ (No Operator Overloading)✅ Supported (Method & Operator Overloading)
Run-time PolymorphismMethod Overriding✅ Supported (via Inheritance & @Override)✅ Supported (via Virtual Functions)

Method Overloading (Java & C++)

"function with same name but different parameters"

  • functions differentiated based on the number or type of parameters

  • they can not be differentiated based on return type of function

Java:

class Calculator{
  public int sum(int a, int b){
       return a+b;
  }
  public float sum(float a, float b){
       return a+b;
  }
  public int sum(int a, int b, int c){
       return a+b+c;
  }
}

C++:

#include <iostream>
using namespace std;

class Calculator {
public:
    int sum(int a, int b) {
        return a + b;
    }
    float sum(float a, float b) {
        return a + b;
    }
    int sum(int a, int b, int c) {
        return a + b + c;
    }
};

int main() {
    Calculator obj;
    cout << obj.sum(3, 4) << endl;
    cout << obj.sum(2.5f, 3.5f) << endl;
    cout << obj.sum(1, 2, 3) << endl;
}

Operator Overloading (Only in C++)

C++ allows operators like +, -, *, etc., to be overloaded.

#include <iostream>
using namespace std;

class Complex {
public:
    int real, imag;
    Complex(int r, int i) : real(r), imag(i) {}

    // Overloading + operator
    Complex operator + (const Complex& obj) {
        return Complex(real + obj.real, imag + obj.imag);
    }

    void display() {
        cout << real << " + " << imag << "i" << endl;
    }
};

int main() {
    Complex c1(3, 2), c2(1, 7);
    Complex c3 = c1 + c2; // Calls overloaded operator
    c3.display();
}

Method Overriding (Java & C++)

"parent and child classes have same function name and parameters but with different definitions"

Java:

class Animal {
    public void eat() {
        System.out.println("Eats something..");
    }
}
class Deer extends Animal {
    @Override
    public void eat() {
        System.out.println("Eats grass..");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Deer(); // Dynamic method dispatch
        a.eat(); // Calls Deer’s eat()
    }
}

C++ Example (Without Virtual Function)

#include <iostream>
using namespace std;

class Animal {
public:
    void eat() {
        cout << "Eats something.." << endl;
    }
};

class Deer : public Animal {
public:
    void eat() {
        cout << "Eats grass.." << endl;
    }
};

int main() {
    Animal* a = new Deer();
    a->eat(); // Calls Animal’s eat() (wrong behavior without virtual function)
}

Since eat() is not virtual, Animal's method is called.

Virtual Functions (C++ Only)

In C++, we use virtual functions for proper run-time polymorphism.

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void eat() { // Virtual function
        cout << "Eats something.." << endl;
    }
};

class Deer : public Animal {
public:
    void eat() override { // Overriding
        cout << "Eats grass.." << endl;
    }
};

int main() {
    Animal* a = new Deer();
    a->eat(); // Calls Deer’s eat() (Correct behavior)
}

Dynamic Method Dispatch (Run-time Polymorphism)

  • The reference type is Animal, but the actual object created is of Deer.

  • This allows calling overridden methods at run-time, not compile-time.

public class Main {
    public static void main(String[] args) {
        Animal a = new Deer(); // Polymorphism
        a.eat(); // Output: Deer eats grass.
    }
}

The overridden method in Deer is called because Java uses dynamic method dispatch.

You can do this:

Deer a = new Deer();

But it won’t allow polymorphism, meaning you can't assign a different subclass later.

For example:

Animal a = new Deer();
a = new Dog(); // Allowed (if Dog extends Animal)

But this is not allowed:

Deer a = new Deer();
a = new Dog(); // ❌ Compilation Error! Dog is not a Deer.

4. Abstraction:

"Abstraction is the concept of hiding unnecessary details and exposing only the essential features to the user."

Java achieves abstraction using abstract classes and interfaces. while C++ achieves abstraction using abstract classes with pure virtual functions.

Abstract Class in java

  • we can not create instance of abstract class

  • only can be used after creating subclass

  • abstract class can have abstract methods(methods without definition) and non-abstract methods

  • they can have constructor which is automatically invoked when instance of child is created

definition of abstract method can only and must be written in child class

abstract class Animal {
    public void eat() { 
        System.out.println("Animal is eating..."); 
    }
    abstract void walk(); // No body (only declared)
}

class Horse extends Animal {
    @Override
    void walk() { 
        System.out.println("Horse walks on 4 legs"); 
    }
}

Interfaces in Java

"Interface are complete abstract class, they can be considered as blueprint of class."

  • All methods are public, abstract and without definition

  • All attributes are final, public and static

interface ChessPlayer {
    void moves(); // Abstract by default
}

class Queen implements ChessPlayer {
    @Override
    public void moves() { 
        System.out.println("Moves in all 8 directions"); 
    }
}

Abstract Class in C++ (Using Pure Virtual Functions)

  • A class with at least one pure virtual function is an abstract class.

  • Declared with = 0 syntax.

  • Cannot be instantiated.

  • Derived class must override the pure virtual function.

#include <iostream>
using namespace std;

class Animal {
public:
    void eat() { 
        cout << "Animal is eating..." << endl; 
    }
    virtual void walk() = 0; // Pure virtual function
};

class Horse : public Animal {
public:
    void walk() override { 
        cout << "Horse walks on 4 legs" << endl; 
    }
};

int main() {
    Animal* a = new Horse();
    a->eat();  // Calls the concrete method
    a->walk(); // Calls the overridden method
    delete a;
    return 0;
}

If you want complete abstraction, use:

  • Java: interface

  • C++: Abstract class with only pure virtual functions.


IMP Keywords in Java and C++

static:

"used to share the same variable or method of a given class"

  • static method or properties belongs to class and not to its instance.

  • static can be property, function, block or a nested class.

Java:

class Example {
    static int count = 0;
    Example() {
        count++;
    }
    static void showCount() {
        System.out.println("Count: " + count);
    }
}

public class Main {
    public static void main(String[] args) {
        new Example();
        new Example();
        Example.showCount(); // Output: Count: 2
    }
}

C++:

#include <iostream>
using namespace std;

class Example {
public:
    static int count;
    Example() { count++; }
    static void showCount() { cout << "Count: " << count << endl; }
};

int Example::count = 0; // Static variable definition

int main() {
    Example e1, e2;
    Example::showCount(); // Output: Count: 2
    return 0;
}

super in Java OR (: :) in C++

"used to refer immediate parent class object. It is used to access parent's properties, function and constructor."

Java:

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

class Child extends Parent {
    void display() {
        super.display(); // Calls Parent's display method
        System.out.println("Child display");
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        c.display();
    }
}

C++:

  • Similar to super in Java.

  • scope resolution operator (::) is used to call base class methods.

#include <iostream>
using namespace std;

class Parent {
public:
    void display() { cout << "Parent display" << endl; }
};

class Child : public Parent {
public:
    void display() {
        Parent::display(); // Calls Parent's display method
        cout << "Child display" << endl;
    }
};

int main() {
    Child c;
    c.display();
    return 0;
}

final in Java OR const in C++:

Java:

  • final variable: Cannot change value.

  • final method: Cannot be overridden.

  • final class: Cannot be extended.

final class Parent {
    final void show() {
        System.out.println("Final method cannot be overridden");
    }
}

// class Child extends Parent {} // ERROR: Cannot extend final class

public class Main {
    public static void main(String[] args) {
        final int x = 10;
        // x = 20; // ERROR: Cannot change final variable
    }
}

C++:

  • const variables cannot be changed.

  • const functions cannot modify class members.

  • Declare class with virtual functions to prevent further overriding.

#include <iostream>
using namespace std;

class Parent {
public:
    virtual void show() const { // Const function
        cout << "Final method equivalent" << endl;
    }
};

int main() {
    const int x = 10;
    // x = 20; // ERROR: Cannot change const variable
    return 0;
}

Constructor Chaining

Constructor Chaining in Java

"Constructor chaining is a process of calling a constructor from another constructor within same class or between the superclass and a subclass."

Within the same class: Using this()

public class Student {
    private String name;
    private int rollNo;

    // Constructor 1
    public Student() {
        this("Omkar", 324031); // Calls Constructor 2
    }

    // Constructor 2
    public Student(String name, int rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }

    public void display() {
        System.out.println("Name: " + name + ", Roll No: " + rollNo);
    }

    public static void main(String[] args) {
        Student s = new Student();
        s.display();  // Output: Name: Omkar, Roll No: 324031
    }
}

Between a superclass and subclass: Using super()

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

public class Child extends Parent{
   public Child(){
     super();  //calling parent constructor in child constructor
   }
}

Constructor Chaining in C++

  • Within the same class: Using delegating constructors (C++11 and later).
#include <iostream>
using namespace std;

class Student {
    string name;
    int rollNo;
public:
    // Constructor 1
    Student() : Student("Omkar", 324031) {}  // Calls Constructor 2

    // Constructor 2
    Student(string name, int rollNo) {
        this->name = name;
        this->rollNo = rollNo;
    }

    void display() {
        cout << "Name: " << name << ", Roll No: " << rollNo << endl;
    }
};

int main() {
    Student s;
    s.display(); // Output: Name: Omkar, Roll No: 324031
    return 0;
}
  • Between a superclass and subclass: Using explicit constructor calls in the derived class.
#include <iostream>
using namespace std;

class Parent {
public:
    Parent() {
        cout << "Parent constructor" << endl;
    }
};

class Child : public Parent {
public:
    Child() : Parent() {  // Calls Parent constructor
        cout << "Child constructor" << endl;
    }
};

int main() {
    Child c;
    return 0;
}

SOLID Principle

The SOLID principles are a set of five design principles in Object-Oriented Programming (OOP).

  • S= Single Responsibility Principle (SRP): A class should have only one job or responsibility. Each class focus on a single functionality and it is encapsulated.

  • O= Open Closed Principle (OCP): Software entities (like classes, modules, functions, etc.) should be open for extension but closed for modification. Relate it with polymorphism and abstraction.

  • L= Liskov Substitution Principle (LSP): Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program. relate with inheritance.

  • Interface Segregation Principle (ISP): Clients should not be forced to depend on interfaces they do not use. This principle advocates for creating smaller, more specific interfaces instead of a single, large, general-purpose interface.

  • Dependency Inversion Principle (DIP): High-level modules should not depend on low-level modules. Both should depend on abstractions.

Mastering the core concepts of Object-Oriented Programming in any language is crucial for writing clean and maintainable code. With these fundamentals in mind, you'll be well-prepared to tackle any OOP-related interview questions and excel as a developer. Happy coding!


Important concepts asked in Interviews

Difference Between Abstract Class and Interface

Abstract Class

  • Can have both abstract and non-abstract methods.

  • Can have constructors and instance variables.

  • Used when classes share a common structure but need to implement their own behaviors.

Interface

  • Only abstract methods (before Java 8).

  • All variables are public, static, and final.

  • Used for achieving 100% abstraction and multiple inheritance.

Method Hiding vs. Overriding

  • Method Hiding: Static methods are hidden, not overridden. Method resolution happens at compile-time.

  • Overriding: Non-static methods are overridden, resolved at runtime via dynamic binding.

class Parent {
    static void show() {
        System.out.println("Static method in Parent");
    }
}

class Child extends Parent {
    static void show() {
        System.out.println("Static method in Child");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.show(); // Static method in Parent (Method Hiding)
    }
}

📌 Explanation: Even though the reference is of Child, the method in Parent is called because static methods belong to the class, not the object.

class Parent {
    void show() {
        System.out.println("Non-static method in Parent");
    }
}

class Child extends Parent {
    void show() {
        System.out.println("Non-static method in Child");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.show(); // Non-static method in Child (Overriding)
    }
}

📌 Explanation: The method in Child is called because non-static methods are resolved at runtime using dynamic binding.

Covariant Return Type

  • In method overriding, the return type in the child class can be a subtype of the parent class's return type.
class Parent {
    Parent getInstance() {
        return new Parent();
    }
}

class Child extends Parent {
    @Override
    Child getInstance() { // Covariant return type (subtype of Parent)
        return new Child();
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        System.out.println(c.getInstance().getClass().getName()); // Child
    }
}

📌 Explanation: Instead of returning Parent, the overridden method in Child returns a Child object.

Garbage Collection & Finalization

  • Java has automatic memory management.

  • Unused objects are removed by the Garbage Collector (GC).

  • We can request GC using System.gc(), but it's not guaranteed to run immediately

class Test {
    @Override
    protected void finalize() {
        System.out.println("Object is garbage collected.");
    }
}

public class Main {
    public static void main(String[] args) {
        Test t1 = new Test();
        Test t2 = new Test();

        t1 = null; // Eligible for GC
        t2 = null; // Eligible for GC

        System.gc(); // Request garbage collection
    }
}

📌 Explanation: The finalize() method runs before an object is collected by the GC.

Why Do We Use @Override?

The @Override annotation is used to ensure that a method is actually overriding a method from its superclass.

  • finalize() is a method inherited from Object (since every class in Java extends Object).

  • Using @Override ensures that finalize() in Test is correctly overriding the finalize() method from Object.

What Happens Without @Override?

class Test {
    protected void finalizee() { // Typo in method name
        System.out.println("Object is garbage collected.");
    }
}
  • Java will not recognize this as an overridden method (since finalizee() does not exist in Object).

  • The intended behavior of finalize() won't be executed before garbage collection.

  • If you had used @Override, the compiler would catch this mistake.

Diamond Problem in C++. How it is solved in Java and C++

The Diamond Problem occurs in multiple inheritance, where a class inherits from two classes that have a common base class. This leads to ambiguity about which method to inherit if both parent classes define the same method.

Example of Diamond Problem in C++ (Where It Actually Occurs):

class A {
public:
    void show() { cout << "Class A" << endl; }
};
class B : public A {};
class C : public A {};
class D : public B, public C {}; // Diamond Problem

int main() {
    D obj;
    obj.show(); // Error: Ambiguity (show() exists in both B and C)
}

Here, D inherits from both B and C, and both B and C inherit from A. When calling show(), the compiler does not know whether to use B::show() or C::show().

How C++ Solves the Diamond Problem?

This is solved using virtual base classes.

class A {
public:
    int x;
};

class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {}; // No ambiguity in accessing x

How Java Solves the Diamond Problem?

Java prevents the diamond problem by not allowing multiple inheritance with classes, but it allows it using interfaces with default methods.

Example in Java Using Interfaces

interface A {
    default void show() { System.out.println("Class A"); }
}
interface B extends A {}
interface C extends A {}
class D implements B, C {
    public static void main(String[] args) {
        D obj = new D();
        obj.show(); // No ambiguity, calls A's show()
    }
}

Here, even though D implements both B and C, which extend A, there is no ambiguity because Java inherits the method from A only once.

If Interfaces Have Conflicting Methods?

If both B and C override show(), the subclass D must explicitly specify which method to use:

class D implements B, C {
    public void show() { 
        B.super.show(); // Resolving conflict explicitly
    }
}

Friend Function in C++

A friend function is a function that is not a member of a class but has access to its private and protected members. It is declared inside the class with the keyword friend.

Why Use Friend Functions?

  • To perform non-member operations that still require access to a class's internal data.

  • When two or more classes need to access each other's private data.

  • To overload operators that require access to private members.

Friend Function Accessing Private Members:

#include <iostream>
using namespace std;

class Box {
private:
    int width;
public:
    Box(int w) { width = w; }   //constructor
    // Friend function declaration
    friend void printWidth(Box obj);
};

// Friend function definition
void printWidth(Box obj) {
    cout << "Width: " << obj.width << endl;  // Accessing private member
}

int main() {
    Box b(10);
    printWidth(b);  // Calling friend function
}

Friend Function with Two Classes:

class ClassB;  // Forward declaration

class ClassA {
private:
    int numA;
public:
    ClassA(int a) { numA = a; }
    friend void show(ClassA, ClassB); // Friend function
};

class ClassB {
private:
    int numB;
public:
    ClassB(int b) { numB = b; }
    friend void show(ClassA, ClassB); // Friend function
};

// Friend function definition
void show(ClassA a, ClassB b) {
    cout << "ClassA: " << a.numA << ", ClassB: " << b.numB << endl;
}

int main() {
    ClassA obj1(10);
    ClassB obj2(20);
    show(obj1, obj2); // Calling friend function
}

Friend Function for Operator Overloading:

class Complex {
private:
    int real, imag;
public:
    Complex(int r, int i) { real = r; imag = i; }

    // Friend function for addition operator overloading
    friend Complex operator+(Complex, Complex);

    void display() { cout << real << " + " << imag << "i" << endl; }
};

// Friend function definition
Complex operator+(Complex c1, Complex c2) {
    return Complex(c1.real + c2.real, c1.imag + c2.imag);
}

int main() {
    Complex c1(3, 4), c2(1, 2);
    Complex sum = c1 + c2;
    sum.display();
}