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

gfgfgbfb

The document provides an overview of Object-Oriented Programming (OOP) principles including encapsulation, abstraction, inheritance, and polymorphism, along with their implementations and examples. It also discusses friend functions in C++, constructors, and the differences between compile-time and runtime polymorphism, as well as various types of inheritance. The content emphasizes the importance of these concepts in building modular and reusable software systems.
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)
4 views

gfgfgbfb

The document provides an overview of Object-Oriented Programming (OOP) principles including encapsulation, abstraction, inheritance, and polymorphism, along with their implementations and examples. It also discusses friend functions in C++, constructors, and the differences between compile-time and runtime polymorphism, as well as various types of inheritance. The content emphasizes the importance of these concepts in building modular and reusable software systems.
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/ 43

UNIT 1

Object-Oriented Programming (OOP) revolves around designing software based on real-world entities,
organizing them into objects that interact with one another. The four main pillars of OOP are:

1. Encapsulation

Encapsulation is the bundling of data (attributes) and methods (functions) that operate on the data into a single
unit, typically a class. Access to the internal details is restricted, which helps enforce a clear boundary
between an object’s internal state and its external interactions.

• Goal: Protect the integrity of the object and prevent unauthorized access or modification.

• Implementation:

o Use access specifiers such as private, protected, and public to control visibility.

o Provide getter and setter methods to allow controlled access to private variables.

Example:

class BankAccount {

private double balance; // Encapsulated field

public double getBalance() { // Getter

return balance;

public void deposit(double amount) { // Setter with validation

if (amount > 0) {

balance += amount;

2. Abstraction

Abstraction focuses on showing only the essential features of an object while hiding the underlying
implementation details. It simplifies the complexity of the system by only exposing the relevant aspects.

• Goal: Reduce complexity and increase efficiency by focusing on "what" an object does rather than
"how" it does it.

• Implementation:

o Use abstract classes and interfaces to define common functionality while leaving
implementation to derived classes.

Example:
abstract class Shape {

abstract void draw(); // Abstract method

class Circle extends Shape {

@Override

void draw() {

System.out.println("Drawing a circle");

3. Inheritance

Inheritance allows one class (child/subclass) to acquire the properties and behaviors of another class
(parent/superclass). It promotes code reuse and establishes a natural hierarchy between classes.

• Goal: Avoid code duplication and establish a relationship between objects.

• Implementation: Use the extends keyword (in Java) or similar in other languages to inherit.

Example:

class Animal {

void eat() {

System.out.println("This animal eats food.");

class Dog extends Animal {

void bark() {

System.out.println("The dog barks.");

Usage:

Dog dog = new Dog();

dog.eat(); // Inherited from Animal

dog.bark();

4. Polymorphism
Polymorphism allows objects to take on multiple forms. It enables a single interface or method to work in
different ways depending on the context, promoting flexibility and scalability.

• Goal: Allow a single interface or method to behave differently based on the object it operates on.

• Types:

1. Compile-time Polymorphism (Method Overloading):

▪ Multiple methods with the same name but different parameter lists.

2. Runtime Polymorphism (Method Overriding):

▪ Subclass provides a specific implementation of a method defined in the superclass.

Example (Overloading):

class Calculator {

int add(int a, int b) {

return a + b;

double add(double a, double b) {

return a + b;

Example (Overriding):

class Animal {

void sound() {

System.out.println("Some generic animal sound");

class Cat extends Animal {

@Override

void sound() {

System.out.println("Meow");

Usage:

Animal myAnimal = new Cat();

myAnimal.sound(); // Calls Cat's overridden method


By combining these four principles, OOP helps developers build modular, reusable, and scalable software
systems.

Friend Function
In C++, a friend function is a special function that is not a member of a class but has the right to access private
and protected members of that class. Declaring a function as a friend allows it to bypass the encapsulation
rules that normally restrict access to a class's internal details.

Key Points:

1. Declaration:

o A friend function is declared within the class using the friend keyword but is defined outside the
class.

o The declaration in the class simply tells the compiler that the function is a friend, not a member.

2. Access to Members:

o A friend function can access private and protected members of the class it is declared a friend
of.

3. Usage:

o It is commonly used for operator overloading, where the operator function needs access to
private members of a class.

o When two or more classes need to access each other's private or protected members.

Syntax:

#include <iostream>

using namespace std;

class MyClass {

private:

int privateData;

public:

MyClass(int val) : privateData(val) {}

// Declare the friend function

friend void display(const MyClass& obj);

};

// Define the friend function

void display(const MyClass& obj) {


// Accessing private member

cout << "Private Data: " << obj.privateData << endl;

int main() {

MyClass obj(42);

display(obj); // Call the friend function

return 0;

Output:

Private Data: 42

Characteristics:

1. Not Member Functions:

o A friend function is not called using an object of the class. It is a standalone function.

2. Defined Outside the Class:

o The function definition is typically outside the class unless it's a member of another class or a
part of the same file.

3. Two-Way Access:

o If a friend function of one class accesses another class's private data, that other class must
also explicitly declare it as a friend.

4. Global or Part of Another Class:

o Friend functions are either global functions or part of another class.

Example: Friend Function for Operator Overloading

#include <iostream>

using namespace std;

class MyClass {

private:

int value;

public:

MyClass(int v) : value(v) {}

// Friend function for adding two objects

friend MyClass operator+(const MyClass& a, const MyClass& b);


};

// Define the friend function

MyClass operator+(const MyClass& a, const MyClass& b) {

return MyClass(a.value + b.value);

int main() {

MyClass obj1(10), obj2(20);

MyClass result = obj1 + obj2;

cout << "Result: " << result.value << endl; // Access private member directly is not possible

return 0;

Explanation:

• The operator+ is defined as a friend to access the private value of the MyClass objects.

Friend functions are a powerful feature but should be used judiciously to maintain proper encapsulation and
avoid breaking the design principles of object-oriented programming.

Constructors
Here’s a concise explanation of the default constructor, parameterized constructor, copy constructor, and
assignment operator constructor in C++:

1. Default Constructor

• Definition: A constructor with no parameters or all parameters having default values.

• Purpose: Initializes objects with default values.

• Compiler-Provided Default: If no constructor is defined, the compiler provides a default constructor.

Example:

class MyClass {

public:

int x;

MyClass() { // Default constructor

x = 0; // Initialize default value

};

MyClass obj; // Calls the default constructor


2. Parameterized Constructor

• Definition: A constructor that takes arguments to initialize objects with specific values.

• Purpose: Enables passing custom values at the time of object creation.

Example:

class MyClass {

public:

int x;

MyClass(int value) { // Parameterized constructor

x = value;

};

MyClass obj(10); // Passes 10 to initialize 'x'

3. Copy Constructor

• Definition: A constructor that creates a new object as a copy of an existing object.

• Syntax: ClassName(const ClassName &obj)

• Purpose: Used for deep copying or when objects are passed by value.

• Compiler-Provided Copy Constructor: If not explicitly defined, the compiler generates a default one
that performs a shallow copy.

Example:

class MyClass {

public:

int x;

MyClass(int value) { x = value; } // Parameterized constructor

MyClass(const MyClass &obj) { x = obj.x; } // Copy constructor

};

MyClass obj1(10); // Calls parameterized constructor

MyClass obj2(obj1); // Calls copy constructor

4. Assignment Operator Constructor

• Definition: Not a constructor, but an operator (operator=) that assigns values from one object to
another.
• Purpose: Used for assigning one existing object to another of the same class.

• Difference from Copy Constructor: The copy constructor initializes a new object, while the
assignment operator updates an existing object.

• Compiler-Provided Assignment Operator: If not explicitly defined, the compiler generates a shallow
copy implementation.

Example:

class MyClass {

public:

int x;

MyClass(int value) { x = value; } // Parameterized constructor

MyClass& operator=(const MyClass &obj) { // Assignment operator

x = obj.x;

return *this;

};

MyClass obj1(10); // Calls parameterized constructor

MyClass obj2 = obj1; // Calls copy constructor

obj2 = obj1; // Calls assignment operator

Summary Table:

Constructor Purpose Called When

Default Constructor Initializes objects with defaults Object is created without arguments.

Parameterized Constructor Initializes objects with custom Object is created with arguments.
values

Copy Constructor Creates a new object as a A new object is created as a copy of an


copy existing one.

Assignment Constructor Assigns values to an existing = operator is used between two existing
(Operator) object objects.

UNIT 2

In C++, runtime polymorphism and compile-time polymorphism are two forms of polymorphism that allow
functions or methods to behave differently based on their usage. Here's a detailed explanation of both:
1. Compile-time Polymorphism

Compile-time polymorphism, also known as early binding, happens when the function call is resolved at
compile time. This is achieved through:

• Function Overloading: Multiple functions with the same name but different parameter lists.

• Operator Overloading: Defining custom behavior for operators for user-defined types.

Characteristics:

• Resolved at compile time.

• Improves performance due to early resolution.

• Limited flexibility because the exact method to call must be known at compile time.

Example of Function Overloading:

#include <iostream>

using namespace std;

class Calculator {

public:

int add(int a, int b) {

return a + b;

double add(double a, double b) {

return a + b;

};

int main() {

Calculator calc;

cout << "Integer addition: " << calc.add(5, 3) << endl; // Calls add(int, int)

cout << "Double addition: " << calc.add(2.5, 3.1) << endl; // Calls add(double, double)

return 0;

Example of Operator Overloading:

#include <iostream>

using namespace std;


class Complex {

public:

double real, imag;

Complex(double r, double i) : real(r), imag(i) {}

// Overloading + operator

Complex operator+(const Complex& other) {

return Complex(real + other.real, imag + other.imag);

void display() {

cout << real << " + " << imag << "i" << endl;

};

int main() {

Complex c1(1.2, 2.3), c2(3.4, 4.5);

Complex c3 = c1 + c2; // Calls operator+()

c3.display();

return 0;

2. Runtime Polymorphism

Runtime polymorphism, also known as late binding, occurs when the function call is resolved at runtime. This
is achieved through:

• Function Overriding: A derived class provides a specific implementation of a virtual function defined in
the base class.

• Virtual Functions: Functions marked with the virtual keyword in the base class to enable late binding.

Characteristics:

• Resolved at runtime using vtable (virtual table).

• Provides flexibility because the exact method to call depends on the actual type of the object at
runtime.

• Requires pointers or references to invoke overridden methods.

Example of Runtime Polymorphism:


#include <iostream>

using namespace std;

class Animal {

public:

virtual void sound() { // Virtual function

cout << "Animal makes a sound" << endl;

virtual ~Animal() {} // Virtual destructor

};

class Dog : public Animal {

public:

void sound() override { // Overriding base class method

cout << "Dog barks" << endl;

};

class Cat : public Animal {

public:

void sound() override {

cout << "Cat meows" << endl;

};

int main() {

Animal* animal; // Pointer to base class

Dog dog;

Cat cat;

animal = &dog;

animal->sound(); // Calls Dog's sound()

animal = &cat;
animal->sound(); // Calls Cat's sound()

return 0;

Key Points:

• Without the virtual keyword, the method of the base class would always be called, regardless of the
object type.

• Polymorphism is achieved dynamically through virtual tables and pointers.

Differences Between Compile-time and Runtime Polymorphism:

Feature Compile-time Polymorphism Runtime Polymorphism

Binding Time Resolved at compile time Resolved at runtime

Mechanism Function/Operator overloading Function overriding, virtual functions

Flexibility Limited, determined at compile time Flexible, determined at runtime

Performance Faster due to early binding Slightly slower due to late binding (vtable
lookup)

Example Function overloading, operator Virtual functions and overriding


overloading

By combining these forms, C++ supports both performance and flexibility, catering to different programming
needs.

UNIT 3

Inheritance
Inheritance in C++ is a mechanism that allows one class (called the derived class) to inherit properties and
behavior (data members and member functions) from another class (called the base class). This promotes
code reuse and establishes a relationship between classes. There are several types of inheritance in C++, each
serving a specific purpose. Here's an overview:

1. Single Inheritance

• Definition: A derived class inherits from only one base class.

• Example:
class Base {
public:
void show() {
cout << "Base class" << endl;
}
};

class Derived : public Base {


public:
void display() {
cout << "Derived class" << endl;
}
};

• Use Case: Useful when there is a straightforward relationship between two classes.

2. Multiple Inheritance

• Definition: A derived class inherits from two or more base classes.

• Example:
class Base1 {
public:
void show() {
cout << "Base1 class" << endl;
}
};

class Base2 {
public:
void display() {
cout << "Base2 class" << endl;
}
};

class Derived : public Base1, public Base2 {


public:
void print() {
cout << "Derived class" << endl;
}
};
• Use Case: Useful when a derived class needs to inherit features from multiple sources.

• Note: This can lead to ambiguities (e.g., Diamond Problem).

3. Multilevel Inheritance

• Definition: A derived class acts as a base class for another class.

• Example:
class Base {
public:
void show() {
cout << "Base class" << endl;
}
};

class Intermediate : public Base {


public:
void display() {
cout << "Intermediate class" << endl;
}
};

class Derived : public Intermediate {


public:
void print() {
cout << "Derived class" << endl;
}
};

• Use Case: Useful for representing hierarchical relationships.

4. Hierarchical Inheritance

• Definition: Multiple derived classes inherit from a single base class.

• Example:
class Base {
public:
void show() {
cout << "Base class" << endl;
}
};

class Derived1 : public Base {


public:
void display1() {
cout << "Derived1 class" << endl;
}
};

class Derived2 : public Base {


public:
void display2() {
cout << "Derived2 class" << endl;
}
};

• Use Case: Useful when multiple classes share common features.

5. Hybrid (Virtual) Inheritance

• Definition: A combination of multiple and multilevel inheritance to avoid duplication of data from the
base class.

• Example: Solves the Diamond Problem using virtual inheritance.


class Base {
public:
void show() {
cout << "Base class" << endl;
}
};

class Intermediate1 : virtual public Base {};


class Intermediate2 : virtual public Base {};

class Derived : public Intermediate1, public Intermediate2 {


public:
void print() {
cout << "Derived class" << endl;
}
};

• Use Case: Ensures proper data sharing in complex inheritance hierarchies.

Types of Access Specifiers in Inheritance

When inheriting a class, the access specifier (public, protected, or private) determines how the base class
members are inherited:

• Public inheritance: Public and protected members of the base class remain public and protected in
the derived class.

• Protected inheritance: Public and protected members of the base class become protected in the
derived class.

• Private inheritance: Public and protected members of the base class become private in the derived
class.

Understanding the appropriate type of inheritance to use is essential for designing efficient and maintainable
object-oriented systems in C++.

Generic Programming
Generic programming in C++ is a programming paradigm that allows developers to write flexible, reusable, and
type-independent code. This is achieved through the use of templates, which enable functions, classes, and
other constructs to work with any data type without being rewritten for each one.

Key Concepts in Generic Programming

1. Templates:

o A template is a blueprint or formula for creating generic classes or functions.

o It allows you to define a single implementation that can operate on different data types.

2. Function Templates:

o Used to define functions that can operate on any data type.

Example:
template <typename T>
T add(T a, T b) {
return a + b;
}

int main() {
std::cout << add(3, 4) << std::endl; // Works with integers
std::cout << add(3.5, 2.5) << std::endl; // Works with doubles
}
3. Class Templates:

o Used to define classes that can work with any data type.

o Example:
template <typename T>
class Box {
private:
T data;
public:
void setData(T d) { data = d; }
T getData() const { return data; }
};

int main() {
Box<int> intBox;
intBox.setData(42);
std::cout << intBox.getData() << std::endl;

Box<std::string> strBox;
strBox.setData("Hello");
std::cout << strBox.getData() << std::endl;
}

4. Template Specialization:

o Allows customizing the behavior of a template for a specific data type.

o Example:
template <typename T>
class Box {
public:
void display() { std::cout << "Generic Box" << std::endl; }
};

// Specialization for int


template <>
class Box<int> {
public:
void display() { std::cout << "Integer Box" << std::endl; }
};
int main() {
Box<double> doubleBox;
doubleBox.display(); // Outputs: Generic Box

Box<int> intBox;
intBox.display(); // Outputs: Integer Box
}

5. Standard Template Library (STL):

o The STL is a collection of pre-written generic classes and functions such as containers (e.g.,
vector, list), algorithms (e.g., sort, find), and iterators.

o These components are heavily based on the principles of generic programming.

6. Type Deduction:

o When you use a template, the compiler deduces the type automatically based on the provided
arguments, making the code cleaner and reducing the likelihood of errors.

Advantages of Generic Programming

• Code Reusability: Avoids duplication of logic for different data types.

• Flexibility: Adapts to various types without modifying the original code.

• Efficiency: Reduces the need for casting or manual type management.

Limitations

• Complexity: Errors in templates can sometimes be verbose and harder to debug.

• Compilation Overhead: Templates can increase compilation time due to type expansion.

• Code Bloat: If not managed carefully, templates can lead to larger binary sizes as separate versions are
generated for different types.

Generic programming is one of the most powerful features of C++ and forms the backbone of the STL, making it
an essential concept for modern C++ programming.

UNIT 4

Platform Independence

Platform independence means that Java programs can run on any operating system (Windows, macOS, Linux,
etc.) without needing recompilation. This is achieved through:
1. Compilation to Bytecode:

o When Java source code is compiled, it is converted into an intermediate, platform-neutral form
called bytecode. This bytecode is not specific to any particular operating system or hardware.

2. Java Virtual Machine (JVM):

o The JVM acts as an interpreter for the bytecode. Each operating system has its own
implementation of the JVM, which translates the bytecode into machine code specific to that
platform at runtime. Since the JVM abstracts the underlying operating system, the same Java
bytecode can run on any platform where a JVM is available.

Key Point: Write Once, Run Anywhere (WORA) — Developers write Java code once, and it can run on any
platform with a JVM.

Architecture Independence

Architecture independence means that Java programs are not tied to the specific hardware architecture (like
x86, ARM, etc.) of the system they run on. This is made possible because:

1. Hardware-Agnostic Bytecode:

o The bytecode generated by the Java compiler does not depend on the underlying processor
architecture. It is designed to be neutral to factors such as instruction sets and register
configurations.

2. JVM as a Mediator:

o The JVM translates the architecture-independent bytecode into architecture-specific machine


code during execution. This ensures that Java programs work uniformly across different
hardware configurations.

Input datatype
import java.util.Scanner;

public class InputExample {


public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);

System.out.print("Enter an integer: ");


int intValue = scanner.nextInt();

System.out.print("Enter a float value: ");


float floatValue = scanner.nextFloat();
System.out.print("Enter a double value: ");
double doubleValue = scanner.nextDouble();

System.out.print("Enter a boolean value: ");


boolean booleanValue = scanner.nextBoolean();

System.out.print("Enter a single word: ");


String word = scanner.next();

System.out.print("Enter a character: ");


char charValue = scanner.next().charAt(0);

scanner.nextLine(); // Consume leftover newline


System.out.print("Enter a whole sentence: ");
String sentence = scanner.nextLine();

System.out.println("\nYou entered:");
System.out.println("Integer: " + intValue);
System.out.println("Float: " + floatValue);
System.out.println("Double: " + doubleValue);
System.out.println("Boolean: " + booleanValue);
System.out.println("Word: " + word);
System.out.println("Character: " + charValue);
System.out.println("Sentence: " + sentence);

scanner.close();
}
}

Here’s an explanation of the keywords static, final, and super in Java, along with examples for each:

1. static

The static keyword is used to declare members (variables, methods, or blocks) that belong to the class rather
than to any specific instance of the class.

Characteristics:

• Static members are shared across all instances of a class.

• They can be accessed without creating an instance of the class.


• Static methods cannot access non-static members directly because non-static members belong to an
instance.

Example:
class StaticExample {
static int count = 0; // Static variable

StaticExample() {
count++; // Increment the static variable
}

static void displayCount() { // Static method


System.out.println("Count is: " + count);
}
}

public class Main {


public static void main(String[] args) {
StaticExample obj1 = new StaticExample();
StaticExample obj2 = new StaticExample();
StaticExample.displayCount(); // Output: Count is: 2
}
}

2. final

The final keyword is used to declare constants, prevent method overriding, and prevent inheritance.

Usage:

1. Final Variable: Its value cannot be changed once initialized.

2. Final Method: Cannot be overridden in a subclass.

3. Final Class: Cannot be subclassed.

Example:
class FinalExample {
final int MAX_VALUE = 100; // Final variable

final void displayMessage() { // Final method


System.out.println("This is a final method.");
}
}

// class SubClass extends FinalExample { // Uncommenting this will cause an


error because FinalExample is final.
// void displayMessage() { // Error: Cannot override final method
// System.out.println("Overridden");
// }
// }

3. super

The super keyword refers to the immediate parent class and is used to:

1. Access a parent class's methods or variables.

2. Call a parent class's constructor.

Example:
class Parent {
String message = "Hello from Parent";

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

class Child extends Parent {


String message = "Hello from Child";

void display() {
super.display(); // Call parent class's method
System.out.println(super.message); // Access parent class's variable
}
}

public class Main {


public static void main(String[] args) {
Child child = new Child();
child.display();
// Output:
// Parent Method
// Hello from Parent
}
}

Summary Table:

Keyword Purpose Key Feature

static For members belonging to the class Shared across all instances; callable without creating
rather than instances. an object.

final To restrict modification. Used for constants, methods that can't be overridden,
or classes that can't be subclassed.

super To refer to the parent class. Access parent methods, variables, or constructor.

Interface in JAVA
In Java, an interface is a reference type, similar to a class, that is used to define a set of abstract methods
(methods without implementation) and constants. An interface provides a way to achieve abstraction and
multiple inheritance in Java. It is one of the key features that supports the Object-Oriented Programming (OOP)
principles of Java.

Key Characteristics of Interfaces in Java

1. Pure Abstraction:

o An interface is a blueprint for a class and defines what a class must do, but not how it does it.

o It contains only method declarations (by default, abstract methods) and constant variables
(declared with final).

2. Method Implementation:

o Interfaces cannot contain method bodies for abstract methods (before Java 8).

o However, from Java 8 onward, interfaces can have:

▪ Default methods: Methods with a body using the default keyword.

▪ Static methods: Methods with a body using the static keyword.

o From Java 9 onward, interfaces can also have private methods.

3. No Constructors:

o Interfaces cannot have constructors because they cannot be instantiated.


4. Multiple Inheritance:

o A class can implement multiple interfaces, enabling a form of multiple inheritance that Java
classes do not support directly.

5. Public Methods:

o Methods in an interface are implicitly public and abstract (unless they are default or static).

6. Variables in Interfaces:

o Variables in an interface are implicitly public, static, and final (constants).

Syntax of an Interface

Here’s a basic example of defining an interface:


// Defining an interface
interface Animal {
// Abstract method
void sound(); // implicitly public and abstract

// Default method (Java 8 and later)


default void eat() {
System.out.println("This animal eats food.");
}

// Static method (Java 8 and later)


static void info() {
System.out.println("This is an Animal interface.");
}
}

Implementing an Interface

A class must use the implements keyword to use an interface and provide implementations for all of its
abstract methods.
// Implementing the interface
class Dog implements Animal {
// Providing implementation for abstract method
public void sound() {
System.out.println("The dog barks.");
}
}
// Main class to test the implementation
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog();
myDog.sound(); // Output: The dog barks.
myDog.eat(); // Output: This animal eats food.

// Static method is called using the interface name


Animal.info(); // Output: This is an Animal interface.
}
}

Multiple Interface Implementation

A class can implement multiple interfaces, providing a way to achieve multiple inheritance.
interface Printable {
void print();
}

interface Showable {
void show();
}

class Document implements Printable, Showable {


public void print() {
System.out.println("Printing the document...");
}

public void show() {


System.out.println("Showing the document...");
}
}

public class Main {


public static void main(String[] args) {
Document doc = new Document();
doc.print(); // Output: Printing the document...
doc.show(); // Output: Showing the document...
}
}

Advantages of Interfaces

1. Multiple Inheritance: Allows a class to inherit behavior from multiple sources.

2. Standardization: Ensures consistent method signatures across classes that implement the same
interface.

3. Abstraction: Provides a way to define contracts that multiple classes must adhere to.

Practical Applications of Interfaces

1. Defining Contracts:

o Interfaces are commonly used to define contracts or APIs between different modules of a
system.

2. Plug-and-Play:

o Interfaces allow objects to be interchangeable if they implement the same interface, making the
code flexible.

3. Marker Interfaces:

o Empty interfaces like Serializable or Cloneable are used to signal or mark classes for special
behavior.

Summary

• Interface is a mechanism to achieve abstraction and multiple inheritance.

• It provides a contract that implementing classes must follow.

• From Java 8 onward, interfaces can include default and static methods.

• They play a crucial role in designing reusable, scalable, and maintainable systems.

Abstract Method and Class


Abstract Class

An abstract class in Java:

1. Definition: A class that cannot be instantiated directly. It is meant to serve as a base class for other
classes.

2. Purpose: Provides a common structure and functionality that can be shared across multiple
subclasses, while also leaving certain details to be implemented by the subclasses.

3. Characteristics:
o It can contain abstract methods (methods without a body).

o It can also contain concrete methods (methods with a body).

o It can have instance variables, constructors, and static methods.

4. Use Case: When you want to provide a template for a group of related classes and ensure they
implement specific methods.

Syntax:
abstract class Animal {
// Abstract method (no body)
abstract void makeSound();

// Concrete method (has a body)


void sleep() {
System.out.println("Sleeping...");
}
}

Abstract Method

An abstract method in Java:

1. Definition: A method that is declared without an implementation (no method body) and is intended to
be overridden in subclasses.

2. Purpose: Ensures that all subclasses provide their specific implementation for the method.

3. Characteristics:

o It can only exist within an abstract class.

o It does not have a body (just a method signature).

o Subclasses must override it unless they are also abstract classes.

Syntax:
abstract class Animal {
// Abstract method
abstract void makeSound();
}

class Dog extends Animal {


// Providing implementation for the abstract method
void makeSound() {
System.out.println("Bark!");
}
}

Key Points

1. You cannot create an instance of an abstract class directly:

2. Animal a = new Animal(); // Error!

3. Subclasses of an abstract class must either:

o Provide implementations for all abstract methods, or

o Be declared as abstract themselves.

4. Abstract classes can have constructors, but they are called only when instantiated through a subclass.

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

// Concrete method
void info() {
System.out.println("This is a shape.");
}
}

class Circle extends Shape {


void draw() {
System.out.println("Drawing a circle.");
}
}

class Rectangle extends Shape {


void draw() {
System.out.println("Drawing a rectangle.");
}
}

public class Main {


public static void main(String[] args) {
Shape circle = new Circle();
Shape rectangle = new Rectangle();

circle.draw(); // Output: Drawing a circle.


rectangle.draw(); // Output: Drawing a rectangle.
circle.info(); // Output: This is a shape.
}
}

This demonstrates how abstract classes and methods enforce a contract while allowing flexibility in
implementation.

Abstract Class vs Interface

Here’s a detailed comparison between Interface and Abstract Class in Java:

Feature Interface Abstract Class

Definition A reference type that can contain A class that can have both abstract
abstract methods, default methods, methods (without implementation) and
static methods, and constants. concrete methods (with implementation).

Purpose Used to define a contract or a set of Used to represent a base class that
methods that implementing classes provides partial implementation and
must adhere to. forces subclasses to complete the rest.

Keyword Used Declared using the interface keyword. Declared using the abstract keyword.

Method - Only default, static, or private methods - Can have both abstract and fully
Implementation can have implementations. implemented methods.

Multiple A class can implement multiple A class can extend only one abstract class
Inheritance interfaces. (single inheritance).

Constructors Interfaces cannot have constructors Abstract classes can have constructors to
because they cannot be instantiated. initialize common fields for subclasses.

Access Modifiers Methods are implicitly public. Methods can have any access modifier
(public, protected, private).

Variables - Variables are implicitly public, static, - Can have instance variables (non-final,
and final (constants). static, or final).

Use Case Ideal for defining capabilities (e.g., Ideal for defining inheritance hierarchies
Flyable, Readable, etc.). or partial implementations.

Inheritance Type Allows achieving multiple inheritance Does not support multiple inheritance
because a class can implement multiple because a class can extend only one
interfaces. abstract class.
Default and Static Supported (introduced in Java 8). Not specifically supported, but methods
Methods in abstract classes can have
implementations.

Private Methods Supported (introduced in Java 9). Not applicable.

Performance Interfaces require more work for the JVM Abstract classes are slightly faster
to resolve methods at runtime, as they because method calls are resolved
rely on dynamic method lookup. statically at compile time when possible.

Examples Examples include Runnable, Serializable, Examples include AbstractList,


Comparable. AbstractMap, HttpServlet.

When to Use an Interface

• If you want to provide a contract or behavior that multiple classes should follow.

• When you need multiple inheritance.

Example:
interface Flyable {
void fly();
}

interface Movable {
void move();
}

class Bird implements Flyable, Movable {


public void fly() {
System.out.println("Bird flies.");
}

public void move() {


System.out.println("Bird moves.");
}
}

When to Use an Abstract Class

• If you want to provide a base class with some common functionality while forcing subclasses to
implement specific methods.

• When you expect to extend the class in a single inheritance hierarchy.

Example:
abstract class Animal {
abstract void sound();

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

class Dog extends Animal {


void sound() {
System.out.println("The dog barks.");
}
}

Summary

• Use interfaces for defining capabilities or when multiple inheritance is needed.

• Use abstract classes for creating a base class with partial implementation and when sharing common
behaviour is required.

Generic Programming in Java

Generic programming in Java allows you to write classes, interfaces, and methods that can operate on any
data type while ensuring type safety. This is achieved using generics, which were introduced in Java 5 as
part of the Java Collections Framework (JCF).

Why Use Generics?

1. Type Safety: Ensures that only the specified type of data can be used, reducing runtime errors.

2. Code Reusability: Allows writing flexible and reusable code.

3. Elimination of Casting: Reduces the need for explicit type casting.

Generic Classes

A generic class is a class that can operate on any data type specified when the class is instantiated.

Syntax:

class ClassName<T> {

// Fields and methods using T

• T: A type parameter that acts as a placeholder for the actual type.

Example:

public class Box<T> {

private T data;
public void setData(T data) {

this.data = data;

public T getData() {

return data;

public static void main(String[] args) {

Box<Integer> intBox = new Box<>();

intBox.setData(123);

System.out.println("Integer Box: " + intBox.getData());

Box<String> strBox = new Box<>();

strBox.setData("Hello Generics");

System.out.println("String Box: " + strBox.getData());

Key Points:

• The type parameter (T) is replaced with the actual type when the object is created.

• You can use multiple type parameters like <T, U>.

Generic Methods

A generic method is a method that can be declared within a generic or non-generic class and works with
any data type.

Syntax:

public <T> ReturnType methodName(T parameter) {

// Method body

Example:

public class GenericMethodExample {

public static <T> void printArray(T[] array) {

for (T element : array) {


System.out.print(element + " ");

System.out.println();

public static void main(String[] args) {

Integer[] intArray = {1, 2, 3, 4};

String[] strArray = {"Java", "Generics"};

printArray(intArray);

printArray(strArray);

Key Points:

• The <T> before the return type indicates that this method is generic.

• The type parameter is inferred from the arguments passed.

Generic Interfaces

Generic interfaces allow the definition of type parameters at the interface level.

Example:

interface Pair<K, V> {

K getKey();

V getValue();

class KeyValuePair<K, V> implements Pair<K, V> {

private K key;

private V value;

public KeyValuePair(K key, V value) {

this.key = key;

this.value = value;

}
public K getKey() {

return key;

public V getValue() {

return value;

public static void main(String[] args) {

Pair<Integer, String> pair = new KeyValuePair<>(1, "One");

System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());

Bounded Type Parameters

You can restrict the types that can be passed to a generic class, method, or interface using bounded type
parameters.

Syntax:

<T extends SuperClass>

Example:

public class BoundedTypeExample<T extends Number> {

private T number;

public BoundedTypeExample(T number) {

this.number = number;

public double square() {

return number.doubleValue() * number.doubleValue();

public static void main(String[] args) {

BoundedTypeExample<Integer> intExample = new BoundedTypeExample<>(5);

System.out.println("Square: " + intExample.square());


BoundedTypeExample<Double> doubleExample = new BoundedTypeExample<>(5.5);

System.out.println("Square: " + doubleExample.square());

Wildcard in Generics

Wildcards allow greater flexibility when working with generics by representing an unknown type.

Syntax:

• ?: Represents an unknown type.

• Bounded Wildcards:

o <? extends T>: Any type that is a subclass of T.

o <? super T>: Any type that is a superclass of T.

Example:

import java.util.*;

public class WildcardExample {

public static void printList(List<?> list) {

for (Object obj : list) {

System.out.print(obj + " ");

System.out.println();

public static void main(String[] args) {

List<Integer> intList = Arrays.asList(1, 2, 3);

List<String> strList = Arrays.asList("A", "B", "C");

printList(intList);

printList(strList);

Advantages of Generics in Java


1. Compile-Time Type Checking: Prevents runtime errors by ensuring type safety at compile time.

2. Code Reusability: Write code once and reuse it for different types.

3. Elimination of Explicit Casting: Simplifies code by removing the need for casting.

Limitations of Generics

1. Type Erasure: Generics in Java are implemented using type erasure, meaning type information is
removed during runtime.

2. Primitive Types: Generics do not support primitive types directly (e.g., int, double); you must use
wrapper classes (Integer, Double).

Generics in Java make code more robust, readable, and efficient by encouraging the use of type-safe
programming practices.

UNIT 5
Exception Handling
Java provides a robust mechanism for handling exceptions to ensure a program can handle runtime errors
gracefully. Let’s understand each keyword in detail with examples.

1. try

o A try block is used to enclose code that might throw an exception.

o It acts as the "guarded region" where exceptions might occur.

o It must be followed by either a catch block, a finally block, or both.

o Detect potential exceptions.

o Protect code that could disrupt normal execution.

o A single try block can have multiple catch blocks to handle different exceptions.

o The try block cannot exist without either catch or finally.

Example:
try {
int result = 10 / 0; // Division by zero causes ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Exception caught: " + e.getMessage());
}

2. catch

o A catch block is used to handle exceptions thrown by the try block.

o It provides a way to deal with the error without halting the program.

o The catch block takes a parameter (the exception type to handle).


o Exceptions are handled in the order they are written; specific exceptions should come before
general ones.

o Specify how to handle specific exceptions.

o Prevent program termination due to unhandled exceptions.

• Example:
try {
String str = null;
System.out.println(str.length()); // NullPointerException
} catch (NullPointerException e) {
System.out.println("Caught NullPointerException: " + e);
}

3. throw

o The throw keyword is used to explicitly throw an exception in Java.

o User can define its own conditions for occurrence of a pre-defined exception.

o It can be used to throw built-in exceptions or user-defined exceptions.

o Force an exception to occur during runtime.

o Create and propagate exceptions programmatically.

o When an exception is thrown, the flow of execution is interrupted, and it searches for the
nearest catch block.

o Only one exception can be thrown at a time using throw.

• Example:
class Test {
static void checkAge(int age) {
if (age < 18) {
throw new IllegalArgumentException("Age must be 18 or older.");
}
System.out.println("Access granted.");
}
}

public class Main {


public static void main(String[] args) {
checkAge(15); // Throws IllegalArgumentException
}
}
4. throws

o The throws keyword is used in a method declaration to indicate the exceptions that the method
can throw.

o It informs the caller of the method about potential exceptions.

o Delegate exception handling responsibility to the caller.

o Declare checked exceptions that a method might throw.

• Example:
import java.io.*;

class Test {
static void readFile() throws IOException {
FileReader file = new FileReader("nonexistent.txt");
}
}

public class Main {


public static void main(String[] args) {
try {
Test.readFile();
} catch (IOException e) {
System.out.println("File not found: " + e.getMessage());
}
}
}

5. finally

o The finally block is used to execute code regardless of whether an exception is thrown or
caught.

o It is typically used for resource cleanup (e.g., closing files, database connections).

o Ensure important cleanup code runs irrespective of exceptions.

o Guarantee execution of critical code.

o The finally block is optional but highly recommended for cleanup operations.

o It executes even if there is a return statement in the try or catch blocks.


• Example:
try {
int[] arr = new int[3];
System.out.println(arr[5]); // ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Exception caught: " + e);
} finally {
System.out.println("Finally block executed.");
}

Example Combining All Keywords


import java.io.*;

class Example {
static void processFile(String filename) throws IOException {
FileReader file = new FileReader(filename);
System.out.println("File opened successfully.");
}

public static void main(String[] args) {


try {
processFile("nonexistent.txt");
} catch (IOException e) {
System.out.println("Caught exception: " + e.getMessage());
} finally {
System.out.println("Cleanup code executed in finally block.");
}

try {
throw new ArithmeticException("Division by zero error.");
} catch (ArithmeticException e) {
System.out.println("Exception caught using throw: " +
e.getMessage());
}
}
}

Key Differences Between throw and throws

Feature throw throws

Purpose Used to explicitly throw an exception. Declares exceptions a method can throw.

Usage Within the method body. In the method signature.

Type Creates and throws one specific Can declare multiple exceptions separated by
exception. commas.

Example throw new IOException(); void method() throws IOException {}

Summary

• try and catch work together to handle exceptions.

• throw is used to explicitly generate exceptions.

• throws declares exceptions for a method to notify the caller.

• finally ensures cleanup code executes, even after exceptions.

MULTITHREADING

Multithreading in Java is a technique that allows a program to execute multiple threads simultaneously,
enabling parallel execution of tasks. This is particularly useful for making programs more efficient by utilizing
multiple CPU cores and improving the performance of applications that perform time-consuming or
independent tasks.

Key Concepts of Multithreading in Java

1. Thread:

o A thread is the smallest unit of a process. In Java, each thread runs independently but shares
the process's resources, such as memory and file handles.

o Threads in Java are represented by the Thread class in the java.lang package.

2. Multithreading:

o It refers to the ability to execute two or more threads concurrently.

o Multithreading is used to achieve multitasking within a program.

3. Main Thread:

o Every Java program starts with a main thread, which is the thread responsible for executing the
main() method.

Creating Threads in Java

Java provides two primary ways to create threads:


1. By Extending the Thread Class

• Create a class that extends the Thread class.

• Override the run() method to define the task for the thread.

• Start the thread using the start() method.


class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running");
}
}

public class Test {


public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}

2. By Implementing the Runnable Interface

• Create a class that implements the Runnable interface.

• Override the run() method to define the task for the thread.

• Pass an instance of the class to a Thread object and start the thread.
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running");
}
}

public class Test {


public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}

Thread States
Threads in Java can be in one of the following states:

1. New: The thread is created but not yet started.

2. Runnable: The thread is ready to run but is waiting for CPU time.

3. Running: The thread is currently executing.

4. Blocked/Waiting: The thread is waiting for a resource or another thread to complete its task.

5. Terminated: The thread has completed its task and is no longer active.

Synchronization

When multiple threads access shared resources, synchronization is necessary to prevent data inconsistency or
corruption. Java provides synchronized blocks and methods to achieve this.

class SharedResource {

synchronized void printMessage(String message) {

System.out.print("[");

System.out.print(message);

System.out.println("]");

Thread Priorities

Threads in Java can have priorities ranging from 1 (MIN_PRIORITY) to 10 (MAX_PRIORITY). The default priority is
5 (NORM_PRIORITY). The JVM uses these priorities to decide thread scheduling.

Thread thread = new Thread();

thread.setPriority(Thread.MAX_PRIORITY);

Benefits of Multithreading

1. Efficient utilization of CPU resources.

2. Faster execution by running tasks in parallel.

3. Better user responsiveness in GUI applications.

4. Simpler program structure for complex tasks.

Challenges of Multithreading

1. Thread synchronization issues.

2. Increased complexity in debugging.

3. Deadlocks when two or more threads are blocked forever.

By mastering multithreading, Java developers can build highly efficient, responsive, and concurrent
applications.

You might also like