Object Oriented Programming (OOPs) in Java

What is OOPs in Java ?
Advantages of object oriented programming:
  1. Improved software-development productivity:

    • Provides improved software-development productivity over traditional procedure-based programming techniques due to below factors.
      1. Modularity: Provides separation of duties in object-based program development.
      2. Extensibility: Objects can be extended to include new attributes and behaviors.
      3. Reusability: Objects can also be reused within an across applications.
  2. Improved software maintainability:

    • For the reasons mentioned above, it is easier to maintain.
    • Since the design is modular, part of the system can be updated in case of issues without a need to make large-scale changes.
  3. Faster development:

    • Reuse enables faster development.
    • Come with rich libraries of objects, and code developed during projects is also reusable in future projects.
  4. Lower cost of development:

    • Typically, more effort is put into the object-oriented analysis and design, which lowers the overall cost of development.
  5. Higher-quality software:

    • Faster development of software and lower cost of development allows more time and resources to be used in the verification of the software.
    • Although quality is dependent upon the experience of the teams, it tends to result in higher-quality software.
Disadvantages of object oriented programming:

Major principles of object-oriented programming:

Classes, Objects (Instances), Methods

Class

Components of Class

  1. Modifiers:
    • A class can be public or has default access.
    • It can’t be declared as private or protected.
  2. Class name:
    • The name should begin with a initial letter (capitalized by convention).
  3. Superclass (if any):
    • The name of the class’s parent (superclass), if any, preceded by the keyword extends.
    • A class can only extend (subclass) one parent.
  4. Interfaces (if any):
    • A comma-separated list of interfaces implemented by the class, if any, preceded by the keyword implements.
    • A class can implement more than one interface.
  5. Body:
    • The class body surrounded by braces { }.
Constructors:

Note: There are various types of classes that are used in real time applications such as nested classes, anonymous classes, lambda expressions.

Object

Components of Object:

  1. State:
    • Represented by attributes of an object.
    • Also reflects the properties of an object.
  2. Behavior:
    • Represented by methods of an object.
    • Also reflects the response of an object with other objects.
  3. Identity:
    • It gives a unique name to an object and enables one object to interact with other objects.

Example:

Notes:

Declaring Objects / Instantiating Class

Important Points:
Dog tuffy;

Initializing an object

Example:

public class Dog { 
    // Instance Variables 
    String name; 
    String breed; 
    int age; 
    String color; 
  
    // Constructor Declaration of Class 
    public Dog(String name, String breed, int age, String color){ 
        this.name = name; 
        this.breed = breed; 
        this.age = age; 
        this.color = color; 
    } 
  
    // method 1 
    public String getName(){ 
        return name; 
    } 
  
    // method 2 
    public String getBreed(){ 
        return breed; 
    } 
  
    // method 3 
    public int getAge(){ 
        return age; 
    } 
  
    // method 4 
    public String getColor(){ 
        return color; 
    } 
  
    @Override
    public String toString(){ 
        return("Hi my name is "+ this.getName() + ".\nMy breed, age and color are " + 
               this.getBreed()+"," + this.getAge()+ ","+ this.getColor()); 
    } 
  
    public static void main(String[] args){
        // Initializing a Dog object
        Dog tuffy = new Dog("tuffy","papillon", 5, "white"); 
        System.out.println(tuffy.toString()); 
    } 
}

Output:

Hi my name is tuffy.
My breed,age and color are papillon,5,white
About above class
Dog tuffy = new Dog("tuffy", "papillon", 5, "white");

Important Points:

Methods

Components of Method

  1. Access Modifier:
    • Defines access type of the method i.e. from where it can be accessed in our application.
    • In Java, there 4 type of the access specifiers.
      • public: accessible in all class in the application.
      • protected: accessible within the package in which it is defined and in its subclass(es)(including subclasses declared outside the package).
      • private: accessible only within the class in which it is defined.
      • default (declared/defined without using any modifier): accessible within same class and package within which its class is defined.
  2. The return type:
    • The data type of the value returned by the method or void if does not return a value.
  3. Method Name:
    • The rules for field names apply to method names as well, but the convention is a little different.
  4. Parameter list:
    • Comma separated list of the input parameters are defined, preceded with their data type, within the enclosed parenthesis.
    • If there are no parameters, we must use empty parentheses ().
  5. Exception list:
    • The exceptions we expect by the method can throw, we can specify these exception(s).
  6. Method body:
    • The code we need to be executed to perform our intended operations enclosed between braces.

Message Passing

Inheritance

What is Inheritance ?
Important terminologies:
class DerivedClass extends BaseClass {  
   //methods and fields  
} 

Types of Inheritance

1. Single Inheritance :

Single_Inheritance

class one { 
	public void print_geek() { 
		System.out.println("Geeks"); 
	} 
} 

class two extends one { 
	public void print_for() { 
		System.out.println("for"); 
	} 
} 
 
public class Main { 
	public static void main(String[] args) { 
		two g = new two(); 
		g.print_for(); 
		g.print_geek(); 
	} 
} 

Output:

for
Geeks

2. Multilevel Inheritance

Multilevel_Inheritance

// Java program to illustrate the concept of Multilevel inheritance 
class one { 
	public void print_geek() { 
		System.out.println("Geeks 1"); 
	}
  
  public void print_hello() { 
		System.out.println("Hello from GrandParent"); 
	}
} 

class two extends one { 
	public void print_for() { 
		System.out.println("for"); 
	} 
} 

class three extends two { 
	public void print_geek() { 
		System.out.println("Geeks 3"); 
	} 
} 

public class Main { 
	public static void main(String[] args) { 
		three g = new three();
    g.print_geek();
		g.print_for(); 
		g.print_hello(); 
	} 
} 

Output:

Geeks 3
for
Hello from GrandParent
Method Resolution:
Accessing Grandparent’s member in Java using super
class Grandparent { 
    public void Print() { 
        System.out.println("Grandparent"); 
    } 
} 
   
class Parent extends Grandparent { 
    public void Print() {        
        System.out.println("Parent"); 
    } 
} 
   
class Child extends Parent { 
    public void Print() { 
        super.super.Print();  // Trying to access Grandparent's Print() 
        System.out.println("Child"); 
    } 
} 
   
public class Main { 
    public static void main(String[] args) { 
        Child c = new Child(); 
        c.Print(); 
    } 
} 

Output:

Compiler Error
Important Points:
class Grandparent { 
    public void Print() { 
        System.out.println("Grandparent"); 
    } 
} 
   
class Parent extends Grandparent { 
    public void Print() { 
        super.Print(); 
        System.out.println("Parent"); 
    } 
} 
   
class Child extends Parent { 
    public void Print() { 
        super.Print(); 
        System.out.println("Child"); 
    } 
} 
   
public class Main { 
    public static void main(String[] args) { 
        Child c = new Child(); 
        c.Print(); 
    } 
} 

Output:

Grandparent
Parent
Child

3. Hierarchical Inheritance :

4. Multiple Inheritance (Through Interfaces) :

interface one { 
	public void print_geek(); 
} 

interface two { 
	public void print_for(); 
} 

interface three extends one, two { 
	public void print_geek(); 
} 

class child implements three { 
	@Override
	public void print_geek() { 
		System.out.println("Geeks"); 
	} 

	public void print_for() { 
		System.out.println("for"); 
	} 
} 

public class Main { 
	public static void main(String[] args) { 
		child c = new child(); 
		c.print_geek(); 
		c.print_for(); 
	} 
} 

Output:

Geeks
for

5. Hybrid Inheritance(Through Interfaces)

Important Points about inheritance in Java
What all can be done in a Subclass ?

Polymorphism

What is Polymorphism ?
Types Polymorphism

Compile Time Polymorphism

Method Overloading

// Overloaded sum with different number of parameters and differnt argument types
public class Sum { 
  public int sum(int x, int y) { 
    return (x + y); 
  } 

  public int sum(int x, int y, int z) { 
    return (x + y + z); 
  } 

  public double sum(double x, double y) { 
    return (x + y); 
  } 

  public static void main(String args[]) { 
    Sum s = new Sum(); 
    System.out.println(s.sum(10, 20)); 
    System.out.println(s.sum(10, 20, 30)); 
    System.out.println(s.sum(10.5, 20.5)); 
  } 
} 

Output:

30
60
31.0

Operator Overloading

class OperatorOVERDDN { 
    void operator(String str1, String str2) { 
        String s = str1 + str2; 
        System.out.println("Concatinated String - " + s); 
    } 
  
    void operator(int a, int b) { 
        int c = a + b; 
        System.out.println("Sum = " + c); 
    } 
} 
  
class Main { 
    public static void main(String[] args) { 
        OperatorOVERDDN obj = new OperatorOVERDDN(); 
        obj.operator(2, 3); 
        obj.operator("joe", "now"); 
    } 
} 

Output:

Sum = 5
Concatinated String - joenow

Runtime Polymorphism

Method Overriding

class Parent { 
    void show() { 
        System.out.println("Parent's show()"); 
    } 
} 
  
class Child extends Parent { 
    // This method overrides show() of Parent 
    @Override
    void show() { 
        System.out.println("Child's show()"); 
    } 
} 
  
class Main { 
    public static void main(String[] args) { 
        // If a Parent type reference refers to a Parent object, then Parent's show is called 
        Parent obj;
        obj = new Parent(); 
        obj.show(); 
  
        // If a Parent type reference refers to a Child object, then Child's show() is called. 
        // This is RUN TIME POLYMORPHISM. 
        // Also known as Dynamic Dispatch (here child's show() method, above parent's show() method)
        obj = new Child(); 
        obj.show(); 
    } 
}

Output:

Parent's show()
Child's show()

Note: In Java, we can override methods only, not the variables(data members), so runtime polymorphism cannot be achieved by data members.

class A { 
    int x = 10; 
} 
  
class B extends A { 
    int x = 20; 
} 
  
public class Test { 
    public static void main(String args[]) { 
        A a = new B(); // object of type B 
      
        // Data member of class A will be accessed 
        System.out.println(a.x); 
    } 
}

Output:

10
Why Method Overriding ?
Rules for method overriding
1. Overriding and Access-Modifiers :
2. Private methods can not be overridden :
class Parent { 
    // private methods are not overridden 
    private void m1() { 
        System.out.println("From parent m1()"); 
    } 
  
    protected void m2() { 
        System.out.println("From parent m2()"); 
    } 
} 
  
class Child extends Parent { 
    // new m1() method unique to Child class 
    private void m1() { 
        System.out.println("From child m1()"); 
    } 
  
    // overriding method with more accessibility :- protected to public 
    @Override
    public void m2() { 
        System.out.println("From child m2()"); 
    } 
} 
  
class Main { 
    public static void main(String[] args) { 
        Parent obj1 = new Parent(); 
        obj1.m2(); 
        Parent obj2 = new Child(); 
        obj2.m2(); 
    } 
}

Output:

From parent m2()
From child m2()
3. Final methods can not be overridden :
class Parent { 
    // Can't be overridden 
    final void show() {} 
} 
  
class Child extends Parent { 
    // This would produce error 
    void show() {} 
} 
4. Static methods can not be overridden(Method Overriding vs Method Hiding) :

5. The overriding method must have same return type (or subtype) :
6. Invoking overridden method from sub-class :
class Parent { 
	void show() { 
		System.out.println("Parent's show()"); 
	} 
} 

class Child extends Parent { 
	// This method overrides show() of Parent 
	@Override
	void show() { 
		super.show(); 
		System.out.println("Child's show()"); 
	} 
} 

class Main { 
	public static void main(String[] args) { 
		Parent obj = new Child(); 
		obj.show(); 
	} 
} 
7. Overriding and constructor :
8. Overriding and Exception-Handling :
9. Overriding and abstract method:
10. Overriding and synchronized/strictfp method :

Encapsulation (Data Hiding)

What is Data Encapsulation ?
How Encapsulation is achieved ?
public class Encapsulate { 
    // private variables declared these can only be accessed by public methods of class 
    private String geekName; 
    private int geekRoll; 
    private int geekAge; 
  
    // get method for age to access private variable geekAge 
    public int getAge() { 
      return geekAge; 
    } 
   
    // get method for name to access private variable geekName 
    public String getName() { 
      return geekName; 
    } 
      
    // get method for roll to access private variable geekRoll 
    public int getRoll() { 
       return geekRoll; 
    } 
   
    // set method for age to access private variable geekage 
    public void setAge( int newAge) { 
      geekAge = newAge; 
    } 
   
    // set method for name to access private variable geekName 
    public void setName(String newName) { 
      geekName = newName; 
    } 
      
    // set method for roll to access private variable geekRoll 
    public void setRoll( int newRoll)  { 
      geekRoll = newRoll; 
    } 
} 

public class TestEncapsulation {     
    public static void main (String[] args) { 
        Encapsulate obj = new Encapsulate(); 
          
        // setting values of the variables  
        obj.setName("Harsh"); 
        obj.setAge(19); 
        obj.setRoll(51); 
          
        // Displaying values of the variables 
        System.out.println("Geek's name: " + obj.getName()); 
        System.out.println("Geek's age: " + obj.getAge()); 
        System.out.println("Geek's roll: " + obj.getRoll()); 
          
        // Direct access of geekRoll is not possible due to encapsulation 
        // System.out.println("Geek's roll: " + obj.geekName);         
    } 
} 

Output:

Geek's name: Harsh
Geek's age: 19
Geek's roll: 51
Advantages of Encapsulation:

Abstraction (Detail Hiding)

What is Data Abstraction ?
How Abstraction is achieved ?
Advantages of Abstraction:
Encapsulation vs Data Abstraction

Abstract Classes

What are abstract classes ?
What are abstract methods ?
  1. An abstract method is a method that is declared without an implementation.
  2. It contains only signature of the mehod.
When to use abstract classes and abstract methods ?

Situation Example:

abstract class Shape  { 
    String color; 
      
    // these are abstract methods 
    abstract double area(); 
    public abstract String toString(); 
      
    // abstract class can have constructor 
    public Shape(String color) { 
        System.out.println("Shape constructor called"); 
        this.color = color; 
    } 
      
    // this is a concrete method 
    public String getColor() { 
        return color; 
    } 
} 

class Circle extends Shape { 
    double radius; 
     
    public Circle(String color,double radius) { 
        // calling Shape constructor 
        super(color); 
        System.out.println("Circle constructor called"); 
        this.radius = radius; 
    } 
  
    @Override
    double area() { 
        return Math.PI * Math.pow(radius, 2); 
    } 
  
    @Override
    public String toString() { 
        return "Circle color is " + super.color + "and area is : " + area(); 
    }    
} 

class Rectangle extends Shape{ 
    double length; 
    double width; 
      
    public Rectangle(String color,double length,double width) { 
        // calling Shape constructor 
        super(color); 
        System.out.println("Rectangle constructor called"); 
        this.length = length; 
        this.width = width; 
    } 
      
    @Override
    double area() { 
        return length*width; 
    } 
  
    @Override
    public String toString() { 
        return "Rectangle color is " + super.color + "and area is : " + area(); 
    } 
  
}

public class Test  { 
    public static void main(String[] args) { 
        Shape s1 = new Circle("Red", 2.2); 
        Shape s2 = new Rectangle("Yellow", 2, 4); 
          
        System.out.println(s1.toString()); 
        System.out.println(s2.toString()); 
    } 
} 

Output:

Shape constructor called
Circle constructor called
Shape constructor called
Rectangle constructor called
Circle color is Redand area is : 15.205308443374602
Rectangle color is Yellowand area is : 8.0
Properties of Abstract Classes

Property-1: An instance of an abstract class cannot be created, we can have references of abstract class type though.

abstract class Base { 
    abstract void fun(); 
} 
class Derived extends Base { 
    void fun() { System.out.println("Derived fun() called"); } 
} 
class Main { 
    public static void main(String args[]) {  
        // Below line will cause compiler error as thetries to create an instance of abstract class. 
        // Base b = new Base(); 
  
        // But we can have references of Base type. 
        Base b = new Derived(); 
        b.fun();  
    } 
} 

Output:

Derived fun() called

Property-2: Constructor of abstract class is called when an instance of a inherited class is created.

abstract class Base { 
    Base() { System.out.println("Base Constructor Called"); } 
    abstract void fun(); 
} 
class Derived extends Base { 
    Derived() { System.out.println("Derived Constructor Called"); } 
    void fun() { System.out.println("Derived fun() called"); } 
} 
class Main { 
    public static void main(String args[]) {  
       Derived d = new Derived(); 
    } 
}

Output:

Base Constructor Called
Derived Constructor Called

Property-3: We can have an abstract class without any abstract method, this allows to create classes that cannot be instantiated, but can only be inherited.

abstract class Base {    
    void fun() { System.out.println("Base fun() called"); } 
} 
   
class Derived extends Base { } 
   
class Main { 
    public static void main(String args[]) {  
        Derived d = new Derived(); 
        d.fun(); 
    } 
}

Output:

Base fun() called

Property-4: Abstract classes can also have final methods (methods that cannot be overridden).

abstract class Base { 
    final void fun() { System.out.println("Base fun() called"); } 
} 
   
class Derived extends Base {} 
   
class Main { 
    public static void main(String args[]) {  
       Base b = new Derived(); 
       b.fun(); 
    } 
}

Output:

Base fun() called

Interfaces

What is an Interface?

Basic Syntax:

interface <interface_name> {
    // declare constant fields
    // declare methods that are abstract by default.
}
Why do we use interface ?
Important Points about Interfaces
Why use interfaces when we have abstract classes?

Example:

interface In1 { 
    // public, static and final 
    final int a = 10; 
  
    // public and abstract  
    void display(); 
} 
  
// A class that implements the interface. 
class TestClass implements In1 { 
    // Implementing the capabilities of interface. 
    public void display() { 
        System.out.println("Geek"); 
    } 
  
    public static void main (String[] args) { 
        TestClass t = new TestClass(); 
        t.display(); 
        System.out.println(a); 
    } 
} 

Output:

Geek
10

A real World Example:

interface Vehicle { 
    // all are the abstract methods. 
    void changeGear(int a); 
    void speedUp(int a); 
    void applyBrakes(int a); 
} 
  
class Bicycle implements Vehicle{ 
    int speed; 
    int gear; 
     // to change gear 
    @Override
    public void changeGear(int newGear){ 
        gear = newGear; 
    } 
      
    // to increase speed 
    @Override
    public void speedUp(int increment){ 
        speed = speed + increment; 
    } 
      
    // to decrease speed 
    @Override
    public void applyBrakes(int decrement){ 
        speed = speed - decrement; 
    } 
      
    public void printStates() { 
         System.out.println("speed: " + speed + " gear: " + gear); 
    } 
} 
  
class Bike implements Vehicle { 
    int speed; 
    int gear; 
      
    // to change gear 
    @Override
    public void changeGear(int newGear){  
        gear = newGear; 
    } 
      
    // to increase speed 
    @Override
    public void speedUp(int increment){  
        speed = speed + increment; 
    } 
      
    // to decrease speed 
    @Override
    public void applyBrakes(int decrement){ 
        speed = speed - decrement; 
    } 
      
    public void printStates() { 
         System.out.println("speed: " + speed + " gear: " + gear); 
    } 
      
} 
class GFG { 
    public static void main (String[] args) { 
        // creating an inatance of Bicycle doing some operations  
        Bicycle bicycle = new Bicycle(); 
        bicycle.changeGear(2); 
        bicycle.speedUp(3); 
        bicycle.applyBrakes(1); 
          
        System.out.println("Bicycle present state :"); 
        bicycle.printStates(); 
          
        // creating instance of the bike. 
        Bike bike = new Bike(); 
        bike.changeGear(1); 
        bike.speedUp(4); 
        bike.applyBrakes(3); 
          
        System.out.println("Bike present state :"); 
        bike.printStates(); 
    } 
} 

Output:

Bicycle present state :
speed: 2 gear: 2
Bike present state :
speed: 1 gear: 1

New features added in interfaces in Java 8

1. Default implementation for interface methods

Prior to Java 8, interface could not define implementation. We can now add default implementation for interface methods.

This default implementation has special use and does not affect the intention behind interfaces.

Reason:

Suppose we need to add a new function in an existing interface. Obviously the old code will not work as the classes have not implemented those new functions. So with the help of default implementation, we will give a default body for the newly added functions. Then the old codes will still work.

interface In1 { 
    final int a = 10; 
    default void display() { 
        System.out.println("hello"); 
    } 
} 
  
class TestClass implements In1 { 
    public static void main (String[] args) { 
        TestClass t = new TestClass(); 
        t.display(); 
    } 
}

Output:

hello
2. Defining static methods in interface

We can now define static methods in interfaces which can be called independently without an object.

Note:- these methods are not inherited.

interface In1 { 
    final int a = 10; 
    default void display() { 
        System.out.println("hello"); 
    } 
} 
  
class TestClass implements In1 { 
    public static void main (String[] args) { 
        In1.display(); 
    } 
}

Output:

hello

New features added in interfaces in Java 9

From Java 9 onwards, interfaces can contain following also

  1. Static methods
  2. Private methods
  3. Private Static methods
Implements vs. Extends