0% found this document useful (0 votes)
2 views

Module-2

Uploaded by

subhani.shaik
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

Module-2

Uploaded by

subhani.shaik
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 20

Module-3

Inheritance: Inheritance Hierarchies, super keyword, final keyword, final classes, and
methods. Polymorphism: dynamic binding, method overriding. Abstraction: abstract
classes and methods. The Object class– Object Cloning – Inner Classes- Garbage
Collection - Finalize Method.

What is Inheritance?
Inheritance allows a class (called a subclass or derived class) to acquire properties and
behaviors (methods) from another class (called a superclass or base class). This mechanism
promotes code reusability and establishes a natural hierarchy between classes.
Example:
class Animal {
void eat() {
System.out.println("This animal eats food.");
}
}

// Subclass
class Dog extends Animal {
void bark() {
System.out.println("The dog barks.");
}
}

Benefits of Inheritance
 Code Reusability
Inheritance enables classes to reuse code from existing classes, reducing redundancy and
promoting efficiency. This not only saves time but also minimizes the chances of errors that
might occur when rewriting similar code.
 Modularity and Maintainability
By organizing code into a hierarchy of classes, inheritance promotes a modular approach to
software design. This makes the code easier to understand, maintain, and modify. Changes
made to a base class automatically propagate to all its subclasses, reducing the effort required
for maintenance and updates.
 Extensibility
New functionality can be added to existing code without modifying it, adhering to the open-
closed principle of SOLID. This makes it easier to extend the capabilities of a system without
risking the stability of existing code.
 Polymorphism
Inheritance is the foundation for polymorphism, allowing objects of different classes to be
treated as objects of a common base class. This enables more flexible and dynamic code.
 Logical Hierarchy and Organization
Inheritance helps in creating a logical structure for your code, mirroring real-world
relationships between objects. This can make the overall design of a system more intuitive
and easier to understand.
 Reduced Redundancy
Since child classes inherit features from the parent class, there is less need to write the same
code repeatedly. This reduces redundancy and keeps the code cleaner and more concise.
 Faster Development
Because much of the code is reused from the superclass, development can be faster. You don't
have to write the same code multiple times; instead, you write it once in the superclass and
inherit it in the subclasses.

Drawbacks of Inheritance
1. Tight Coupling Between Classes
Inheritance creates a strong dependency between parent and child classes. Changes in
the superclass can inadvertently affect all subclasses, making the system less flexible
and harder to maintain.
2. Increased Complexity
Deep or extensive inheritance hierarchies can make codebases more complex and
harder to understand. Developers may struggle to trace the flow of execution or
determine where specific behaviors are defined.
3. Fragile Base Class Problem
Modifications to a base class can unintentionally break the functionality of derived
classes, especially if those subclasses rely on the base class's implementation details.
This issue is known as the "fragile base class" problem.
4. Violation of Encapsulation
Inheritance can expose the internal workings of a class to its subclasses, potentially
violating the principle of encapsulation. Subclasses may access or modify protected
members of the superclass, leading to unintended side effects.
5. Reduced Flexibility and Reusability
Once a subclass inherits from a superclass, it inherits all of its properties and methods,
even those that may not be relevant. This can lead to subclasses carrying unnecessary
baggage, reducing flexibility and reusability.
6. Overriding Issues
While method overriding allows subclasses to provide specific implementations, it
can also introduce bugs if not done carefully. Overriding methods without a clear
understanding of the superclass's behavior can lead to inconsistent or unexpected
outcomes.
7. Performance Overhead
Inheritance can introduce performance overhead due to dynamic method dispatch and
increased memory usage, especially in languages that support multiple inheritance or
deep hierarchies.
8. Difficulty in Refactoring
Refactoring code that uses inheritance can be challenging. Changes to a superclass
may necessitate changes in all subclasses, increasing the risk of introducing bugs
during the refactoring process.

Inheritance Hierarchies
An inheritance hierarchy is a tree-like structure that represents the relationships between
classes in OOP. At the top is the most general class, and as you move down, classes become
more specialized.

Object
└── Animal
├── Mammal
│ └── Dog
└── Bird
└── Sparrow
In this hierarchy:
 Dog is a subclass of Mammal, which is a subclass of Animal.
 Each subclass inherits attributes and methods from its superclass.
Benefits of Using Inheritance Hierarchies
 Code Reusability: Common code can be written in the superclass and reused in
subclasses.
 Improved Maintainability: Changes in the superclass automatically propagate to
subclasses.
 Logical Structure: Reflects real-world relationships and promotes organized code.
Drawbacks
 Tight Coupling: Subclasses are dependent on superclasses, which can lead to issues if
the superclass changes.
 Complexity: Deep inheritance hierarchies can become difficult to manage and
understand.
 Limited Flexibility: In some cases, composition (having classes contain instances of
other classes) might be more appropriate than inheritance.

Types of Inheritance
 Single Inheritance: A subclass inherits from one superclass.
 Example: class Dog extends Animal
// Superclass
class Animal {
void eat() {
System.out.println("This animal eats food.");
}
}

// Subclass
class Dog extends Animal {
void bark() {
System.out.println("The dog barks.");
}
}

// Main class to test the inheritance


public class TestSingleInheritance {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // Inherited method from Animal class
dog.bark(); // Method from Dog class
}
}
//This animal eats food.
The dog barks.

 Multilevel Inheritance: A subclass inherits from a superclass, and then another subclass
inherits from that subclass.
 Example: class Dog extends Mammal, class Mammal extends Animal

// Base class
class Animal {
void eat() {
System.out.println("This animal eats food.");
}
}

// Intermediate subclass
class Dog extends Animal {
void bark() {
System.out.println("The dog barks.");
}
}

// Derived subclass
class Puppy extends Dog {
void weep() {
System.out.println("The puppy weeps.");
}
}
// Main class to test the inheritance
public class TestMultilevelInheritance {
public static void main(String[] args) {
Puppy puppy = new Puppy();
puppy.eat(); // Inherited from Animal
puppy.bark(); // Inherited from Dog
puppy.weep(); // Defined in Puppy
}
}

// output: This animal eats food.


The dog barks.
The puppy weeps.

This demonstrates multilevel inheritance, where Puppy inherits from Dog, which in turn
inherits from Animal. As a result, an instance of Puppy has access to methods from all three
classes.

 Hierarchical Inheritance: Multiple subclasses inherit from a single superclass.


 Example: class Dog extends Animal, class Cat extends Animal
// Superclass
class Animal {
void eat() {
System.out.println("This animal eats food.");
}
}

// Subclass 1
class Dog extends Animal {
void bark() {
System.out.println("The dog barks.");
}
}
// Subclass 2
class Cat extends Animal {
void meow() {
System.out.println("The cat meows.");
}
}

// Main class to test the inheritance


public class TestHierarchicalInheritance {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // Inherited method from Animal class
dog.bark(); // Method from Dog class

Cat cat = new Cat();


cat.eat(); // Inherited method from Animal class
cat.meow(); // Method from Cat class
}
}
OUTPUT: This animal eats food.
The dog barks.
This animal eats food.
The cat meows.
This demonstrates hierarchical inheritance, where multiple subclasses (Dog and Cat) inherit
from a single superclass (Animal). Each subclass gains access to the methods of the
superclass while introducing its own specific behaviors.

 Multiple Inheritance: A subclass inherits from more than one superclass.

Multiple inheritance is a feature in object-oriented programming where a class can inherit


characteristics and behaviors from more than one parent class. While this allows for greater
flexibility and code reuse, it also introduces complexities such as ambiguity in method
resolution.
The super Keyword: The super keyword in Java is a reference variable used within a
subclass to refer to its immediate parent class. It's instrumental in accessing superclass
members (methods, variables, and constructors) that may be hidden or overridden in the
subclass.
Example
class Animal {
void eat() {
System. out.println("This animal eats food");
}
}
class Dog extends Animal {
void eat() {
super.eat(); // Calls the eat method of Animal
System.out.println("This dog eats bones");
}
}

Output: This animal eats food


This dog eats bones

The final Keyword


1. Final Variables
A variable declared as final can be assigned a value only once. Once initialized, its value
cannot be changed, making it a constant.
Example:
final int MAX_SPEED = 120;
MAX_SPEED = 150; // Compile-time error: cannot assign a value to a final variable

2. Final Methods
A method declared as final cannot be overridden by subclasses. This ensures that the
method's behavior remains consistent across all instances.
Example:
class Animal {
public final void makeSound() {
System.out.println("Animal makes a sound");
}
}

class Dog extends Animal {


@Override
public void makeSound() { // Compile-time error: cannot override the final method from
Animal
System.out.println("Dog barks");
}
}

3. Final Classes
A class declared as final cannot be subclassed. This is useful for creating immutable classes
or preventing inheritance for security or design reasons.
Example:
public final class MathUtility {
public static final double PI = 3.14159;
}
Benefits of Using final
 Immutability: Ensures that variables, methods, or classes cannot be modified after
their initial definition, promoting consistency and reliability.
 Security: Prevents unauthorized changes to critical code components, enhancing
security.
 Performance Optimization: Allows the Java Virtual Machine (JVM) to optimize
code more effectively, as it can make assumptions about the immutability of final
elements.
 Design Clarity: Conveys the developer's intent, making the code easier to understand
and maintain.
Polymorphism:
Polymorphism is a fundamental concept in Object-Oriented Programming (OOP) that allows
objects of different classes to be treated as instances of a common superclass. It enables a
single interface to represent different underlying forms (data types), enhancing flexibility and
reusability in code.
Types of Polymorphism
1. Compile-Time Polymorphism (Static Binding)
Also known as method overloading, compile-time polymorphism occurs when multiple
methods have the same name but differ in parameters (number, type, or order). The method to
be invoked is determined at compile time.
Example:
class Calculator {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}
In this example, the add method is overloaded to handle both integer and double parameters.

2. Runtime Polymorphism (Dynamic Binding)


Runtime polymorphism is achieved through method overriding, where a subclass provides a
specific implementation of a method that is already defined in its superclass. The method to
be invoked is determined at runtime based on the object's actual type.
Example:
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
myDog.sound(); // Outputs: Dog barks
}
}
Here, the sound method is overridden in the Dog class. Even though myDog is of type
Animal, it refers to a Dog object, and the overridden method in Dog is called at runtime.

Benefits of Polymorphism
 Code Reusability: Write generic code that works with objects of different classes.
 Extensibility: Easily add new classes without modifying existing code.
 Maintainability: Simplifies code updates and maintenance by centralizing common
behavior.
 Flexibility: Allows for dynamic method invocation, enabling more adaptable code
structures.

Method overriding
In method overriding, a subclass defines a method with the same signature (name, return
type, and parameter list) as a method in its superclass. The overriding method in the subclass
provides a specific implementation that replaces the superclass's version when invoked on an
object of the subclass.

Key Characteristics:
 Same Method Signature: The method in the subclass must have the same name,
return type, and parameters as the method in the superclass.
 Inheritance Required: Method overriding occurs between a superclass and a
subclass.
 Runtime Polymorphism: The method to be invoked is determined at runtime based
on the object's actual type.
Example
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Animal();
myAnimal.sound(); // Outputs: Animal makes a sound

Animal myDog = new Dog();


myDog.sound(); // Outputs: Dog barks
}
}
In this example, the Dog class overrides the sound() method of the Animal class. When
sound() is called on an Animal reference that points to a Dog object, the Dog's version of
sound() is executed, demonstrating runtime polymorphism.

Abstraction- abstract classes and methods


Abstraction in Java is a fundamental concept in object-oriented programming that focuses on
hiding the complex implementation details and showing only the essential features of an
object. This is primarily achieved using abstract classes and abstract methods.

Abstraction allows developers to define the structure of classes and methods without
providing complete implementations. It helps in managing complexity by exposing only
relevant details to the user and hiding the internal workings.

Abstract Classes
An abstract class is a class that cannot be instantiated on its own and is declared using the
abstract keyword. It may contain both abstract methods (without implementation) and
concrete methods (with implementation).
Key Features:
 Cannot be instantiated: You cannot create objects of an abstract class directly.
 Can contain constructors: Abstract classes can have constructors, which are called
when a subclass is instantiated.
 Can have member variables and methods: They can include fields and methods
with or without implementations.
 Supports inheritance: Other classes can extend abstract classes and provide
implementations for abstract methods.

Example
abstract class Animal {
abstract void makeSound();

public void eat() {


System.out.println("This animal eats food.");
}
}
In this example, Animal is an abstract class with one abstract method makeSound(), and one
concrete method, eat().
Abstract Methods
An abstract method is a method declared without an implementation. It serves as a
placeholder for methods that must be implemented by subclasses.

Characteristics:
 No method body: Abstract methods end with a semicolon and do not have a body.
 Must be in abstract classes: They can only be declared within abstract classes.
 Must be overridden: Subclasses are required to provide concrete implementations
for all abstract methods.

abstract class Animal {


abstract void makeSound();
}
Here, makeSound() is an abstract method that must be implemented by any non-abstract
subclass of Animal.
Implementing Abstract Classes and Methods
To use an abstract class, you need to create a subclass that extends it and provides
implementations for all its abstract methods.

Example:
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Bark");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.makeSound(); // Outputs: Bark
dog.eat(); // Outputs: This animal eats food.
}
}
In this example, Dog extends the abstract class Animal and provides an implementation for
the makeSound() method.

Benefits of Using Abstraction


 Code Reusability: Abstract classes allow you to define common behaviors that can
be reused across multiple subclasses.
 Improved Maintainability: By separating the definition of methods from their
implementation, code becomes easier to manage and update.
 Enhanced Security: Hiding implementation details prevents external entities from
accessing sensitive parts of the code.
 Facilitates Polymorphism: Abstract classes enable polymorphic behavior, allowing
objects to be treated as instances of their abstract superclass.

Abstract Classes vs. Interfaces

While both abstract classes and interfaces are used to achieve abstraction, they have distinct
differences:

Abstract Classes:
 Can have both abstract and concrete methods.
 Can have member variables with any access modifier.
 Supports constructors.
 A class can extend only one abstract class.

Interfaces:

 All methods are implicitly abstract (until Java 8, which introduced default and static
methods).
 All variables are implicitly public, static, and final.
 Cannot have constructors.
 A class can implement multiple interfaces.

Choose abstract classes when you want to share code among several closely related classes,
and interfaces when you want to define a contract that can be implemented by unrelated
classes.
Object class
In Java, the Object class serves as the ultimate superclass for all other classes. Located in the
java.lang package, it provides a set of fundamental methods that are inherited by every Java
class, either directly or indirectly. Understanding these methods is crucial for effective Java
programming.

Key Methods of the Object Class


1. equals(Object obj)
o Purpose: Determines whether two objects are considered equal.
o Default Behavior: Compares memory addresses (reference equality).
o Typical Override: To compare object contents (logical equality).

Example:
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
MyClass other = (MyClass) obj;
return this.id == other.id;
}
hashCode()
 Purpose: Returns an integer hash code value for the object.
 Usage: Essential for objects used in hash-based collections like HashMap or HashSet.
 Contract with equals(): Equal objects must have the same hash code.
Example:
@Override
public int hashCode() {
return Objects.hash(id);
}
3.toString()
 Purpose: Provides a string representation of the object.
 Default Behavior: Returns a string consisting of the class name followed by the
object's hash code.
 Typical Override: To return a more informative and readable string.
Example:
@Override
public String toString() {
return "MyClass{id=" + id + "}";
}
4.getClass()
 Purpose: Returns the runtime class of the object.
 Usage: Useful for reflection and obtaining class metadata.
Example:
Class<?> clazz = myObject.getClass();
System.out.println("Class name: " + clazz.getName());
5. clone()
Purpose: Creates and returns a copy of the object.
Requirements:
 The class must implement the Cloneable interface.
 Override the clone() method with public access.

 Default Behavior: Performs a shallow copy.

Example:

@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
6. finalize()
Purpose: Called by the garbage collector before reclaiming the object's memory.
Note: This method is deprecated in Java 9 and later due to unpredictability and performance
issues.

7.wait(), notify(), notifyAll()


Purpose: Used for thread synchronization.
Usage:
 wait(): Causes the current thread to wait until another thread invokes notify() or
notifyAll().
 notify(): Wakes up a single thread waiting on the object's monitor.
 notifyAll(): Wakes up all threads waiting on the object's monitor.
Significance of the Object Class
 Universal Superclass: Every class in Java inherits from Object, ensuring a consistent
set of methods across all objects.
 Foundation for Collections: Methods like equals() and hashCode() are vital for the
correct functioning of collections.
 Facilitates Debugging: Overriding toString() provides meaningful information
during debugging.
 Enables Reflection: getClass() is essential for introspecting objects at runtime.

inner classes
In Java, inner classes are classes defined within another class. They enable logical grouping
of classes that are only used in one place, enhancing encapsulation and readability. Java
supports several types of inner classes, each serving distinct purposes.
Types of Inner Classes in Java
1. Member Inner Class
A non-static class defined within another class.
 Access: Can access all members (including private) of the outer class.
 Instantiation:
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
2. Static Nested Class
A static class defined within another class.
 Access: Can access only static members of the outer class.
 Instantiation:
OuterClass.StaticNestedClass nested = new OuterClass.StaticNestedClass();

3. Local Inner Class


A class defined within a method or a block.
 Scope: Accessible only within the method or block where it's defined.
 Example:
void method() {
class LocalInner {
void display() {
System.out.println("Inside Local Inner Class");
}
}
LocalInner local = new LocalInner();
local.display();
}
4. Anonymous Inner Class
A class without a name, used for instantiating classes or interfaces.
 Usage: Often used for event handling or implementing interfaces on the fly.
 Example:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Anonymous Inner Class");
}
};
new Thread(r).start();

Advantages of Using Inner Classes


 Encapsulation: Helps in logically grouping classes that are only used in one place.
 Namespace Management: Reduces namespace clutter by keeping related classes
together.
 Access to Outer Class Members: Inner classes can access members of the outer
class, facilitating better integration.
garbage collection (GC)
In Java, garbage collection (GC) is an automatic process managed by the Java Virtual
Machine (JVM) to reclaim memory by removing objects that are no longer in use. This helps
prevent memory leaks and optimizes application performance.

How Garbage Collection Works


Java's garbage collection typically follows a generational approach, dividing the heap into
different areas based on object lifespan:
Young Generation: Where new objects are allocated. It consists of:
 Eden Space: Most objects are initially allocated here.
 Survivor Spaces (S0 and S1): Objects that survive garbage collection in Eden are
moved here.

 Old Generation (Tenured Generation): Holds objects that have survived multiple
garbage collection cycles in the Young Generation.

 Permanent Generation (PermGen): Used in earlier Java versions to store metadata about
classes. Replaced by Metaspace in Java 8 and later.

The garbage collection process involves several steps:


1. Mark: Identify all reachable objects starting from "GC roots" (e.g., local variables,
static fields).
2. Sweep: Remove objects that are not marked as reachable.
3. Compact: Rearrange the remaining objects to eliminate memory fragmentation.
This process helps in efficiently managing memory and ensuring that unused objects do not
consume valuable resources.

finalize() method
In Java, the finalize() method was traditionally used to perform cleanup operations before an
object was reclaimed by the garbage collector. However, its usage has been fraught with
issues, leading to its deprecation in Java 9 and plans for removal in future releases.
What is finalize()?
The finalize() method is defined in the Object class and is invoked by the garbage collector
before an object is destroyed. It was intended for releasing resources such as file handles or
network connections.

@Override
protected void finalize() throws Throwable {
try {
// Cleanup operations
} finally {
super.finalize();
}
}
Why finalize() Is Deprecated
The deprecation of finalize() stems from several inherent problems:
 Unpredictable Timing: There's no guarantee when, or even if, finalize() will be
executed, making resource management unreliable.
 Performance Issues: Objects with finalizers take longer to be garbage collected,
leading to potential memory leaks and performance degradation.
 Security Risks: Finalizers can be exploited to resurrect objects, posing security
vulnerabilities.
 Complexity and Errors: Overriding finalize() correctly is challenging, and mistakes
can lead to subtle bugs.
Due to these issues, the Java community recommends avoiding finalize() in favor of more
reliable resource management techniques.

You might also like