Contents

Methods

Java Instance Methods

Instance methods are methods that are called on an instance (object) of the class they belong to. To invoke an instance method, you first need to create an object of the class.

Example syntax:

				
					public void display(String message) {
    // Code to be executed
}

				
			

Instance methods can return any data type, including primitive types (e.g., int, float) or user-defined types.

Memory Allocation of Instance Methods

Instance methods themselves are stored in the Permanent Generation (PermGen) space of the heap until Java 7, but since Java 8, they are stored in Metaspace. However, their local variables and parameters (arguments passed) are stored in the stack.

These methods can be called from within the class in which they are defined or from other classes, depending on the access modifiers.

Key Points:

1. Instance methods belong to objects, not to the class itself.
2. They are stored in a single memory location and can access the object they belong to using the this reference.
3. Instance methods can be overridden, as they are resolved at runtime through dynamic binding.

Example:

				
					// Example to demonstrate accessing an instance method.
class Car {
    
    String model = "";
    
    // Instance method to set car model
    public void setModel(String model) {
        this.model = model;
    }
}

public class Test {
    public static void main(String[] args) {
        
        // Create an instance of the Car class
        Car myCar = new Car();
        
        // Call the instance method to set model
        myCar.setModel("Tesla Model S");
        System.out.println(myCar.model);
    }
}

				
			

Output:

				
					Tesla Model S

				
			
Java Static Methods

Static methods, unlike instance methods, belong to the class rather than an object. They can be called without creating an instance of the class.

Example syntax:

				
					public static void display(String message) {
    // Code to be executed
}

				
			

Static methods must include the static modifier in their declaration.

Memory Allocation of Static Methods

Static methods are stored in the Metaspace area (starting from Java 8) since they are associated with the class rather than any object. However, their local variables and arguments are stored in the stack.

Key Points:

1. Static methods are called using the class name, without the need for an instance.
2. They are shared across all instances of the class.
3. Static methods cannot be overridden, but they can be hidden by subclass methods if both superclass and subclass declare a static method with the same name. This is called Method Hiding.

Example:

				
					// Example to demonstrate accessing static methods.
class MathUtil {
    
    public static double pi = 3.14159;
    
    // Static method to calculate the circumference of a circle
    public static double circumference(double radius) {
        return 2 * pi * radius;
    }
}

public class Test {
    public static void main(String[] args) {
        
        // Access static method and field using class name
        double result = MathUtil.circumference(5);
        System.out.println("Circumference: " + result);
        
        // Access static method using an instance (though it's unnecessary)
        MathUtil util = new MathUtil();
        double result2 = util.circumference(7);
        System.out.println("Circumference: " + result2);
    }
}

				
			

Output:

				
					Circumference: 31.4159
Circumference: 43.98226

				
			

Abstract Method

In Java, there are situations where we need only method declarations in superclass, which is accomplished using the abstract type modifier. Abstraction can be implemented through abstract classes and methods. In this discussion, we will explore Java Abstract Methods.

Java Abstract Method

An abstract method serves as a blueprint for other classes or interfaces. In this context, methods are declared but not implemented. Abstract methods must be implemented by subclasses or classes that implement the respective interfaces.

These methods are often referred to as “subclass responsibilities” because they lack implementations in the superclass. Consequently, any subclass must override these methods to provide concrete definitions.

Declaring Abstract Methods in Java

To declare an abstract method, use the following general syntax:

				
					abstract returnType methodName(parameterList);

				
			

Note that no method body is included. Any concrete class (a class not marked with the abstract keyword) that extends an abstract class must implement all abstract methods from that class.

Key Points about Abstract Methods

1. Any class containing one or more abstract methods must itself be declared as abstract.
2. A class containing an abstract method must be abstract, but the reverse is not necessarily true.
3. If a non-abstract class extends an abstract class, it must implement all abstract methods of the abstract class; otherwise, the non-abstract class must also be declared as abstract.
4. The following combinations of modifiers with abstract methods are illegal:final

  • abstract native
  • abstract synchronized
  • abstract static
  • abstract private
  • abstract strictf

Example of Java Abstract Method

Example 1: Performing Addition and Subtraction with Abstraction

Here is a program demonstrating how to perform addition and subtraction using abstraction.

				
					// Java Program to implement addition and subtraction using abstraction

// Abstract Class
abstract class MathOperation {
    abstract void displayInfo();
}

// Class Add
class Add extends MathOperation {
    void displayInfo() {
        int a = 5;
        int b = 10;
        System.out.println("Addition: " + (a + b));
    }
}

// Class Subtract
class Subtract extends MathOperation {
    void displayInfo() {
        int c = 15;
        int d = 4;
        System.out.println("Subtraction: " + (c - d));
    }
}

// Driver Class
public class AbstractionDemo {
    public static void main(String args[]) {
        MathOperation addition = new Add();
        addition.displayInfo();
        MathOperation subtraction = new Subtract();
        subtraction.displayInfo();
    }
}

				
			

Output:

				
					Addition: 15
Subtraction: 11

				
			

Example 2: Using Abstract Keyword with Classes and Methods

Here is another example illustrating the use of the abstract keyword.

				
					// A Java program demonstrating the use of abstract keyword

// Abstract class
abstract class Shape {
    abstract void draw();

    void show() {
        System.out.println("This is a concrete method in the abstract class.");
    }
}

// Concrete class Circle
class Circle extends Shape {
    void draw() {
        System.out.println("Drawing a circle.");
    }
}

// Driver class
public class AbstractExample {
    public static void main(String args[]) {
        Circle circle = new Circle();
        circle.draw();
        circle.show();
    }
}

				
			

Output:

				
					Drawing a circle.
This is a concrete method in the abstract class.

				
			

Example 3: Abstract Class with Multiple Abstract Methods

This program illustrates an abstract class containing multiple abstract methods.

				
					// Java Program to implement an abstract class with multiple abstract methods

abstract class AreaCalculator {
    abstract void calculateRectangleArea(int height, int width);
    abstract void calculateSquareArea(int side);
    abstract void calculateCircleArea(float radius);
}

// Class implementing the abstract methods
class Calculator extends AreaCalculator {
    public void calculateRectangleArea(int height, int width) {
        int area = height * width;
        System.out.println("Area of rectangle: " + area);
    }

    public void calculateSquareArea(int side) {
        int area = side * side;
        System.out.println("Area of square: " + area);
    }

    public void calculateCircleArea(float radius) {
        float area = 3.14f * radius * radius;
        System.out.println("Area of circle: " + area);
    }

    public static void main(String[] args) {
        Calculator calc = new Calculator();
        calc.calculateRectangleArea(10, 5);
        calc.calculateSquareArea(4);
        calc.calculateCircleArea(3.0f);
    }
}

				
			

Output:

				
					Area of rectangle: 50
Area of square: 16
Area of circle: 28.259999

				
			
Abstract Method in Interface 

All methods in an interface are inherently public and abstract, allowing for the declaration of abstract methods within an interface.

Here’s an implementation of this concept:

				
					// Java Program to demonstrate abstract methods in an interface

// Declaring an interface
interface Operations {
    int addTwoNumbers(int a, int b);
    int addThreeNumbers(int a, int b, int c);
}

// Main Class
public class InterfaceExample implements Operations {
    public int addTwoNumbers(int a, int b) {
        return a + b;
    }

    public int addThreeNumbers(int a, int b, int c) {
        return a + b + c;
    }

    public static void main(String args[]) {
        Operations ops = new InterfaceExample();
        System.out.println("Sum of two numbers: " + ops.addTwoNumbers(5, 10));
        System.out.println("Sum of three numbers: " + ops.addThreeNumbers(5, 10, 15));
    }
}

				
			

Output:

				
					Sum of two numbers: 15
Sum of three numbers: 30

				
			
Final Method in Abstract Class

While you cannot use final with an abstract method, you can define a final method in an abstract class.

				
					class Vehicle {
    final void fuel() {
        System.out.println("Fuel type: Petrol");
    }
}

class Car extends Vehicle {
    // This will cause an error
    // void fuel() { System.out.println("Fuel type: Diesel"); }
}

				
			

Output:

				
					Error: Cannot override the final method from Vehicle

				
			
Static Methods Cannot Be Overridden (Method Hiding):

When a static method is redefined in a subclass, it is method hiding, not overriding.

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

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

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.show(); // Calls Parent's static show()
    }
}

				
			

Output:

				
					Parent static show()

				
			
Private Methods Cannot Be Overridden:

Private methods are not visible to the child class and hence cannot be overridden.

				
					class Parent {
    private void message() {
        System.out.println("Parent's private message()");
    }

    public void callMessage() {
        message();
    }
}

class Child extends Parent {
    private void message() {
        System.out.println("Child's private message()");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.callMessage(); // Calls Parent's message()
    }
}

				
			

Output:

				
					Parent's private message()

				
			
Covariant Return Type:

The return type of an overriding method can be a subtype of the original return type.

				
					class Parent {
    public Object getObject() {
        return "Parent object";
    }
}

class Child extends Parent {
    @Override
    public String getObject() {
        return "Child object";
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.getObject());
    }
}

				
			

Output:

				
					Child object

				
			
the Parent Class Method using super:

The overridden method in the subclass can call the method from the parent class using the super keyword.

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

class Child extends Parent {
    @Override
    void display() {
        super.display();
        System.out.println("Child display()");
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        c.display(); // Calls both Parent's and Child's display()
    }
}

				
			

Output:

				
					Parent display()
Child display()

				
			
Method Overriding vs Method Overloading

1. Overloading is when methods have the same name but different signatures.
Overriding is when a subclass provides a specific implementation of a method that already exists in the parent class.
2. Overloading is a form of compile-time polymorphism (method resolution at compile time), while overriding is a form of run-time polymorphism (method resolution at runtime).

Overriding

Overriding is a feature in Java where a subclass or child class provides a specific implementation of a method that is already present in its parent class or superclass. When a method in a subclass has the same name, parameters (signature), and return type (or a subtype) as a method in its superclass, it overrides the method from the superclass.

Method Overriding enables Run-Time Polymorphism. This means that the method that gets executed depends on the object that is used to call it, not the reference type. If the object is of the subclass, the subclass method will be called; otherwise, the superclass method will be executed.

Example of Method Overriding:

				
					// Superclass
class Animal {
    void sound() { System.out.println("Animal makes a sound"); }
}

// Subclass
class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

// Driver class
class Main {
    public static void main(String[] args) {
        Animal a1 = new Animal();
        a1.sound();  // Calls Animal's sound()

        Animal a2 = new Dog();
        a2.sound();  // Calls Dog's sound (Run-time Polymorphism)
    }
}

				
			

Output:

				
					Animal makes a sound
Dog barks

				
			
Rules for Method Overriding

1. Access Modifiers in Overriding : The overridden method in the subclass can provide more visibility, but not less. For example, a protected method in the superclass can be overridden as public in the subclass, but not private.

				
					class Vehicle {
    protected void start() {
        System.out.println("Vehicle starts");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {  // More accessible
        System.out.println("Car starts");
    }
}

class Main {
    public static void main(String[] args) {
        Vehicle v = new Car();
        v.start();  // Calls Car's start()
    }
}

				
			

Output:

				
					Car starts

				
			

2. Final Methods Cannot Be Overridden : If a method is declared as final in the superclass, it cannot be overridden in the subclass.

				
					class Bird {
    final void fly() {
        System.out.println("Bird is flying");
    }
}

class Eagle extends Bird {
    // This would produce an error if uncommented
    // void fly() { System.out.println("Eagle flies faster"); }
}

				
			

Output:

				
					Compilation error: cannot override final method

				
			

3. Static Methods and Method Hiding : Static methods cannot be overridden; they are hidden. A subclass can define a static method with the same signature as the one in its superclass, but this will hide the method in the superclass rather than overriding it.

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

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

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

    @Override
    void show() {
        System.out.println("Child show");
    }
}

class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.display();  // Calls Parent's static method
        p.show();     // Calls Child's overridden method
    }
}

				
			

Output:

				
					Parent display
Child show

				
			

4. Private Methods Cannot Be Overridden : Private methods in the superclass cannot be overridden by subclasses. They are not visible to subclasses and are resolved at compile time.

				
					class Super {
    private void secretMethod() {
        System.out.println("Super's secret method");
    }

    public void callSecret() {
        secretMethod();
    }
}

class Sub extends Super {
    private void secretMethod() {
        System.out.println("Sub's secret method");
    }
}

public class Main {
    public static void main(String[] args) {
        Super sup = new Super();
        sup.callSecret();  // Calls Super's private method
    }
}

				
			

Output:

				
					Super's secret method

				
			

5. Covariant Return Type in Overriding : The return type of the overriding method can be a subclass of the return type of the overridden method.

				
					public class BitwiseOperators {
    public static void main(String[] args) {
        int a = 5; // 0101 in binary
        int b = 3; // 0011 in binary
        System.out.println("a & b: " + (a & b)); // AND operation
        System.out.println("a | b: " + (a | b)); // OR operation
    }
}

				
			

Output:

				
					class SuperClass {
    public Object getObject() {
        return new Object();
    }
}

class SubClass extends SuperClass {
    @Override
    public String getObject() {
        return "This is a String";
    }
}

public class Main {
    public static void main(String[] args) {
        SuperClass obj = new SubClass();
        System.out.println(obj.getObject());
    }
}

				
			

Output:

				
					This is a String

				
			

6. Calling the Superclass Method : We can call the superclass version of the overridden method using the super keyword.

				
					class Superhero {
    void power() {
        System.out.println("Superhero has generic powers");
    }
}

class Superman extends Superhero {
    @Override
    void power() {
        super.power();
        System.out.println("Superman can fly and has super strength");
    }
}

class Main {
    public static void main(String[] args) {
        Superman clark = new Superman();
        clark.power();
    }
}

				
			

Output:

				
					Superhero has generic powers
Superman can fly and has super strength

				
			
Overriding and Exception Handling

1. Unchecked Exceptions: If the superclass method does not throw any exceptions, the subclass can only throw unchecked exceptions when overriding it.

				
					class SuperClass {
    void riskyMethod() {
        System.out.println("SuperClass riskyMethod");
    }
}

class SubClass extends SuperClass {
    @Override
    void riskyMethod() throws ArithmeticException {
        System.out.println("SubClass riskyMethod");
    }
}

				
			

2. Checked Exceptions : If the superclass method throws an exception, the overriding method can throw the same exception or its subclass, but not a higher-level exception.

				
					class Animal {
    void eat() throws Exception {
        System.out.println("Animal is eating");
    }
}

class Dog extends Animal {
    @Override
    void eat() throws RuntimeException {
        System.out.println("Dog is eating");
    }
}

				
			

Method Overloading

In Java, Method Overloading allows multiple methods to share the same name but differ in parameters—either by the number of parameters, types of parameters, or a combination of both. This feature is also called Compile-time Polymorphism, Static Polymorphism, or Early Binding. When overloaded methods are present, Java gives priority to the most specific match among the parameters.

Example of Method Overloading

				
					// Java program demonstrating method overloading

public class Calculator {
    // Overloaded add() method that accepts two integer parameters
    public int add(int a, int b) {
        return a + b;
    }

    // Overloaded add() method that accepts three integer parameters
    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // Overloaded add() method that accepts two double parameters
    public double add(double a, double b) {
        return a + b;
    }

    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println(calc.add(5, 10));         // Two integers
        System.out.println(calc.add(5, 10, 15));     // Three integers
        System.out.println(calc.add(4.5, 3.5));      // Two doubles
    }
}

				
			

Output:

				
					15
30
8.0

				
			
Ways to Achieve Method Overloading in Java:

1. Changing the Number of Parameters
2. Changing the Data Types of the Arguments
3. Changing the Order of Parameters

1. Changing the Number of Parameters : This approach achieves method overloading by varying the number of input parameters in methods that share the same name.

				
					// Java program to demonstrate method overloading by changing the number of parameters

class Multiply {
    // Multiply two integers
    public int multiply(int a, int b) {
        return a * b;
    }

    // Multiply three integers
    public int multiply(int a, int b, int c) {
        return a * b * c;
    }

    public static void main(String[] args) {
        Multiply obj = new Multiply();
        System.out.println("Product of two integers: " + obj.multiply(2, 3));
        System.out.println("Product of three integers: " + obj.multiply(2, 3, 4));
    }
}

				
			

Output:

				
					Product of two integers: 6
Product of three integers: 24

				
			

2. Changing Data Types of the Arguments : This method achieves overloading by having the same method name but with different parameter types.

				
					// Java program to demonstrate method overloading by changing the data types of parameters

class Volume {
    // Calculate volume using integer values
    public int calculateVolume(int a, int b, int c) {
        return a * b * c;
    }

    // Calculate volume using double values
    public double calculateVolume(double a, double b, double c) {
        return a * b * c;
    }

    public static void main(String[] args) {
        Volume vol = new Volume();
        System.out.println("Volume with integers: " + vol.calculateVolume(2, 3, 4));
        System.out.println("Volume with doubles: " + vol.calculateVolume(2.5, 3.5, 4.5));
    }
}

				
			

Output:

				
					Volume with integers: 24
Volume with doubles: 39.375

				
			

3. Changing the Order of Parameters : You can achieve method overloading by altering the order in which parameters are passed.

				
					// Java program to demonstrate method overloading by changing the order of parameters

class Display {
    // Display information by name followed by age
    public void showDetails(String name, int age) {
        System.out.println("Name: " + name + ", Age: " + age);
    }

    // Display information by age followed by name
    public void showDetails(int age, String name) {
        System.out.println("Age: " + age + ", Name: " + name);
    }

    public static void main(String[] args) {
        Display obj = new Display();
        obj.showDetails("Alice", 30);
        obj.showDetails(25, "Bob");
    }
}

				
			

Output:

				
					Name: Alice, Age: 30
Age: 25, Name: Bob

				
			
What if the exact prototype doesn’t match?

In situations where the parameters don’t match any exact method signature, Java prioritizes methods based on data type compatibility. The compiler tries to:

1. Convert the parameter to a higher data type within the same group (e.g., from byte to int).
2. If no match is found, it attempts to move to a higher data type in another group (e.g., from int to float).

				
					class Demo {
    public void display(int x) {
        System.out.println("Integer: " + x);
    }

    public void display(String s) {
        System.out.println("String: " + s);
    }

    public void display(byte b) {
        System.out.println("Byte: " + b);
    }
}

public class UseDemo {
    public static void main(String[] args) {
        byte a = 20;
        Demo obj = new Demo();

        obj.display(a);          // Byte method
        obj.display("Hello");     // String method
        obj.display(500);         // Integer method
        obj.display('C');         // Char is promoted to int (ASCII value)

        // Uncommenting the line below would cause a compilation error:
        // obj.display(10.5);     // No suitable method for double
    }
}

				
			

Output:

				
					Byte: 20
String: Hello
Integer: 500
Integer: 67

				
			
Advantages of Method Overloading
  • Enhanced Readability and Reusability: By using method overloading, the code becomes more intuitive, reducing the need for verbose method names.
  • Reduced Complexity: Methods performing similar tasks can share a name, making code easier to manage.
  • Efficiency: Overloading allows different versions of methods to handle related tasks with varying parameters, optimizing the function call.
  • Multiple Constructor Options: Overloaded constructors enable different ways to initialize objects, depending on the given arguments.