0% found this document useful (0 votes)
0 views25 pages

Java Notes

The document explains constructors in Java, which are special methods used to initialize newly created objects, highlighting their definition, purpose, characteristics, and types including default and parameterized constructors. It also covers constructor overloading, the use of 'this()' for constructor chaining, and the 'super()' keyword for invoking superclass constructors. Additionally, the document discusses control statements in Java, including decision-making, looping, and jump statements, providing syntax and examples for each type.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
0 views25 pages

Java Notes

The document explains constructors in Java, which are special methods used to initialize newly created objects, highlighting their definition, purpose, characteristics, and types including default and parameterized constructors. It also covers constructor overloading, the use of 'this()' for constructor chaining, and the 'super()' keyword for invoking superclass constructors. Additionally, the document discusses control statements in Java, including decision-making, looping, and jump statements, providing syntax and examples for each type.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 25

CONSTRUCTORS

In Java, constructors are special methods that are used to initialize newly created
objects. When a new instance of a class is created, the constructor is called automatically to
initialize the object’s state (i.e., the values of its fields/attributes). Understanding constructors
is fundamental to object-oriented programming in Java, as they allow you to set up objects
with initial values, ensuring that they are in a valid state right from the moment they are
created.
1. Definition and Purpose of a Constructor
A constructor is a block of code that gets executed when an object of a class is
instantiated (i.e., created). The main purpose of a constructor is to initialize the newly created
object. It sets up the initial state of the object by assigning values to its instance variables or
performing other setup actions.
 Constructors are called automatically when you create an object using the new
keyword.
 A class can have multiple constructors (this is called constructor overloading), each
with a different parameter list, allowing for different ways of initializing objects.
2. Characteristics of a Constructor
 Name: The constructor must have the same name as the class in which it is defined.
For example, in a class Car, the constructor must be named Car.
 No Return Type: Unlike regular methods, constructors do not have a return type, not
even void. They cannot return any value.
 Called Automatically: The constructor is invoked automatically when you create an
object of a class using the new keyword.
 Cannot Be Inherited: Constructors are not inherited by subclasses, but they can be
invoked from a subclass constructor using super().
 Can Be Overloaded: You can define multiple constructors with different parameter
lists (this is known as constructor overloading). Java will choose the appropriate
constructor based on the number and types of arguments passed during object
creation.
3. Types of Constructors in Java
Java supports two main types of constructors:
1. Default Constructor (No-Argument Constructor)
 A default constructor is a constructor that does not take any arguments.
 If no constructor is defined in the class, Java automatically provides a default
constructor. The default constructor initializes the object with default values (e.g.,
null for objects, 0 for numbers, false for booleans).
 When you explicitly define a constructor with parameters, the default constructor is
no longer automatically provided.
Example of a Default Constructor:
class Person {
String name;
int age;
// Default constructor
public Person() {
name = "John Doe"; // Default name
age = 30; // Default age
}

public void displayInfo() {


System.out.println("Name: " + name + ", Age: " + age);
}
}
public class Main {
public static void main(String[] args) {
// Creating an object using the default constructor
Person person1 = new Person();
person1.displayInfo(); // Output: Name: John Doe, Age: 30
}
}
In the example above, the Person class has a default constructor that initializes the
name and age fields with default values. When a Person object is created using this
constructor, these values are automatically assigned.
2. Parameterized Constructor
 A parameterized constructor is a constructor that takes one or more arguments. These
arguments are passed during object creation and used to initialize the object’s instance
variables with specific values.
 It allows you to create objects with custom values, rather than using default values.
Example of a Parameterized Constructor:
class Person {
String name;
int age;

// Parameterized constructor
public Person(String name, int age) {
this.name = name; // Initialize name with the provided value
this.age = age; // Initialize age with the provided value
}

public void displayInfo() {


System.out.println("Name: " + name + ", Age: " + age);
}
}
public class Main {
public static void main(String[] args) {
// Creating an object using the parameterized constructor
Person person1 = new Person("Alice", 25);
person1.displayInfo(); // Output: Name: Alice, Age: 25

Person person2 = new Person("Bob", 30);


person2.displayInfo(); // Output: Name: Bob, Age: 30
}
}
In this example, the Person class has a parameterized constructor that takes the name
and age parameters and initializes the object’s fields with the passed values. This allows
different objects to be created with different attributes.
4. Constructor Overloading
Constructor overloading is the ability to define multiple constructors in the same
class, each with a different number or type of parameters. Java will choose the appropriate
constructor based on the number and types of arguments passed when creating the object.
Example of Constructor Overloading:
class Car {
String model;
int year;
// Default constructor
public Car() {
this.model = "Unknown";
this.year = 0;
}
// Parameterized constructor
public Car(String model, int year) {
this.model = model;
this.year = year;
}
public void displayInfo() {
System.out.println("Model: " + model + ", Year: " + year);
}
}
public class Main {
public static void main(String[] args) {
// Using the default constructor
Car car1 = new Car();
car1.displayInfo(); // Output: Model: Unknown, Year: 0
// Using the parameterized constructor
Car car2 = new Car("Toyota", 2020);
car2.displayInfo(); // Output: Model: Toyota, Year: 2020
}
}
In the example above, the Car class has two constructors:
1. A default constructor that initializes model to "Unknown" and year to 0.
2. A parameterized constructor that initializes model and year with the values passed
during object creation.
5. this() and Constructor Chaining
In Java, you can use the keyword this() within a constructor to call another
constructor in the same class. This is known as constructor chaining. It helps in reusing
code and avoiding redundancy when you have multiple constructors.
Example of this() Usage:
class Car {
String model;
int year;
// Default constructor
public Car() {
this("Unknown", 0); // Calls the parameterized constructor
}
// Parameterized constructor
public Car(String model, int year) {
this.model = model;
this.year = year;
}
public void displayInfo() {
System.out.println("Model: " + model + ", Year: " + year);
}
}
public class Main {
public static void main(String[] args) {
// Using the default constructor, which calls the parameterized constructor
Car car1 = new Car();
car1.displayInfo(); // Output: Model: Unknown, Year: 0
// Using the parameterized constructor
Car car2 = new Car("Honda", 2023);
car2.displayInfo(); // Output: Model: Honda, Year: 2023
}
}
In this example, the default constructor Car() calls the parameterized constructor
using this("Unknown", 0). This way, common initialization code is reused.
6. The super() Constructor Call
In Java, constructors of a subclass can invoke the constructor of its superclass using
the super() keyword. This is used for initializing the state of the parent class when creating an
object of the child class.
class Vehicle {
String model;
int year;
// Constructor of the superclass
public Vehicle(String model, int year) {
this.model = model;
this.year = year;
}
}
class Car extends Vehicle {
String type;
// Constructor of the subclass
public Car(String model, int year, String type) {
super(model, year); // Call the superclass constructor
this.type = type;
}
public void displayInfo() {
System.out.println("Model: " + model + ", Year: " + year + ", Type: " + type);
}
}
public class Main {
public static void main(String[] args) {
Car car1 = new Car("Toyota", 2022, "SUV");
car1.displayInfo(); // Output: Model: Toyota, Year: 2022, Type: SUV
}
}
In this example, the constructor of the Car class calls the constructor of the Vehicle
class using super (model, year) to initialize the parent class fields.
CONTROL STATEMENTS IN JAVA
Control statements in Java are used to determine the flow of program execution. They
allow you to make decisions (conditional statements), repeat certain actions (loops), and
control the program flow in other ways. Control statements are crucial for implementing
logic, performing actions repeatedly, and handling different scenarios in a program.
Java has the following types of control statements:
1. Decision-making statements (Conditional statements)
2. Looping statements
3. Jump statements
Let's explore each of these control statements in detail.
1. Decision-Making Statements (Conditional Statements)
Decision-making statements allow you to execute certain blocks of code based on
conditions. These are useful when you need to perform different actions based on whether a
condition is true or false.
a. if statement
The if statement is used to execute a block of code if a specific condition is true.
Syntax:
if (condition) {
// Block of code to be executed if the condition is true
}
Example:
public class Main {
public static void main(String[] args) {
int num = 10;
if (num > 5) {
System.out.println("The number is greater than 5");
}
}
}
In this example, since num is greater than 5, the message "The number is greater than
5" is printed.
b. if-else statement
The if-else statement provides an alternative block of code to execute if the condition
is false.
Syntax:
if (condition) {
// Block of code to be executed if the condition is true
} else {
// Block of code to be executed if the condition is false
}
Example:
public class Main {
public static void main(String[] args) {
int num = 3;
if (num > 5) {
System.out.println("The number is greater than 5");
} else {
System.out.println("The number is less than or equal to 5");
}
}
}
Here, since num is not greater than 5, the message "The number is less than or equal to 5"
will be printed.
c. else-if ladder
An else-if ladder allows you to check multiple conditions. This is useful when you
have several possible choices and need to execute different blocks of code depending on
which condition is true.
Syntax:
if (condition1) {
// Block of code for condition1
} else if (condition2) {
// Block of code for condition2
} else {
// Block of code if none of the above conditions are true
}
Example:
public class Main {
public static void main(String[] args) {
int num = 10;

if (num > 10) {


System.out.println("The number is greater than 10");
} else if (num == 10) {
System.out.println("The number is equal to 10");
} else {
System.out.println("The number is less than 10");
}
}
}
In this case, the output will be "The number is equal to 10" because num is 10.
d. switch statement
The switch statement is used when you have multiple conditions based on the value of
a single variable. It is an alternative to using many else-if statements.
Syntax:
switch (expression) {
case value1:
// Block of code to execute if expression equals value1
break;
case value2:
// Block of code to execute if expression equals value2
break;
default:
// Block of code to execute if no cases match
}
 The break statement is used to terminate the switch statement.
 If no break is used, the program continues to execute subsequent cases (fall-through
behavior).
 The default case is optional and executes if none of the conditions match.
Example:
public class Main {
public static void main(String[] args) {
int day = 3;

switch (day) {
case 1:
System.out.println("Monday");
break;
case 2:
System.out.println("Tuesday");
break;
case 3:
System.out.println("Wednesday");
break;
case 4:
System.out.println("Thursday");
break;
case 5:
System.out.println("Friday");
break;
case 6:
System.out.println("Saturday");
break;
case 7:
System.out.println("Sunday");
break;
default:
System.out.println("Invalid day");
}
}
}
Here, the output will be "Wednesday" because day equals 3.
2. Looping Statements
Looping statements allow you to execute a block of code multiple times, based on a
condition.
a. for loop
The for loop is used when you know beforehand how many times you need to execute
a statement or a block of code.
Syntax:
for (initialization; condition; increment/decrement) {
// Block of code to be executed
}
 initialization: Initializes the loop counter.
 condition: Specifies the condition for the loop to continue executing.
 increment/decrement: Modifies the loop counter after each iteration.
Example:
public class Main {
public static void main(String[] args) {
for (int i = 1; i <= 5; i++) {
System.out.println("Iteration " + i);
}
}
}
This loop will print:
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
b. while loop
The while loop is used when you want to execute a block of code as long as a
specified condition is true.
Syntax:
while (condition) {
// Block of code to be executed
}
Example:
public class Main {
public static void main(String[] args) {
int i = 1;
while (i <= 5) {
System.out.println("Iteration " + i);
i++;
}
}
}
This loop will also print:
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
c. do-while loop
The do-while loop is similar to the while loop, except that it guarantees that the block
of code will execute at least once, regardless of whether the condition is true.
Syntax:
do {
// Block of code to be executed
} while (condition);
Example:
public class Main {
public static void main(String[] args) {
int i = 1;
do {
System.out.println("Iteration " + i);
i++;
} while (i <= 5);
}
}
This loop will also print:
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5
However, unlike the while loop, the do-while loop guarantees the code inside the
block is executed at least once.
3. Jump Statements
Jump statements allow you to control the flow of execution within loops or switch
statements by jumping to another part of the code.
a. break statement
The break statement is used to terminate the current loop or switch statement, and
transfer control to the next statement after the loop or switch block.
Example:
public class Main {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
if (i == 5) {
break; // Exits the loop when i equals 5
}
System.out.println(i);
}
}
}
The output will be:
1
2
3
4
b. continue statement
The continue statement is used to skip the current iteration of a loop and move to the
next iteration.
Example:
public class Main {
public static void main(String[] args) {
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue; // Skips the iteration when i equals 3
}
System.out.println(i);
}
}
}
The output will be:
1
2
4
5
c. return statement
The return statement is used to exit from the current method and optionally return a
value (if the method has a return type other than void).
Example:
public class Main {
public static void main(String[] args) {
System.out.println("Before return statement");
return; // Exits the main method
// The following code will not be executed
System.out.println("After return statement");
}
}
The output will be:
Before return statement

DYNAMIC METHOD DISPATCH IN JAVA


Dynamic Method Dispatch (also known as Runtime Polymorphism or Late
Binding) is a mechanism in Java where a method call is resolved at runtime rather than at
compile-time. It is a concept primarily associated with object-oriented programming (OOP)
and plays a key role in achieving polymorphism, which allows objects of different classes to
be treated as objects of a common superclass.
The core idea behind dynamic method dispatch is that Java determines which method
to call based on the actual object type (i.e., the type of the object being referred to by the
reference variable) at runtime, rather than the reference type used to declare the variable.
In Java, dynamic method dispatch is supported via method overriding, where a
subclass provides its own implementation of a method that is already defined in its
superclass. This behavior is dynamic because, at runtime, the JVM decides which version of
the overridden method should be executed.
Key Concepts Involved
a. Method Overriding
For dynamic method dispatch to occur, method overriding is necessary. This is when
a subclass provides a specific implementation of a method that is already defined in the
parent class. The overridden method in the subclass has the same signature (name, return
type, and parameters) as the method in the superclass.
b. Reference Variable vs. Object Type
 The reference variable is the one used to hold the object reference.
 The actual object is the object that the reference variable is pointing to.
 Dynamic method dispatch depends on the actual object type and not the reference
variable type.
How Dynamic Method Dispatch Works
At compile-time, Java knows the type of the reference variable, but at runtime, it
needs to decide which method to invoke based on the actual object that the reference variable
points to.
 When a method is called using a reference variable, the JVM first checks if the
method is overridden by the actual object type.
 If the method is overridden in the subclass, the JVM calls the subclass’s version of the
method.
 If the method is not overridden in the subclass, the JVM calls the superclass's version
of the method.
Steps for Dynamic Method Dispatch
1. Create a superclass with a method that is intended to be overridden.
2. Create a subclass that overrides the method of the superclass.
3. Use a reference variable of the superclass type to point to objects of both the
superclass and subclass.
4. Call the overridden method through the superclass reference, which points to an
object of the subclass.
5. The JVM decides which version of the method to call based on the actual object type
at runtime.
Example of Dynamic Method Dispatch
Let’s illustrate dynamic method dispatch with an example.
a. Superclass and Subclass Definitions
class Animal {
// Method in the superclass
public void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
// Overridden method in the subclass
@Override
public void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
// Overridden method in the subclass
@Override
public void sound() {
System.out.println("Cat meows");
}
}
b. Main Class Demonstrating Dynamic Dispatch
public class Main {
public static void main(String[] args) {
// Creating reference variables of type Animal, but pointing to subclass objects
Animal myAnimal = new Animal(); // Reference of Animal, object of Animal
Animal myDog = new Dog(); // Reference of Animal, object of Dog
Animal myCat = new Cat(); // Reference of Animal, object of Cat
// Calling the overridden method using the reference variables
myAnimal.sound(); // Output: Animal makes a sound
myDog.sound(); // Output: Dog barks
myCat.sound(); // Output: Cat meows
}
}
Why is Dynamic Method Dispatch Important?
Dynamic method dispatch allows for polymorphism, which enables you to write
more flexible and reusable code. This is especially useful in scenarios where the type of
object may not be known at compile-time, but you still want to invoke methods based on the
actual object at runtime.
Benefits of Dynamic Method Dispatch:
1. Flexibility: You can use references of a superclass type to hold objects of multiple
subclass types, which makes your code more flexible and modular.
2. Code Reusability: You can define common behaviors in a superclass and override
them in subclasses, allowing you to reuse the same method names with different
implementations.
3. Extensibility: As new subclasses are added to the system, dynamic dispatch ensures
that the correct method is invoked for new types of objects without needing to modify
existing code.
4. Polymorphism: The ability to treat objects of different classes in a common way
using the same superclass reference is a key advantage of polymorphism.

JVM ARCHITECTURE IN JAVA


The Java Virtual Machine (JVM) is a crucial component of the Java Runtime
Environment (JRE) that provides a platform-independent way of running Java programs. It is
responsible for executing Java bytecode and converting it into machine code that can be
understood by the underlying operating system. The JVM plays a vital role in achieving
Java's Write Once, Run Anywhere philosophy, as Java programs are compiled into
bytecode, which can run on any device or platform that has a JVM implementation.
Key Components of JVM
The JVM architecture is designed to be platform-independent. It abstracts the
underlying hardware and operating system, providing a uniform execution environment for
Java applications. It consists of several components that work together to load, verify, and
execute Java programs. The JVM architecture can be broken down into the following key
components:
1. Class Loader Subsystem
2. Runtime Data Areas
3. Execution Engine
4. Java Native Interface (JNI)
5. Garbage Collector
JVM Architecture Diagram

1. Class Loader Subsystem


The Class Loader is responsible for loading the compiled .class files (which contain
Java bytecode) into memory. It loads the class files into the JVM from the file system,
network, or other sources, and also manages the process of linking them to the program.
There are three types of class loaders in Java:
 Bootstrap Class Loader: Loads core Java libraries from the Java Runtime
Environment (JRE) such as rt.jar.
 Extension Class Loader: Loads Java classes from the JDK extensions directory.
 System Class Loader: Loads classes from the classpath, typically the classes you
write.
2. Runtime Data Areas
The JVM uses different data areas during the execution of a program. These areas are
used to store information that is used during the execution of Java programs.
a. Method Area
 Stores metadata about the classes, including information about methods, fields, and
method data.
 It is shared among all threads and contains class structures like the bytecode.
 It also stores the constant pool, which holds the constant values used in the program.
b. Heap
 The heap is where all objects and arrays are stored.
 It is the area in memory where dynamic memory allocation occurs.
 The heap is shared by all threads, and it is managed by the Garbage Collector for
memory cleanup.
c. Stack
 Each thread has its own stack.
 The stack stores method invocations, local variables, and partial results.
 Whenever a method is called, a stack frame is created in the stack, and when the
method finishes execution, the frame is popped off the stack.
d. Program Counter (PC) Register
 The PC register keeps track of the current instruction being executed by the thread.
 Every thread has its own PC register, which holds the address of the next instruction
to be executed for that thread.
e. Native Method Stack
 The native method stack is used to support native methods (written in languages like
C or C++) that are used by the Java program.
 The stack keeps track of method calls made to native libraries.
3. Execution Engine
The Execution Engine is the part of the JVM that actually runs the bytecode. It
consists of two main components:
a. Interpreter
 The Interpreter reads bytecode line by line and interprets it, meaning it converts the
bytecode into machine-specific instructions.
 This approach works but is typically slower because it doesn't translate bytecode into
native code ahead of time.
b. Just-In-Time (JIT) Compiler
 The JIT compiler is used to improve the performance of the JVM by compiling
bytecode into native machine code just before execution.
 It works by analyzing the bytecode and compiling it into machine code for the
specific platform, thus speeding up the execution of the program.
 The compiled machine code is cached so that the next time the same code is
encountered, the JVM uses the cached native code, improving performance.
4. Java Native Interface (JNI)
The JNI is an interface that allows Java code to interact with native applications and
libraries written in other languages such as C, C++, or assembly. This is necessary when Java
programs need to use platform-specific features or interact with existing non-Java code.
Example use of JNI:
 Java programs can call a method written in C or C++.
 Java can access hardware-specific features that are not available in standard Java
libraries.
5. Garbage Collector
The Garbage Collector (GC) is responsible for automatic memory management in Java.
It monitors objects that are no longer in use and deallocates memory to ensure efficient
memory usage. The garbage collector runs in the background and removes objects that are no
longer referenced.
 Mark-and-sweep is a typical garbage collection algorithm, which works in two
phases:
 Mark phase: The GC marks all objects that are still in use (i.e., reachable
objects).
 Sweep phase: The GC sweeps through the heap and deletes objects that are
not marked, freeing up memory.
There are several types of garbage collectors, including:
 Serial Garbage Collector: Uses a single thread for garbage collection.
 Parallel Garbage Collector: Uses multiple threads to collect garbage.
 CMS (Concurrent Mark and Sweep) Collector: Aims to minimize application
pause times.
 G1 Garbage Collector: Designed for large heaps with predictable pause time goals.
JVM Execution Flow
1. Java Code Compilation: Java source code (.java files) is compiled by the Java
compiler (javac) into bytecode (.class files).
2. Class Loading: The class loader subsystem loads the .class files into memory.
3. Method Resolution: The JVM looks up the necessary methods and classes and
ensures that they are available.
4. Bytecode Execution: The execution engine interprets or compiles the bytecode into
machine code, depending on whether the JIT compiler is used.
5. Memory Management: The garbage collector ensures that memory is efficiently
managed by clearing unused objects.
Advantages of JVM
1. Platform Independence: Java code is compiled into bytecode, which can run on any
system that has a JVM, making Java a "write once, run anywhere" language.
2. Automatic Memory Management: The garbage collector automatically manages
memory allocation and deallocation, reducing the risk of memory leaks.
3. Security: The JVM provides a secure execution environment by enforcing security
policies and preventing unauthorized access to system resources.
4. Optimized Performance: With the JIT compiler, Java performance is optimized for
the platform it is running on, improving speed and efficiency.

METHOD OVERLOADING AND METHOD OVERRIDING IN JAVA


In Java, Method Overloading and Method Overriding are two important concepts
related to polymorphism. They allow you to define multiple methods with the same name, but
the way they behave is different depending on the context. Both of these concepts are
essential for writing flexible, reusable, and maintainable code.
Let's go through each concept in detail, with examples, to understand their differences,
advantages, and use cases.
Method Overloading in Java
Method Overloading occurs when multiple methods in the same class have the same
name but differ in the following:
 Number of parameters.
 Type of parameters.
 Order of parameters.
Overloading is resolved at compile-time, which means the Java compiler determines
which method to call based on the number and type of arguments passed during the method
call. This is known as compile-time polymorphism or static polymorphism.
Rules for Method Overloading:
 The methods must have the same name.
 The method signature must be different. This can be achieved by:
 Changing the number of parameters.
 Changing the type of parameters.
 Changing the order of parameters.
 Return type can be different, but it is not considered when overloading.
Example of Method Overloading:
class Calculator {
// Method to add two integers
public int add(int a, int b) {
return a + b;
}
// Overloaded method to add three integers
public int add(int a, int b, int c) {
return a + b + c;
}

// Overloaded method to add two double numbers


public double add(double a, double b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
// Calling the first method
System.out.println("Sum of two integers: " + calc.add(10, 20));
// Calling the overloaded method (three integers)
System.out.println("Sum of three integers: " + calc.add(10, 20, 30));
// Calling the overloaded method (double numbers)
System.out.println("Sum of two doubles: " + calc.add(10.5, 20.5));
}
}
Output:
Sum of two integers: 30
Sum of three integers: 60
Sum of two doubles: 31.0
In this example:
 The add method is overloaded three times:
 One that takes two integers.
 One that takes three integers.
 One that takes two double values.
The Java compiler decides which version of add to call based on the method signature
at compile-time.
Benefits of Method Overloading:
1. Improves Code Readability: You can use the same method name for similar
operations, reducing the complexity of method names.
2. Flexibility: It allows methods to handle different types of inputs in a flexible way.
3. Ease of Maintenance: Since methods with similar functionality share the same name,
the code becomes easier to maintain.
METHOD OVERRIDING IN JAVA
Method Overriding occurs when a subclass provides a specific implementation of a
method that is already defined in its superclass. The overridden method in the subclass should
have the same signature (name, return type, and parameters) as the method in the superclass.
Overriding is resolved at runtime, which means the Java Virtual Machine (JVM)
decides which method to call based on the actual object type (not the reference type) during
method invocation. This is called runtime polymorphism or dynamic polymorphism.
Rules for Method Overriding:
 The method in the subclass must have the same name, return type (or a subtype), and
parameter list as the method in the superclass.
 The access level of the overridden method cannot be more restrictive than the original
method in the superclass (e.g., if the superclass method is public, the subclass method
must also be public).
 The @Override annotation is optional but recommended as it helps the compiler
detect errors.
Example of Method Overriding:
class Animal {
// Method in superclass
public void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
// Overridden method in subclass
@Override
public void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
// Overridden method in subclass
@Override
public void sound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Dog(); // Animal reference, Dog object
Animal animal2 = new Cat(); // Animal reference, Cat object

// Calling the overridden method


animal1.sound(); // Output: Dog barks
animal2.sound(); // Output: Cat meows
}
}
Output:
Dog barks
Cat meows
In this example:
 The sound() method is defined in the Animal class and is overridden in the Dog and
Cat subclasses.
 Although the reference type is Animal, the actual object type determines which
sound() method to call.
 animal1 points to a Dog object, so the sound() method of Dog is called.
 animal2 points to a Cat object, so the sound() method of Cat is called.
This is an example of runtime polymorphism. The JVM decides which method to
invoke based on the actual object type at runtime, not the reference type.
Benefits of Method Overriding:
1. Dynamic Polymorphism: Enables different classes to implement a common method
in their own way, thus enabling dynamic behavior.
2. Code Reusability: The subclass can reuse the code of the superclass and extend it to
provide specific behavior.
3. Flexibility: Allows subclasses to alter or extend the functionality of the superclass
methods.

Key Differences Between Method Overloading and Method Overriding

Feature Method Overloading Method Overriding

Providing a specific implementation of


Defining multiple methods with the
Definition a method that is already defined in the
same name but different parameters.
superclass.

Binding Compile-time polymorphism Runtime polymorphism (resolved at


Time (resolved at compile-time). runtime).

Method signature must differ Method signature must be exactly the


Method
(number/type/sequence of same (name, return type, and
Signature
parameters). parameters).

Return type must be the same (or


Return Type Return type can be different.
subtype).

Overridden method cannot have a more


Access
No restriction on access modifier. restrictive access modifier than the
Modifier
superclass method.

Not necessary, can happen within the Requires inheritance (subclass inherits
Inheritance
same class. from superclass).

To perform similar operations with To define specific behavior for a


Purpose
different inputs. method in a subclass.

 Method Overloading allows you to define multiple methods with the same name but
with different parameters. It is resolved at compile-time and provides a way to use the
same method name for different types or numbers of arguments.
 Method Overriding, on the other hand, is used to provide a specific implementation
of a method that is already defined in a superclass. It is resolved at runtime and is
essential for achieving polymorphic behavior in Java.
Both techniques are important tools for writing clean, maintainable, and flexible code,
allowing methods to behave differently based on the context (inputs or objects).

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy