OOP

"Objects encapsulate state and behaviour."

  • Imperative paradigm
  • Objects are first-class citizens.

Components

Classes

  • A class is a set of objects that have the same structure and exhibit the same behaviour.
  • A class provides a blueprint for all objects based on this class.
  • A class is made up of components, such as attributes, methods, events, constants, types, and implemented interfaces.

Friend Class

  • Can access private and protected member of a class.
  • Class specifies its friends, not the other way round.
  • Friendships are not inherited — Friends of parents are not friends of children.

Inner Class

Class inside a class.

Accessibility

Both inner and outer classes can access each other's members, regardless of the #Access Specifiers.

Interface

Completely abstracted blueprint of an object with no behaviour details whatsoever.

Members

Members of class are entities which are part of the class. These can be categorised on basis of ownership and on basis of their type.

Categorisation 1: Based on ownership
  • instance: instance members belong to the object
  • static: static members belong to the class
Categorisation 2: Based on type
  • data: data members are -
    • variables
    • constants
  • method: member methods are methods specified in a class

You can pair any of the categories in categorisation 1 with any of the categories in categorisation 2 to refer to a member of a class more specifically. For example, instance data member, static method, instance member variable etc.

Objects

An object is an instance of a class.

Covariant Return Types

When variance of return types is covariant.

Two examples where this can work -

  • Suppose there are two classes A and B. A has a function with return type A and B has the function with the same name and arguments but with return type B. The function in B can override the one in A in this situation.
  • Suppose there are four classes A, B, C and D. A is inherited by C and B is inherited by D. Now C has a function with return type A and D has a method with same name and arguments with return type B. Method overriding works here.

Basically, the parent return type is being replaced by child return type in the overriding method.

Contexts

Contexts can be thought of as memory areas. Its basically the different contexts we can operate in. A variable can exist as part of an object (member variable) or the class (static), a member can be part of both child and parent class. There are different names to refer to these "contexts" and keywords defined by languages to identify which context we are talking about.

Object Context

Object related - can't be used in static areas. (1)

Some keywords which are meant specifically for objects can't be used in static areas, for example, the main method.

Object Context Qualifiers

A note on Object Context Qualifiers You'll find this term being used at some places in these notes. The term "Object Context Qualifiers" is not used anywhere generally and is not a "real" Computer Science term, AFAIK. Its a term I use for my understanding to refer to keywords which are used to discern or "qualify" the context of a member in an object, that is, to identify if a member being referred to is a member of the current class of the object, or its superclass.

Reasons why it makes sense:

  1. Qualifiers do refer to lexical tokens which are used to remove ambiguity while identifying entities. So that's where this comes from.
  2. Another reason is Java's 'qualified this' construct.
  3. These keywords are used in object contexts, not static ones.
Self-reference

Used to access members of current class.

Super

super - used to access members of parent class.

Modifiers

Abstract

  • Not implemented
  • No functionality/behaviour
Abstract Class
  • Defines blueprints for its child classes.
  • May or may not include abstract methods
  • It can't be initialised.

Final

Final method

Can't be overridden

Final class
  • Can't be inherited
  • Are all methods final in a final class? Class can't be inherited — how will you override? #interview-question
  • If class is final and abstract — it should have static components only #interview-question Doesn't make sense to have instance components if it can't be instantiated or inherited.

Default

Default modifier, used for methods, helps define default implementations to certain methods within the interface itself.

The primary purpose for this feature is to allow adding certain default behaviour to future versions of an interface without breaking the existing functionality and supporting backward compatibility.

This is not present in all OO languages though.

public interface MyInterface {
	void existingMethod(); // Abstract method
	
	// Default method with implementation
	default void newDefaultMethod() {
		System.out.println("This is a default method.");
	}
}

Access Specifiers

Public
  • Any program/ function outside the class can access.
  • Generally given to methods
Protected
  • Visible within class and its subclasses
Private
  • Only methods inside class can access.
  • Generally given to attributes
Access specifiers applied to classes

These are the usual patterns followed by Object oriented languages.

  • Public classes: Top-level class in a file are public classes.
  • Protected and private classes: These are not allowed to top-level classes. Inside another class, they behave the same as any other member.

Static

Ownership: Static members and methods are owned by the class itself, not any object. Also called class members / class methods.

  • Accessing

    • You don't need to create an instance or object to access static members. You can access them through class.

    • Access using objects: Its possible to write f.classMethod(). It is legal but a bad idea, since it causes confusion. The instance is not important in case of class methods. Only the declared type of 'f' matters.

      Its better to write Foo.classMethod() or Bar.classMethod(). This not only makes it clear which class method you would like to call. But also, that it is indeed a class method which you're calling.

      Of course, all this could be avoided by simply trying to not overide your static methods. After all, #Static Methods can't be overridden. ^840b67

  • Initialization in program memory Static members are loaded into program memory along with the class. Even before initializing any objects.

Static Methods
Static Methods can't be overridden
//Can't do this -> Compile time error
class Foo {
	public static void method() {
		System.out.println("in Foo");
	}
}

class Bar extends Foo {
	public void method() {
		System.out.println("in Bar");
	}
}

Then why compiler sometimes talks about overriding static methods? -> It means hiding.

Sometimes you will see error messages from the compiler that talk about overriding static methods. Apparently, whoever writes these particular messages has not read the Java Language Specification and does not know the difference between overriding and hiding. So they use incorrect and misleading terminology. Just ignore it. The Java Language Specification is very clear about the difference between overriding and hiding, even if the compiler messages are not. Just pretend that the compiler said "hide" rather than "override". (2)

Static Methods can't access instance (non-static) variables
class A{
	int x;
	
	public static void func(){
		x++; //Error
	}
}

When you write this, the method doesn't know which 'x' to call. In case of a non-static method, it is understood that its this.x - it is implicit. But in static context, there is no 'this'.

However, if you tell it the object, it can work. But again, this object will be static.

class A{
	int x;
	static A a = new A(); //static object
	
	public static void func(){
		a.x++; //Works fine
	}
}

Pillars

4 Pillars (encapsulation, inheritance, polymorphism, abstraction) of OOPS. +1 secret basement (association)

Encapsulation

Wrapping up the data in a single unit.

Implemented by

#Classes and #Access Specifiers.

Inheritance

Mechanism through which an object acquires all the properties and methods of its parent

What kind of relationship?

IS - A

What is inherited?

💡 By inherited, it is meant that these fields and methods are part of of the subclass, as if the subclass had declared them itself.

When a subclass inherits a superclass, all protected and public fields and methods of the superclass are inherited by the subclass. Protected and public fields can be called and referenced just like the methods declared directly in the subclass.

Private fields and methods of the superclass can never be referenced directly by subclasses. They can, however, be referenced indirectly via methods reachable from the subclass (e.g default (package), protected and public methods).

Types of Inheritance

Single Inheritance
Multilevel Inheritance
Multiple Inheritance
Multiple inheritance not allowed
  • Multiple inheritance is not allowed in some languages
  • However, multiple interfaces can be implemented

Why is Multiple inheritance not allowed?

  • To reduce complexity and simplify the language
  • Diamond Problem (3): Results in ambiguity
  • Creates problem during various operations like casting, constructor chaining
  • Very few scenarios in which multiple inheritance is required

Why is multiple inheritance implementation allowed in interfaces? => No ambiguity - implementation is not given by interfaces

Hybrid Inheritance
Hierarchical Inheritance

As the name suggests.

Purpose of Inheritance

Disadvantages of Inheritance

(4)

  1. You can't change the implementation provided by superclass at runtime. Inheritance is defined at compile time.
  2. Inheritance is said to "break encapsulation" by exposing a subclass to protected members of parent class.
  3. Leads to child class being tightly coupled with parent class. Changes in parent class will lead to changes in subclass.
  4. Excessive deep inheritance trees can make inheritance stack very deep and confusing.

Polymorphism

What is it?

  • Many forms
  • Methods with same name can be used for different purposes

Implemented By

Overloading

When a token can be used for multiple purposes. Typically used in context of methods i.e. method overloading.

Method overloading

When multiple methods have same signature and are called according to the arguments passed.

How can it be achieved?

  • Using different data types of parameters
  • Using variable count of parameters
Overriding

When a child class has same method as that of a parent class, that method is said to override the parent's method.

How can it be achieved?
  • Method must have same name as parent
  • Same parameter as parent - Parameters are invariant.
    • Why? If parameters are allowed to be varied, it will violate Liskov Substitution Principle.
    • Let's say we allow this:
        
        class Animal {}
        class Dog extends Animal {}
        
        class AnimalHandler {
            void handle(Animal animal) {}
        }
        
        class DogHandler extends AnimalHandler {
        
        @Override
        void handle(Dog dog) {} // Not allowed!
        
        }
      
      Then we can't do this (which ideally should be allowed in any good OO design)
      AnimalHandler handler = new DogHandler(); // Polymorphism
      handler.handle(new Animal()); // Compiler expects this to work, but DogHandler cannot handle a generic Animal
      
  • Return type must be same or have an IS-A relationship with parent method's return type #Covariant Return Types.
  • Overridden method should not be more restrictive than parent method.
Exception Declaration with Method Overriding

If a language supports exception declaration, this is how it likely works with method overriding.

Case 1: Super class doesn't declare an exception

Sub class overridden method:

Case 2: Super class declares an exception

Sub class overridden method:

  • can declare
    • same exception
    • subclass exception (variance = covariant)
    • or no exception
  • but cannot declare parent exception

Abstractions

Process of hiding implementation details and showing only functionality to user.

Implemented by

#Abstract components like #Interfaces and #Classes.

How do interface and Abstract class achieve abstraction?

If you're returning an object through an API, you simply pass them an object of the interface, not the class.

The class(implementation) is useless information, the interface is all the consumer will need.

Typically, the approach is that you only show the user the interface of the object you're passing back to them, instead of the precise class that implements that interface. The interface is the essential implementation, the exact class is unnecessary information. (5)

Leaky Abstractions

When consumers have to know the internals in order to consume a system properly.

Association

Relation between two classes established through their objects.

Aggregation

  • Special case of #Association.
  • When a class has another class' object as a data member.
  • Unidirectional association
  • Both entities can survive individually
  • What kind of relationship?
    • HAS - A
    • Whole-part relationship
Composition
  • Special Case of #Aggregation.
  • When one class is highly dependent on the other. For example, Library and books.
  • Strong Existence Relation — Composition means that the contained object cannot exist without the aggregation (for example, a car reservation cannot exist without the car rental agency).
  • Lifetime of individual parts = Lifetime of aggregate
  • What kind of relationship?
    • PART - OF
    • Existence-dependent whole-part relationship
Benefits of Composition
Disadvantages of Composition

(4)

  1. You can't change the implementation provided by superclass at runtime. Inheritance is defined at compile time.
  2. Inheritance is said to "break encapsulation" by exposing a subclass to protected members of parent class
  3. Leads to child class being tightly coupled with parent class. Changes in parent class will lead to changes in subclass.
  4. Excessive deep inheritance trees can make inheritance stack very deep and confusing.

Features

Binding

Static Binding

When the type of object is determined at compile time

Example:

Student s = new Student();

Dynamic Binding

When the type of object is determined at runtime.

Happens when object of child is assigned to reference of parent.

Example:

Human h = new Student()
//Student IS-A Human
foo(Human h) //method definition

//method call
foo(Student s)

Reference of Human type ————> Object of Student type
(Reference Variable) ----------------> (Object)

Argument – Human h = new Student() is not dynamic binding

#interview-question

Someone could very easily make an argument that Human h = new Student() is not Dynamic Binding

class Human{
	foo(){}
}

class Student extends Human{
	foo(){}
	xyz(){}
}

main(){
	Human h = new Student();
	h.xyz(); //This line will give error.
	h.foo();
}

Why it looks like its not runtime polymorphism? The line h.xyz() will immediately show an error.

  • h.xyz() shows error
  • ⇒ Program doesn't know h is an object of class Student
  • ⇒ h is an object of Human type
  • ⇒ Its not runtime polymorphism

What actually happens Compiler doesn't know at compile time that 'h' is an object of Student type.

  • Object type determined at runtime
  • ⇒ Compiler still thinks 'h' is an object of Human type
  • ⇒ COMPILE TIME Error.

Even though we can't call Student-exclusive methods from this reference without casting, the type of object or the version of method to be called is still determined at runtime. Which still makes it a case of dynamic binding and hence, #Runtime Polymorphism.

Runtime Polymorphism

aka Dynamic Method Dispatch (6)

Method #Overriding + #Dynamic Binding = Runtime Polymorphism

Method Overriding - Which Method to call depends on object type + Dynamic Binding - Object type determined at runtime

=> Which method to call - decided at runtime -> Runtime Polymorphism

Object being referred to determines the version of method to be called

The object being referred to (not the reference variable) determines the version of method to be called.

So in this example, if Student has a method overriding a method in Human. Student's method will be called because even though h is an object of Human, it is referring to an object of Student.

Human h = new Student();
h.overriddenMethod();

Shadowing/hiding

  • Overriding => In case of non-static methods

    class A{
    	public void show(){
    		System.out.println("in A");
    	}
    }
    
    class B extends A{
    	public void show(){
    		System.out.println("in B");
    	}
    }
    
  • Hiding => In case of all other members (instance members, static members, static methods)

    class A{
    	int a;
    	static int b;
    	static meth();
    }
    
    class B extends A{
    	int a;
    	static int b;
    	static meth();
    }
    //class B now has both properties.
    //'a' from its own class
    

Major Difference between Method #Overriding and Hiding

This

class Foo {
	public void method() {
		System.out.println("in Foo");
	}
}

class Bar extends Foo {
	public void method() {
		System.out.println("in Bar");
	}
}

is not the same as this

class Foo {
	public static void method() {
		System.out.println("in Foo");
	}
}

class Bar extends Foo {
	public static void method() {
		System.out.println("in Bar");
	}
}

Both the codes compile and run fine.

But the second one isn't an example of one static method z another static method. Its an example of a static method hiding another static method.

What's the difference? #Runtime Polymorphism - When you override, you get it. When you hide, you don't.

class Foo {
	public static void classMethod() {
		System.out.println("classMethod() in Foo");
	}
	
	public void instanceMethod() {
		System.out.println("instanceMethod() in Foo");
	}
}

class Bar extends Foo {
	public static void classMethod() {
		System.out.println("classMethod() in Bar");
	}
	
	public void instanceMethod() {
		System.out.println("instanceMethod() in Bar");
	}
}

public class Test {
	public static void main(String[] args) {
		
		Foo f = new Bar();
		f.instanceMethod();
		f.classMethod(); //*Accessing static method using object
	}
}

Output -

instanceMethod() in Bar
classMethod() in Foo

We are using the same instance to access both the methods. But since one is overriding and the other is hiding, we see different behaviors.

  • In case of instance method- At runtime, JVM uses the instance 'f' to determine which method to run.

    JVM sees at runtime, that 'f' is actually an instance of Bar, so it calls the method in Bar rather than the one in Foo.

  • In case of class (static) method- Since, its a static method, JVM doesn't expect or need any instance to invoke that method. Even if you provide it with an instance like we did here, it will simply ignore it. It will just see the declaration of that instance, determine the declared type of it to determine which method to call. All of this, at compile-time.

    It doesn't matter when at runtime, its decided that f is actually an instance of Bar. That's what we mean when we say a static method does not have run-time polymorphism.

Because of this difference in behaviour in static and instance methods, we use different terms - "overriding" for instance methods and "hiding" for class methods.

And when we say, #Static Methods can't be overridden, it means you can write the code that looks like a static method being overridden, but it won't behave like an overridden method.

Notice that access using objects is possible

Open Recursion

When a method within an object invokes another method of the same object via #Self-reference, it is considered open recursion.

The key feature here is that the method to be called is determined by #Runtime Polymorphism.

Serialization

  • Mechanism of writing the state of an object into a byte stream.
  • Converting state of an object into a form that can be persisted or transported.

Serialization along with different modifiers

  • Static: If there is a static data member in the class, it will not be serialized as static members belong to the class and not the object.

Serialization with #Inheritance

(7), (8)

  • If parent is serializable, so is child.
  • Child can be serializable, without the parent being serializable (Object class isn't serializable and other classes can be made serializable)

In such cases, the members of parent aren't serialized.

Serialization with #Aggregation

If a serializable class has reference to another class, all references must be serializable

Deserialization

Reverse operation of #Serialization where byte stream is converted into object.

Cloning

❗️Not cloning

A obj = new A();
obj.i = 5;
obj.j = 6;

A obj1 = obj; //Shallow cloning

The object is only one. We have just created two references to the same object – not cloning.

Cloning involves creating new object and copying the fields so that we have a new object "cloned" from the original. Not just a new reference.

A obj1 = new A();
obj1.i = obj.i; //Deep
obj1.j = obj.j; //Cloning

Based on how these members are copied, cloning can be:

Shallow Cloning

Reference members are only copied, not cloned.

Deep Cloning

Even reference members within the original object (object being cloned) are cloned, not copied.

Not very efficient if there are too many data members.

References

  1. When should I use "this" in a class? - StackOverflow ^ce7b78
  2. Overriding v/s Hiding - sanaulla.info ^ded3e6
  3. Diamond Problem - StackOverflow ^961273
  4. Prefer composition over inheritance? - Stack Overflow ^f503e8
  5. Stack Overflow - How abstract class and interface achieve abstraction in java? ^5b854f
  6. GFG - Dynamic Method Dispatch or Runtime Polymmorphism in Java ^0d56c2
  7. Making Child classes as Non Serializable in java ^02cd75
  8. Does child class serialize parent class' members that are not serializable? - StackOverflow ^f796cd
  9. c# - Are there good reasons not to use an ORM? - Stack Overflow

© 2025 All rights reservedBuilt with Flowershow Cloud

Built with LogoFlowershow Cloud