KEMBAR78
Java Oop | PDF | Object Oriented Programming | Programming
0% found this document useful (0 votes)
29 views158 pages

Java Oop

The document provides an overview of Java Object-Oriented Programming (OOP), explaining key concepts such as encapsulation, inheritance, polymorphism, and abstraction. It covers classes and objects, constructors, memory allocation, garbage collection, and the use of packages and access modifiers. Additionally, it includes practical examples and exercises to reinforce understanding of these foundational concepts.

Uploaded by

mukeshchevula
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)
29 views158 pages

Java Oop

The document provides an overview of Java Object-Oriented Programming (OOP), explaining key concepts such as encapsulation, inheritance, polymorphism, and abstraction. It covers classes and objects, constructors, memory allocation, garbage collection, and the use of packages and access modifiers. Additionally, it includes practical examples and exercises to reinforce understanding of these foundational concepts.

Uploaded by

mukeshchevula
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/ 158

Java Object-Oriented Programming (OOP)

What is OOP?

Object-Oriented Programming is a programming paradigm that uses objects and


classes to structure code. It helps in creating reusable, modular, and maintainable code.

Key Concepts:

• Encapsulation: Bundling data and methods together


• Inheritance: Creating new classes based on existing ones
• Polymorphism: One interface, multiple implementations
• Abstraction: Hiding implementation details

Classes and Objects

What is a Class?

A class is a template or blueprint for creating objects. It de nes:

• Properties (variables/attributes)
• Methods (functions/behaviors)
// Example: Student class
class Student {
// Properties (Instance Variables)
int rollNumber;
String name;
float marks;
}

Class De nition Rules:

• Class name should start with a capital letter


• Use meaningful names
• A class is a logical construct (template)
Real-World Examples of Classes:

• Car Class: Properties (engine, price, seats), Methods (start, stop, accelerate)
• Human Class: Properties (height, weight, age), Methods (walk, talk, eat)

Properties vs Objects
fi
fi
Class vs Object Comparison:

Class Object

Logical construct Physical reality

Template/Blueprint Instance of class

Occupies no memory Occupies memory space

One class de nition Multiple objects possible

Example:

// Class (Template)
class Car {
String engine;
int price;
int seats;
}

// Objects (Instances)
Car maruti = new Car(); // Object 1
Car bmw = new Car(); // Object 2
Car ferrari = new Car(); // Object 3

Creating Objects

Object Creation Syntax:

ClassName objectName = new ClassName();

Example:

// Creating Student objects


Student kunal = new Student();
Student rahul = new Student();

// Accessing properties using dot operator


kunal.rollNumber = 13;
kunal.name = "Kunal Kushwaha";
fi
kunal.marks = 85.5f;

System.out.println(kunal.name); // Output: Kunal Kushwaha

Properties of Objects:

1. State: Current values of object's attributes


2. Identity: Unique memory address
3. Behavior: Methods that can be called on the object

Constructors

What is a Constructor?

A constructor is a special method that:

• Has the same name as the class


• Has no return type
• Is called automatically when an object is created
• Initializes object properties
Default Constructor:

class Student {
int rollNumber;
String name;
float marks;

// Default constructor (automatically provided if not


written)
Student() {
// Default initialization
rollNumber = 0;
name = null;
marks = 0.0f;
}
}

Parameterized Constructor:

class Student {
int rollNumber;
String name;
float marks;

// Parameterized constructor
Student(int rollNumber, String name, float marks) {
this.rollNumber = rollNumber;
this.name = name;
this.marks = marks;
}
}

Usage:

Student kunal = new Student(13, "Kunal Kushwaha", 85.5f);

The "this" Keyword

Purpose of "this":

• Refers to the current object


• Used to differentiate between instance variables and parameters
• Points to the object that is calling the method
Example:

class Student {
String name;

Student(String name) {
this.name = name; // this.name refers to instance
variable
// name refers to parameter
}

void greet() {
System.out.println("Hello, my name is " + this.name);
}
}

How "this" Works:

Student kunal = new Student("Kunal");


kunal.greet(); // "this" gets replaced with "kunal"
// Internally becomes: kunal.name

Constructor Overloading

Multiple Constructors:

You can have multiple constructors with different parameters:

class Student {
int rollNumber;
String name;
float marks;

// Constructor 1: No parameters
Student() {
this.rollNumber = 0;
this.name = "Default";
this.marks = 0.0f;
}

// Constructor 2: All parameters


Student(int rollNumber, String name, float marks) {
this.rollNumber = rollNumber;
this.name = name;
this.marks = marks;
}

// Constructor 3: Copy constructor


Student(Student other) {
this.rollNumber = other.rollNumber;
this.name = other.name;
this.marks = other.marks;
}
}

Calling One Constructor from Another:

class Student {
int rollNumber;
String name;
float marks;

Student() {
this(0, "Default", 0.0f); // Calls parameterized
constructor
}

Student(int rollNumber, String name, float marks) {


this.rollNumber = rollNumber;
this.name = name;
this.marks = marks;
}
}

Dynamic Memory Allocation

Memory Allocation Process:

1. Compile Time: Code is converted to bytecode


2. Runtime: Program executes in memory (RAM)
Object Creation Steps:

Student kunal = new Student();

Left Side (Student kunal):

• Creates a reference variable of type Student


• Executed at compile time
• Stored in Stack memory
Right Side (new Student()):

• Creates actual object


• Executed at runtime
• Stored in Heap memory
• Returns memory address to reference variable
Memory Representation:

Stack Memory Heap Memory

kunal Student Object


(reference) rollNumber: 0
name: null
marks: 0.0f

The "new" Keyword

Functions of "new":

1. Allocates memory in heap


2. Returns reference to allocated memory
3. Calls constructor to initialize object
Important Points:

• All objects in Java are allocated in heap memory


• Reference variables are stored in stack memory
• Objects are created only at runtime (dynamic allocation)
Example:

Student s1 = new Student();


Student s2 = s1; // s2 points to same object as s1

s1.name = "Kunal";
System.out.println(s2.name); // Output: Kunal (same object)

The " nal" Keyword

Uses of " nal":

1. Final Variables:

final int MAX_SIZE = 100;


// MAX_SIZE cannot be changed after initialization

2. Final Methods:












fi






fi








































































class Parent {
final void display() {
System.out.println("Cannot be overridden");
}
}

3. Final Classes:

final class Utility {


// This class cannot be inherited
}

Important Rules:

• Final variables must be initialized when declared or in constructor


• Final variables with primitive types cannot be changed
• Final variables with object references cannot be reassigned, but object content
can be modi ed
Example:

final Student s = new Student();


s.name = "Kunal"; // Allowed - changing object content
// s = new Student(); // Error - cannot reassign reference

Wrapper Classes

What are Wrapper Classes?

Wrapper classes provide object representations of primitive data types.

Common Wrapper Classes:

// Primitive Wrapper
int Integer
float Float
double Double
char Character
boolean Boolean

Usage:





fi

// Creating wrapper objects
Integer num1 = new Integer(10); // Explicit
Integer num2 = 20; // Autoboxing

// Converting back to primitive


int primitiveValue = num2.intValue(); // Explicit
int autoValue = num2; // Auto-unboxing

Why Wrapper Classes?

• Required for collections (ArrayList, HashMap, etc.)


• Provide utility methods
• Enable null values
• Required for generics

Garbage Collection

What is Garbage Collection?

Automatic memory management system in Java that removes unused objects from
heap memory.

When Objects Become Eligible:

• No reference variables point to the object


• Reference is set to null
• Reference goes out of scope
Example:

Student s1 = new Student();


Student s2 = new Student();
s1 = s2; // First object becomes eligible for garbage collection

Finalize Method:

class Student {
@Override
protected void finalize() throws Throwable {
System.out.println("Object is being destroyed");
super.finalize();
}
}
Key Points:

• Garbage collection is automatic


• Cannot force immediate garbage collection
• Can suggest GC using System.gc()
• Finalize method called before object destruction

Practice Exercises

Exercise 1: Create a Book Class

class Book {
String title;
String author;
double price;

// Add constructors and methods


}

Exercise 2: Implement a Person Class

class Person {
String name;
int age;
String address;

// Add multiple constructors


// Add methods: displayInfo(), updateAge()
}

Exercise 3: Create a Calculator Class

class Calculator {
// Add methods for basic operations
// Use constructor to initialize any required values
}

Note: This is a foundational topic in Java OOP. Master these concepts before moving to
advanced topics like inheritance and polymorphism.
Java Packages

What are Packages?

Packages in Java are like containers or folders that organize related classes and
interfaces. They help in:

• Avoiding naming con icts between classes


• Organizing code into logical groups
• Controlling access to classes and members
• Creating hierarchical structure for large projects
Real-World Analogy:

Think of packages like folders in your le system:

📁 Company Project
📁 com.company.project
📁 utils
📄 Helper.java
📄 Calculator.java
📁 models
📄 User.java
📄 Product.java
📁 services
📄 UserService.java
📄 ProductService.java

Package Declaration Syntax:

// First line in any Java file (optional)


package com.company.project.utils;

public class Helper {


// Class content
}

Package Naming Conventions:


fl
fi
Packages follow reverse domain naming:

• Company domain: example.com → Package: com.example


• Project name: myproject → Package: com.example.myproject
• Module: utils → Package: com.example.myproject.utils
Example Package Structure:

// File: src/com/company/intro/Main.java
package com.company.intro;

public class Main {


public static void main(String[] args) {
System.out.println("Hello from Main class!");
}
}

// File: src/com/company/intro/Greeting.java
package com.company.intro;

public class Greeting {


public static void message() {
System.out.println("Hello World!");
}
}

Bene ts of Packages:

1. Namespace Management: Avoid class name con icts


2. Access Control: Control visibility with access modi ers
3. Code Organization: Logical grouping of related classes
4. Maintainability: Easier to locate and manage code
fi
fl
fi
Access Modi ers

Access modi ers control the visibility and accessibility of classes, methods, and
variables.

Types of Access Modi ers:

Same Same Subcla Different


Modi er
Class Package ss Package

public ✅ Yes ✅ Yes ✅ Yes ✅ Yes

protected ✅ Yes ✅ Yes ✅ Yes ❌ No

default (no
✅ Yes ✅ Yes ❌ No ❌ No
modi er)

private ✅ Yes ❌ No ❌ No ❌ No

1. Public Access Modi er:

package com.company.demo;

public class PublicExample {


public int publicVar = 10;

public void publicMethod() {


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

Usage from different package:

package com.company.test;
import com.company.demo.PublicExample;

public class TestClass {


public static void main(String[] args) {
PublicExample obj = new PublicExample();
System.out.println(obj.publicVar); // ✅ Accessible
fi
fi
fi
fi
fi
fi
obj.publicMethod(); // ✅ Accessible
}
}

2. Private Access Modi er:

public class PrivateExample {


private int privateVar = 20;

private void privateMethod() {


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

public void accessPrivate() {


System.out.println(privateVar); // ✅ Accessible within
same class
privateMethod(); // ✅ Accessible within same class
}
}

3. Protected Access Modi er:

package com.company.parent;

public class ParentClass {


protected int protectedVar = 30;

protected void protectedMethod() {


System.out.println("Protected method");
}
}

package com.company.child;
import com.company.parent.ParentClass;

public class ChildClass extends ParentClass {


public void testProtected() {
System.out.println(protectedVar); // ✅ Accessible in
subclass
fi
fi
protectedMethod(); // ✅ Accessible in subclass
}
}

4. Default (Package-Private) Access:

package com.company.demo;

class DefaultClass { // No access modifier = default


int defaultVar = 40;

void defaultMethod() {
System.out.println("Default method");
}
}

Import Statements

What are Import Statements?

Import statements allow you to use classes from other packages without specifying their
full package name.

Syntax:

import package.name.ClassName;
import package.name.*; // Import all classes from package

Example:

// Without import
com.company.utils.Helper helper = new
com.company.utils.Helper();

// With import
import com.company.utils.Helper;
Helper helper = new Helper();

Different Types of Imports:


1. Single Class Import:

import java.util.Scanner;
import java.util.ArrayList;

public class ImportExample {


public static void main(String[] args) {
Scanner input = new Scanner(System.in);
ArrayList<String> list = new ArrayList<>();
}
}

2. Package Import (Wildcard):

import java.util.*; // Imports all classes from java.util


package

public class WildcardImport {


public static void main(String[] args) {
Scanner input = new Scanner(System.in);
ArrayList<String> list = new ArrayList<>();
HashMap<String, Integer> map = new HashMap<>();
}
}

3. Static Import:

import static java.lang.Math.PI;


import static java.lang.Math.sqrt;

public class StaticImportExample {


public static void main(String[] args) {
double area = PI * 5 * 5; // No need to write Math.PI
double result = sqrt(25); // No need to write Math.sqrt
}
}

Important Notes:

• Default imports: java.lang.* is automatically imported


• Same package: Classes in same package don't need import
• Import order: imports don't affect performance
• Wildcard imports: Can lead to naming con icts

Static Elements in Java

Understanding Static Concept:

Static means belonging to the class rather than to any speci c instance (object) of the
class.

Real-World Example - Human Population:

class Human {
String name; // Instance variable (different for
each person)
int age; // Instance variable (different for
each person)
static long population = 8000000000L; // Static variable
(same for all humans)

public Human(String name, int age) {


this.name = name;
this.age = age;
population++; // Increment when new human is created
}
}

Memory Representation:

Stack Memory: Heap Memory (Objects):

kunal Human Object 1


name = "Kunal"
rahul age = 25

Human Object 2
name = "Rahul"
age = 23

Class Memory (Static):


























































































































































fl
























































fi
population = 2 ← Shared by all

Static Variables and Methods

Static Variables:

Static variables are shared among all instances of a class.

public class Counter {


private static int count = 0; // Static variable
private int instanceCount = 0; // Instance variable

public Counter() {
count++; // Shared counter
instanceCount++; // Individual counter
}

public static int getCount() { // Static method


return count;
}

public void displayCounts() {


System.out.println("Static count: " + count);
System.out.println("Instance count: " + instanceCount);
}
}

Usage Example:

public class StaticExample {


public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();

System.out.println("Total objects created: " +


Counter.getCount()); // 3

c1.displayCounts(); // Static: 3, Instance: 1



























c2.displayCounts(); // Static: 3, Instance: 1
c3.displayCounts(); // Static: 3, Instance: 1
}
}

Static Methods:

Static methods belong to the class and can be called without creating objects.

public class MathUtils {


// Static method - belongs to class
public static int add(int a, int b) {
return a + b;
}

// Static method - utility function


public static double circleArea(double radius) {
return Math.PI * radius * radius;
}
}

// Usage
public class TestMath {
public static void main(String[] args) {
int sum = MathUtils.add(5, 10); // No object
needed
double area = MathUtils.circleArea(5); // No object
needed
}
}

Key Characteristics of Static Elements:

1. Shared among all instances of the class


2. Belong to the class, not to individual objects
3. Can be accessed without creating objects
4. Loaded when class is rst loaded
5. Memory allocated only once
fi
Non-static vs Static Members

Accessing Rules:

Rule 1: Static methods can only access static members directly

public class AccessRules {


static int staticVar = 10; // Static variable
int instanceVar = 20; // Instance variable

static void staticMethod() {


System.out.println(staticVar); // ✅ OK - static
accessing static
// System.out.println(instanceVar); // ❌ Error - static
accessing non-static
}
}

Why this restriction?

• Static methods don't belong to any speci c object


• Instance variables belong to speci c objects
• No object context available in static methods
Rule 2: Non-static methods can access both static and non-static members

public class AccessRules {


static int staticVar = 10;
int instanceVar = 20;

void instanceMethod() {
System.out.println(staticVar); // ✅ OK - accessing
static
System.out.println(instanceVar); // ✅ OK - accessing
instance
}
}

Accessing Non-static Members from Static Context:

If you need to access non-static members from static methods, create an object:
fi
fi
public class StaticAccess {
int instanceVar = 100;

void instanceMethod() {
System.out.println("Instance method called");
}

static void staticMethod() {


// Create object to access non-static members
StaticAccess obj = new StaticAccess();
System.out.println(obj.instanceVar); // ✅ OK with
object reference
obj.instanceMethod(); // ✅ OK with
object reference
}
}

Memory Perspective:

public class MemoryExample {


static int classVar = 50; // Loaded with class
int objectVar = 100; // Created with each object

static void staticMethod() {


// No 'this' context available
// Cannot access objectVar directly
}

void instanceMethod() {
// 'this' context available
// Can access both classVar and objectVar
}
}

When to Use Static vs Non-static:

Use Static When: Use Non-static When:

Utility functions Object-speci c behavior


fi
Constants Object state/properties

Counters/shared data Individual object data

Helper methods Object lifecycle methods

Examples:

// Static - Utility function


public static double calculateTax(double amount) {
return amount * 0.18;
}

// Non-static - Object behavior


public void updateSalary(double newSalary) {
this.salary = newSalary; // Depends on specific object
}

The "this" Keyword in Static Context

Why "this" Cannot Be Used in Static Methods:

public class ThisExample {


int instanceVar = 10;
static int staticVar = 20;

static void staticMethod() {


// System.out.println(this.instanceVar); // ❌ Error!
// 'this' keyword cannot be used in static context
}

void instanceMethod() {
System.out.println(this.instanceVar); // ✅ OK
System.out.println(this.staticVar); // ✅ OK (but not
recommended)
}
}

Explanation:
1. "this" refers to the current object
2. Static methods don't belong to any speci c object
3. No object context exists in static methods
4. Hence, "this" has no meaning in static context
Memory Visualization:

Static Method Call: Instance Method Call:

staticMethod() obj.method()
No 'this' 'this' = obj
No object Object context
context available

Static Block Initialization

What are Static Blocks?

Static blocks are used to initialize static variables or perform one-time setup when
the class is rst loaded.

Syntax:

public class StaticBlockExample {


static int a;
static int b;

// Static block - executed once when class is loaded


static {
System.out.println("Static block executed");
a = 10;
b = 20;
// Can perform complex initialization here
}

public StaticBlockExample() {
System.out.println("Constructor called");
}
}

Execution Order Example:
























fi
































fi






































public class ExecutionOrder {
static {
System.out.println("1. Static block executed");
}

{
System.out.println("3. Instance block executed");
}

public ExecutionOrder() {
System.out.println("4. Constructor executed");
}

public static void main(String[] args) {


System.out.println("2. Main method started");
ExecutionOrder obj1 = new ExecutionOrder();
ExecutionOrder obj2 = new ExecutionOrder();
}
}

Output:

1. Static block executed


2. Main method started
3. Instance block executed
4. Constructor executed
3. Instance block executed
4. Constructor executed

Key Points about Static Blocks:

1. Executed only once when class is rst loaded


2. Executed before main method (if in same class)
3. Cannot access non-static members
4. Multiple static blocks allowed (executed in order)
5. Used for complex static initialization
Multiple Static Blocks:

public class MultipleStaticBlocks {


static int x;
static int y;
fi
static {
System.out.println("First static block");
x = 100;
}

static {
System.out.println("Second static block");
y = x + 50; // Can use previously initialized static
variables
}

public static void main(String[] args) {


System.out.println("x = " + x + ", y = " + y);
}
}

Output:

First static block


Second static block
x = 100, y = 150

Inner Classes

What are Inner Classes?

Inner classes are classes de ned inside another class. They have access to the outer
class's members.

Types of Inner Classes:

1. Non-static Inner Class (Regular Inner Class)


2. Static Nested Class
3. Local Inner Class
4. Anonymous Inner Class
1. Non-static Inner Class:

public class OuterClass {


private int outerVar = 10;
fi
class InnerClass {
int innerVar = 20;

void display() {
System.out.println("Outer variable: " +
outerVar); // Can access outer
System.out.println("Inner variable: " + innerVar);
}
}

public void createInner() {


InnerClass inner = new InnerClass();
inner.display();
}
}

Usage:

public class TestInner {


public static void main(String[] args) {
// Create outer class object first
OuterClass outer = new OuterClass();

// Create inner class object using outer object


OuterClass.InnerClass inner = outer.new InnerClass();
inner.display();

// Or use outer class method


outer.createInner();
}
}

2. Static Nested Class:

public class OuterClass {


private static int staticVar = 30;
private int instanceVar = 40;

static class StaticNestedClass {


void display() {
System.out.println("Static variable: " + staticVar);
// ✅ OK
// System.out.println(instanceVar); // ❌ Error -
cannot access non-static
}
}
}

Usage:

public class TestNested {


public static void main(String[] args) {
// No need to create outer class object
OuterClass.StaticNestedClass nested = new
OuterClass.StaticNestedClass();
nested.display();
}
}

Key Differences:

Aspect Non-static Inner Class Static Nested Class

Outer object ✅ Requires outer object ❌ Independent


dependency
Access to outer ✅ All members ❌ Only static members
members
Instantiation outer.new InnerCla new Outer.NestedCla
ss() ss()
Why Use Inner Classes?

1. Logical grouping - Classes that are only used in one place


2. Encapsulation - Access to outer class private members
3. Code organization - Keep related classes together
4. Event handling - Common in GUI programming

Object toString() Method

Understanding toString() Method:

Every class in Java inherits the toString() method from the Object class.
Default toString() Behavior:

public class Student {


String name;
int age;

public Student(String name, int age) {


this.name = name;
this.age = age;
}
}

public class ToStringExample {


public static void main(String[] args) {
Student student = new Student("Kunal", 25);
System.out.println(student); // Calls toString()
automatically
}
}

Output:

Student@6d06d69c

Format: ClassName@HashCode

Overriding toString() Method:

public class Student {


String name;
int age;

public Student(String name, int age) {


this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}

Now the output becomes:

Student{name='Kunal', age=25}

When toString() is Called:

1. Explicit call: object.toString()


2. Print statements: System.out.println(object)
3. String concatenation: "Student: " + object
4. Debugger and logging frameworks
Best Practices for toString():

public class Person {


private String firstName;
private String lastName;
private int age;

// Constructor...

@Override
public String toString() {
return String.format("Person{firstName='%s',
lastName='%s', age=%d}",
firstName, lastName, age);
}
}

Internal Working:

When you write:

System.out.println(student);

Java internally does:

System.out.println(student.toString());

Singleton Design Pattern


What is Singleton Pattern?

Singleton pattern ensures that a class has only one instance and provides global
access to that instance.

Why Use Singleton?

• Database connections - Avoid multiple connections


• Logger instances - Centralized logging
• Con guration objects - Single source of settings
• Cache objects - Shared cache across application
Implementing Singleton:

Step 1: Private Constructor

public class Singleton {


// Private constructor prevents instantiation from outside
private Singleton() {
System.out.println("Singleton instance created");
}
}

Step 2: Static Instance Variable

public class Singleton {


// Static variable to hold the single instance
private static Singleton instance;

private Singleton() {
System.out.println("Singleton instance created");
}
}

Step 3: Public Static Method

public class Singleton {


private static Singleton instance;

private Singleton() {
System.out.println("Singleton instance created");
}
fi
// Public method to get the instance
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}

public void showMessage() {


System.out.println("Hello from Singleton!");
}
}

Using Singleton:

public class SingletonTest {


public static void main(String[] args) {
// Get the only object available
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
Singleton obj3 = Singleton.getInstance();

// All references point to the same object


System.out.println(obj1 == obj2); // true
System.out.println(obj2 == obj3); // true

obj1.showMessage();
}
}

Output:

Singleton instance created


true
true
Hello from Singleton!

Thread-Safe Singleton:

public class ThreadSafeSingleton {


private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {}

// Synchronized method for thread safety


public static synchronized ThreadSafeSingleton getInstance()
{
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}

Eager Initialization Singleton:

public class EagerSingleton {


// Instance created at class loading time
private static final EagerSingleton INSTANCE = new
EagerSingleton();

private EagerSingleton() {}

public static EagerSingleton getInstance() {


return INSTANCE;
}
}

Double-Checked Locking:

public class DoubleCheckedSingleton {


private static volatile DoubleCheckedSingleton instance;

private DoubleCheckedSingleton() {}

public static DoubleCheckedSingleton getInstance() {


if (instance == null) {
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}

Singleton Pattern Pros and Cons:

Advantages:

• ✅ Controlled access to sole instance


• ✅ Reduced memory footprint
• ✅ Global access point
• ✅ Lazy initialization possible
Disadvantages:

• ❌ Testing dif culties (global state)


• ❌ Thread safety concerns
• ❌ Violation of Single Responsibility Principle
• ❌ Hidden dependencies

Key Takeaways

1. Package Management:

• Organize code using packages


• Follow naming conventions (reverse domain)
• Use access modi ers appropriately
• Import statements for external classes
2. Static vs Non-static:

// Static - belongs to class


public static void utilityMethod() { }
static int sharedCounter = 0;

// Non-static - belongs to object


public void instanceMethod() { }
int objectProperty = 10;

3. Access Rules:
fi
fi
• Static methods → Can only access static members directly
• Non-static methods → Can access both static and non-static members
• "this" keyword → Not available in static context
4. Initialization Order:

1. Static blocks (when class is loaded)


2. Instance blocks (when object is created)
3. Constructor (when object is created)
5. Inner Classes:

• Non-static inner → Depends on outer object


• Static nested → Independent of outer object
• Access to outer members varies by type
6. Design Patterns:

• Singleton → Ensures single instance


• toString() → Provides string representation
• Access control → Private constructors for patterns
7. Best Practices:

// Good package structure


package com.company.module.feature;

// Appropriate access modifiers


public class PublicAPI {
private int internalData;
protected int subclassData;

public int getInternalData() {


return internalData;
}
}

// Override toString() for better debugging


@Override
public String toString() {
return "ClassName{field1=" + field1 + ", field2=" + field2 +
"}";
}

8. Memory Understanding:
• Static elements → Loaded with class, shared among all instances
• Instance elements → Created with each object
• Package organization → Helps in access control and code organization
• Singleton pattern → Ensures controlled instantiation
9. When to Use What:

Concept Use When

Static methods Utility functions, no object state needed

Static variables Shared data across all instances

Packages Organizing large codebases

Inner classes Helper classes used only by outer class

Singleton Need exactly one instance (DB connection, logger)

Practice Exercises

Exercise 1: Package Organization

Create a package structure for a library management system:

com.library.system
models (Book, Member, Transaction)
services (BookService, MemberService)
utils (DateUtils, ValidationUtils)

Exercise 2: Static Counter

public class ObjectCounter {


private static int count = 0;
private int id;

public ObjectCounter() {
count++;
this.id = count;
}

public static int getTotalObjects() {


return count;
}









public int getId() {
return id;
}
}

Exercise 3: Singleton Logger

public class Logger {


// Implement singleton pattern
// Add methods: log(String message), getLogCount()
}

Exercise 4: Inner Class Example

public class University {


String name;

class Department {
String deptName;

void displayInfo() {
// Access university name from outer class
}
}

static class Utility {


// Static nested class for utility functions
}
}
Inheritance

What is Inheritance?

Inheritance is a fundamental principle of OOP where a new class (child/derived class)


acquires properties and methods from an existing class (parent/base class).

Real-World Analogy:

Think of inheritance like family inheritance:

• Parents have certain properties (money, house, values)


• Children inherit these properties from parents
• Children can also have their own additional properties
Basic Inheritance Syntax:

// Parent/Base/Super Class
class Vehicle {
String engine;
int wheels;

void start() {
System.out.println("Vehicle is starting");
}

void stop() {
System.out.println("Vehicle is stopping");
}
}

// Child/Derived/Sub Class
class Car extends Vehicle {
String brand;
int doors;

// Car inherits engine, wheels, start(), stop()


// Plus has its own additional properties

void honk() {
System.out.println("Car is honking");
}
}

Usage Example:

public class InheritanceDemo {


public static void main(String[] args) {
Car myCar = new Car();

// Accessing inherited properties


myCar.engine = "V8";
myCar.wheels = 4;

// Accessing inherited methods


myCar.start();
myCar.stop();

// Accessing own properties


myCar.brand = "BMW";
myCar.doors = 4;
myCar.honk();
}
}

Key Bene ts of Inheritance:

1. Code Reusability - Don't write same code again


2. Method Overriding - Child can provide speci c implementation
3. Hierarchical Classi cation - Logical organization
4. Polymorphism Support - Runtime method resolution
Important Rules:

1. Child class inherits all public and protected members


2. Private members are not inherited
3. Constructors are not inherited
4. Child class can override parent methods

Types of Inheritance

1. Single Inheritance

One class extends only one other class.


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

class Dog extends Animal {


void bark() {
System.out.println("Dog is barking");
}
}

2. Multilevel Inheritance

A chain of inheritance where one class extends another, which extends another.

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

class Car extends Vehicle {


void drive() {
System.out.println("Car is driving");
}
}

class SportsCar extends Car {


void turboBoost() {
System.out.println("Turbo boost activated");
}
}

3. Hierarchical Inheritance

Multiple classes extend the same parent class.

class Shape {
double area() {
return 0;
}
}

class Circle extends Shape {


double radius;

@Override
double area() {
return Math.PI * radius * radius;
}
}

class Rectangle extends Shape {


double length, width;

@Override
double area() {
return length * width;
}
}

class Triangle extends Shape {


double base, height;

@Override
double area() {
return 0.5 * base * height;
}
}

4. Multiple Inheritance (NOT SUPPORTED in Java)

// This is NOT allowed in Java


class Child extends Parent1, Parent2 { // ERROR!
// Cannot extend multiple classes
}

Why Multiple Inheritance is not allowed:

• Diamond Problem - Ambiguity when multiple parents have same method


• Complexity - Makes language too complex
• Solution - Use Interfaces (covered in separate topic)
5. Hybrid Inheritance (NOT SUPPORTED in Java)

Combination of multiple inheritance types. Since Java doesn't support multiple


inheritance, hybrid inheritance is also not directly supported.
Polymorphism

What is Polymorphism?

Polymorphism = "Poly" (many) + "Morphism" (forms)

• One entity represented in multiple forms


• Same interface, different implementations
Real-World Example:

A person can be:

• A student in college
• A son/daughter at home
• An employee at work
• A customer in a shop
Same person, different roles (forms).

Types of Polymorphism:

1. Compile-Time Polymorphism (Static Polymorphism)

Achieved through Method Overloading.

class Calculator {
// Method overloading - same name, different parameters

int add(int a, int b) {


return a + b;
}

double add(double a, double b) {


return a + b;
}

int add(int a, int b, int c) {


return a + b + c;
}

String add(String a, String b) {


return a + b;
}
}

2. Runtime Polymorphism (Dynamic Polymorphism)

Achieved through Method Overriding.

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

class Dog extends Animal {


@Override
void makeSound() {
System.out.println("Dog barks: Woof!");
}
}

class Cat extends Animal {


@Override
void makeSound() {
System.out.println("Cat meows: Meow!");
}
}

public class PolymorphismDemo {


public static void main(String[] args) {
Animal animal1 = new Dog(); // Upcasting
Animal animal2 = new Cat(); // Upcasting

animal1.makeSound(); // Output: Dog barks: Woof!


animal2.makeSound(); // Output: Cat meows: Meow!

// Same method call, different behavior - POLYMORPHISM!


}
}
Method Overloading vs Overriding

Method Overloading (Compile-Time Polymorphism)

De nition: Multiple methods with same name but different parameters.

class MathOperations {
// Different number of parameters
int multiply(int a, int b) {
return a * b;
}

int multiply(int a, int b, int c) {


return a * b * c;
}

// Different types of parameters


double multiply(double a, double b) {
return a * b;
}

// Different order of parameters


void display(int num, String text) {
System.out.println(num + ": " + text);
}

void display(String text, int num) {


System.out.println(text + ": " + num);
}
}

Rules for Method Overloading:

1. Method name must be same


2. Parameters must be different (number, type, or order)
3. Return type can be different
4. Access modi ers can be different
Method Overriding (Runtime Polymorphism)
fi
fi
De nition: Child class provides speci c implementation of a method de ned in parent
class.

class Vehicle {
void start() {
System.out.println("Vehicle is starting");
}

void fuel() {
System.out.println("Adding fuel");
}
}

class ElectricCar extends Vehicle {


@Override
void start() {
System.out.println("Electric car starting silently");
}

@Override
void fuel() {
System.out.println("Charging battery");
}
}

class PetrolCar extends Vehicle {


@Override
void start() {
System.out.println("Petrol car engine roaring");
}

// fuel() method inherited as-is


}

Rules for Method Overriding:

1. Method signature must be exactly same


2. Return type must be same (or covariant)
3. Access modi er cannot be more restrictive
4. Cannot override static, nal, or private methods
Comparison Table:
fi
fi
fi
fi
fi
Aspect Method Overloading Method Overriding

When Compile-time Runtime

Multiple ways to call same Speci c implementation in


Purpose
method child

Parameters Must be different Must be same

Return
Can be different Must be same
Type

Inheritance Not required Required

@Override Not used Should be used

Static Methods and Overriding

Can We Override Static Methods?

Answer: NO - Static methods cannot be overridden.

Why Static Methods Cannot Be Overridden:

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

class Child extends Parent {


static void display() { // This is NOT overriding
System.out.println("Child static method");
}
}

public class StaticMethodDemo {


public static void main(String[] args) {
Parent obj1 = new Parent();
Parent obj2 = new Child(); // Parent reference, Child
object

obj1.display(); // Output: Parent static method


fi
obj2.display(); // Output: Parent static method (NOT
Child!)

// Static method call depends on reference type, not


object type
}
}

What Actually Happens:

This is called Method Hiding, not overriding:

class TestStatic {
public static void main(String[] args) {
Parent.display(); // Parent static method
Child.display(); // Child static method

// Calling through reference


Parent p = new Child();
p.display(); // Parent static method (Method
Hiding)
}
}

Key Points:

1. Static methods belong to class, not object


2. Method resolution happens at compile-time
3. Cannot achieve polymorphism with static methods
4. Static methods can be inherited but not overridden

Final Keyword Advanced Usage

Preventing Method Overriding:

class Vehicle {
final void engineNumber() {
System.out.println("ENG123456");
}

void start() {
System.out.println("Vehicle starting");
}
}

class Car extends Vehicle {


// This will cause compilation error
/*
@Override
void engineNumber() { // ERROR: Cannot override final
method
System.out.println("CAR789012");
}
*/

@Override
void start() { // This is allowed
System.out.println("Car starting");
}
}

Preventing Class Inheritance:

final class SecurityUtils {


public static String encrypt(String data) {
return "encrypted_" + data;
}

public static String decrypt(String data) {


return data.replace("encrypted_", "");
}
}

// This will cause compilation error


/*
class MySecurityUtils extends SecurityUtils { // ERROR!
// Cannot extend final class
}
*/

Real-World Examples of Final Classes:

// These are final classes in Java


final class String { ... }
final class Integer { ... }
final class Math { ... }

Bene ts of Final:

1. Security - Prevent unauthorized extension


2. Performance - Compiler optimizations (method inlining)
3. Design - Ensure class/method behavior is not changed

Dynamic Method Dispatch

What is Dynamic Method Dispatch?

Dynamic Method Dispatch is the mechanism by which Java determines which method
to call at runtime based on the actual object type, not the reference type.

How It Works:

class Shape {
void draw() {
System.out.println("Drawing a shape");
}

void area() {
System.out.println("Calculating area");
}
}

class Circle extends Shape {


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

@Override
void area() {
System.out.println("Area = π × r²");
}
}
fi
class Rectangle extends Shape {
@Override
void draw() {
System.out.println("Drawing a rectangle");
}

@Override
void area() {
System.out.println("Area = length × width");
}
}

public class DynamicDispatchDemo {


public static void main(String[] args) {
Shape shape; // Reference variable

// At runtime, JVM checks actual object type


shape = new Circle();
shape.draw(); // Output: Drawing a circle
shape.area(); // Output: Area = π × r²

shape = new Rectangle();


shape.draw(); // Output: Drawing a rectangle
shape.area(); // Output: Area = length × width

// Same method calls, different behavior at runtime!


}
}

Key Principle:

• Reference type determines what methods can be called


• Object type determines which version of method executes
Practical Example:

class Employee {
String name;

void work() {
System.out.println("Employee is working");
}
double calculateSalary() {
return 30000;
}
}

class Manager extends Employee {


@Override
void work() {
System.out.println("Manager is managing team");
}

@Override
double calculateSalary() {
return 80000;
}
}

class Developer extends Employee {


@Override
void work() {
System.out.println("Developer is coding");
}

@Override
double calculateSalary() {
return 60000;
}
}

public class CompanyDemo {


public static void main(String[] args) {
Employee[] employees = {
new Manager(),
new Developer(),
new Employee()
};

// Polymorphic behavior
for (Employee emp : employees) {
emp.work(); // Different work
methods
System.out.println("Salary: " +
emp.calculateSalary());
}
}
}

Output:

Manager is managing team


Salary: 80000.0
Developer is coding
Salary: 60000.0
Employee is working
Salary: 30000.0

Encapsulation and Data Hiding

What is Encapsulation?

Encapsulation is the process of wrapping data (variables) and methods together in a


single unit (class) and controlling access to them.

Data Hiding:

Making class members private and providing controlled access through public methods.

class BankAccount {
private String accountNumber;
private double balance;
private String ownerName;

// Constructor
public BankAccount(String accountNumber, String ownerName) {
this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = 0.0;
}
// Getter methods (Controlled Read Access)
public String getAccountNumber() {
return accountNumber;
}

public double getBalance() {


return balance;
}

public String getOwnerName() {


return ownerName;
}

// Setter methods (Controlled Write Access)


public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("Deposited: " + amount);
} else {
System.out.println("Invalid deposit amount");
}
}

public void withdraw(double amount) {


if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("Withdrawn: " + amount);
} else {
System.out.println("Invalid withdrawal amount or
insufficient funds");
}
}

// No direct setter for balance - controlled through


deposit/withdraw
}

Usage:

public class BankDemo {


public static void main(String[] args) {
BankAccount account = new BankAccount("123456", "John
Doe");

// Cannot directly access private members


// account.balance = 10000; // ERROR!

// Must use public methods


account.deposit(5000);
account.withdraw(2000);

System.out.println("Balance: " + account.getBalance());


}
}

Bene ts of Encapsulation:

1. Data Security - Prevent unauthorized access


2. Data Validation - Control how data is modi ed
3. Flexibility - Change implementation without affecting users
4. Maintainability - Easier to debug and maintain

Abstraction Concepts

What is Abstraction?

Abstraction means hiding implementation details and showing only essential


information to the user.

Difference: Encapsulation vs Abstraction

Encapsulation Abstraction

Data hiding Implementation hiding

How to achieve data security What to show to user

Implementation level Design level

Wrapping data and methods Hiding complexity

Process of containing information Process of gaining information


fi
fi
Abstraction Example:

// User doesn't need to know how car engine works internally


class Car {
private Engine engine;
private Transmission transmission;

// User only sees these simple methods


public void start() {
// Complex internal operations hidden
engine.ignite();
transmission.engage();
System.out.println("Car started");
}

public void accelerate() {


// Complex calculations hidden
engine.increaseRPM();
transmission.adjustGears();
System.out.println("Car accelerating");
}

public void stop() {


// Complex braking system hidden
engine.shutdown();
transmission.disengage();
System.out.println("Car stopped");
}
}

// User code - simple and clean


public class Driver {
public static void main(String[] args) {
Car myCar = new Car();

myCar.start(); // Don't care how engine starts


myCar.accelerate(); // Don't care about gear changes
myCar.stop(); // Don't care about braking system
}
}
Real-World Abstraction Examples:

1. ATM Machine - You press buttons, don't know internal banking operations
2. Phone Call - You dial number, don't know network routing
3. Email - You write and send, don't know SMTP protocols
4. Coffee Machine - You press button, don't know brewing process

Real-World Examples

Example 1: E-commerce System

// Base class
abstract class Product {
protected String name;
protected double price;
protected String category;

public Product(String name, double price, String category) {


this.name = name;
this.price = price;
this.category = category;
}

// Abstract method - each product calculates tax differently


public abstract double calculateTax();

// Common method for all products


public double getFinalPrice() {
return price + calculateTax();
}

public void displayInfo() {


System.out.println("Product: " + name);
System.out.println("Category: " + category);
System.out.println("Base Price: $" + price);
System.out.println("Tax: $" + calculateTax());
System.out.println("Final Price: $" + getFinalPrice());
}
}
class Electronics extends Product {
private int warrantyMonths;

public Electronics(String name, double price, int


warrantyMonths) {
super(name, price, "Electronics");
this.warrantyMonths = warrantyMonths;
}

@Override
public double calculateTax() {
return price * 0.18; // 18% tax for electronics
}

@Override
public void displayInfo() {
super.displayInfo();
System.out.println("Warranty: " + warrantyMonths + "
months");
}
}

class Clothing extends Product {


private String size;

public Clothing(String name, double price, String size) {


super(name, price, "Clothing");
this.size = size;
}

@Override
public double calculateTax() {
return price * 0.12; // 12% tax for clothing
}

@Override
public void displayInfo() {
super.displayInfo();
System.out.println("Size: " + size);
}
}

class Books extends Product {


private String author;

public Books(String name, double price, String author) {


super(name, price, "Books");
this.author = author;
}

@Override
public double calculateTax() {
return 0; // No tax on books
}

@Override
public void displayInfo() {
super.displayInfo();
System.out.println("Author: " + author);
}
}

Example 2: Shape Calculation System

// Inheritance + Polymorphism + Encapsulation example


class ShapeCalculator {

// Base class with common properties


static abstract class Shape {
protected String color;
protected boolean filled;

public Shape(String color, boolean filled) {


this.color = color;
this.filled = filled;
}

// Abstract methods - must be implemented by subclasses


public abstract double calculateArea();
public abstract double calculatePerimeter();
// Common method for all shapes
public void displayInfo() {
System.out.println("Shape: " +
this.getClass().getSimpleName());
System.out.println("Color: " + color);
System.out.println("Filled: " + filled);
System.out.println("Area: " + calculateArea());
System.out.println("Perimeter: " +
calculatePerimeter());
System.out.println("------------------------");
}
}

static class Circle extends Shape {


private double radius;

public Circle(double radius, String color, boolean


filled) {
super(color, filled);
this.radius = radius;
}

@Override
public double calculateArea() {
return Math.PI * radius * radius;
}

@Override
public double calculatePerimeter() {
return 2 * Math.PI * radius;
}

// Getter for encapsulation


public double getRadius() {
return radius;
}
}

static class Rectangle extends Shape {


private double length;
private double width;

public Rectangle(double length, double width, String


color, boolean filled) {
super(color, filled);
this.length = length;
this.width = width;
}

@Override
public double calculateArea() {
return length * width;
}

@Override
public double calculatePerimeter() {
return 2 * (length + width);
}

public double getLength() { return length; }


public double getWidth() { return width; }
}

static class Triangle extends Shape {


private double side1, side2, side3;

public Triangle(double side1, double side2, double


side3, String color, boolean filled) {
super(color, filled);
this.side1 = side1;
this.side2 = side2;
this.side3 = side3;
}

@Override
public double calculateArea() {
// Using Heron's formula
double s = calculatePerimeter() / 2;
return Math.sqrt(s * (s - side1) * (s - side2) * (s
- side3));
}

@Override
public double calculatePerimeter() {
return side1 + side2 + side3;
}
}

public static void main(String[] args) {


// Polymorphism in action
Shape[] shapes = {
new Circle(5.0, "Red", true),
new Rectangle(4.0, 6.0, "Blue", false),
new Triangle(3.0, 4.0, 5.0, "Green", true)
};

// Same method calls, different implementations


for (Shape shape : shapes) {
shape.displayInfo();
}

// Calculate total area


double totalArea = 0;
for (Shape shape : shapes) {
totalArea += shape.calculateArea();
}

System.out.println("Total area of all shapes: " +


totalArea);
}
}

Best Practices:

1. Use inheritance for "is-a" relationships


2. Prefer composition over inheritance when possible
3. Override toString() method for better debugging
4. Use @Override annotation for method overriding
5. Make elds private and provide public getters/setters
fi
6. Use nal keyword to prevent unwanted inheritance/overriding
Interview Important Points:

1. Static methods cannot be overridden


2. Private methods cannot be overridden
3. Final methods cannot be overridden
4. Constructors are not inherited
5. Method resolution happens at runtime for overridden methods
6. Reference type determines accessible methods, object type determines
executed method
This comprehensive coverage of Java OOP advanced concepts provides a solid
foundation for understanding inheritance, polymorphism, encapsulation, and abstraction
in real-world programming scenarios.

Access Control and Data Protection

What is Access Control?

Access Control in Java is a mechanism that restricts access to classes, methods, and
variables. It provides data security and encapsulation by controlling what parts of your
code can be accessed from where.

Why Access Control is Important?

1. Data Security - Protect sensitive data from unauthorized access


2. Encapsulation - Hide internal implementation details
3. Code Organization - Control the interface of your classes
4. Maintainability - Make code easier to modify and debug
Real-World Example:

Think of access control like security levels in a building:

• Private - Your personal of ce (only you can enter)


• Protected - Department areas (only department members)
• Package - Company oors (only company employees)
• Public - Reception area (anyone can access)

Access Modi ers Deep Dive

The Four Access Modi ers:

public class AccessExample {


fi
fi
fl
fi
fi
private int privateVar = 10; // Only within this
class
int defaultVar = 20; // Within same package
protected int protectedVar = 30; // Within package +
subclasses
public int publicVar = 40; // Accessible everywhere

private void privateMethod() {


System.out.println("Private method");
}

void defaultMethod() {
System.out.println("Default method");
}

protected void protectedMethod() {


System.out.println("Protected method");
}

public void publicMethod() {


System.out.println("Public method");
}
}

Private Access Modi er

Characteristics of Private:

• Most restrictive access modi er


• Only accessible within the same class
• Not inherited by subclasses
• Provides maximum encapsulation
Example:

public class BankAccount {


private double balance; // Private - cannot be
accessed directly
private String accountNumber; // Private - sensitive
information
fi
fi
private String pin; // Private - security
critical

// Constructor
public BankAccount(String accountNumber, String pin) {
this.accountNumber = accountNumber;
this.pin = pin;
this.balance = 0.0;
}

// Public methods to access private data (Controlled Access)


public double getBalance() {
return balance;
}

public void deposit(double amount) {


if (amount > 0) {
balance += amount;
System.out.println("Deposited: $" + amount);
} else {
System.out.println("Invalid amount");
}
}

public boolean withdraw(double amount) {


if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("Withdrawn: $" + amount);
return true;
} else {
System.out.println("Insufficient funds or invalid
amount");
return false;
}
}

// Private helper method


private boolean validatePin(String inputPin) {
return this.pin.equals(inputPin);
}
}

Usage Example:

public class BankDemo {


public static void main(String[] args) {
BankAccount account = new BankAccount("123456", "1234");

// This works - using public methods


account.deposit(1000);
System.out.println("Balance: $" + account.getBalance());

// This would cause compilation error


// account.balance = 10000; // ERROR: Cannot access
private field
// account.pin = "0000"; // ERROR: Cannot access
private field
}
}

Why Use Private?

1. Data Hiding - Protect sensitive information


2. Controlled Access - Access only through public methods
3. Validation - Add checks when setting values
4. Security - Prevent unauthorized modi cations

Public Access Modi er

Characteristics of Public:

• Least restrictive access modi er


• Accessible from anywhere in the program
• Can be accessed from any package
• Forms the public interface of your class
Example:

// File: MathUtils.java
package com.utilities;

public class MathUtils {


public static final double PI = 3.14159; // Public constant
fi
fi
fi
public static int add(int a, int b) { // Public utility
method
return a + b;
}

public static double circleArea(double radius) {


return PI * radius * radius;
}

public static void printWelcome() { // Public method


System.out.println("Welcome to Math Utils!");
}
}

Usage from Different Package:

// File: TestMath.java
package com.test;

import com.utilities.MathUtils; // Import from different


package

public class TestMath {


public static void main(String[] args) {
// All public members accessible
MathUtils.printWelcome();

int sum = MathUtils.add(10, 20);


double area = MathUtils.circleArea(5);

System.out.println("Sum: " + sum);


System.out.println("Circle Area: " + area);
System.out.println("PI value: " + MathUtils.PI);
}
}

When to Use Public?

1. Main method - Must be public for JVM access


2. API methods - Methods intended for external use
3. Constants - Values that should be globally accessible
4. Utility functions - Helper methods for general use

Protected Access Modi er

Characteristics of Protected:

• Package access + Subclass access


• Accessible within same package
• Accessible in subclasses (even in different packages)
• Not accessible to non-subclasses in different packages
Example:

// File: Vehicle.java
package com.vehicles;

public class Vehicle {


protected String engine; // Protected - accessible
to subclasses
protected int maxSpeed; // Protected
private String chassisNumber; // Private - not accessible
to subclasses

protected void startEngine() { // Protected method


System.out.println("Engine started");
}

protected void displaySpecs() {


System.out.println("Max Speed: " + maxSpeed + " km/h");
}
}

Subclass in Same Package:

// File: Car.java
package com.vehicles; // Same package

public class Car extends Vehicle {


public Car() {
engine = "Petrol Engine"; // ✅ OK - protected
accessible
fi
maxSpeed = 180; // ✅ OK - protected
accessible

startEngine(); // ✅ OK - protected
method accessible
}

public void showCarDetails() {


displaySpecs(); // ✅ OK - protected
method accessible
}
}

Subclass in Different Package:

// File: SportsCar.java
package com.sports; // Different package

import com.vehicles.Vehicle;

public class SportsCar extends Vehicle {


public SportsCar() {
engine = "V8 Engine"; // ✅ OK - protected
accessible in subclass
maxSpeed = 300; // ✅ OK - protected
accessible in subclass

startEngine(); // ✅ OK - protected
method accessible
}

public void turboBoost() {


System.out.println("Turbo boost with " + engine); // ✅
OK
}
}

Non-Subclass in Different Package:


// File: TestVehicle.java
package com.test; // Different package

import com.vehicles.Vehicle;

public class TestVehicle {


public static void main(String[] args) {
Vehicle vehicle = new Vehicle();

// These will cause compilation errors


// vehicle.engine = "Test"; // ❌ ERROR: Not
accessible
// vehicle.startEngine(); // ❌ ERROR: Not
accessible
}
}

Default Access Modi er

Characteristics of Default (Package-Private):

• No access modi er keyword speci ed


• Accessible within the same package only
• Not accessible from different packages
• Also called package-private
Example:

// File: Helper.java
package com.utilities;

class Helper { // Default class - package-


private
String message; // Default field

void printMessage() { // Default method


System.out.println("Helper message: " + message);
}

boolean validateInput(String input) { // Default method


return input != null && !input.isEmpty();
fi
fi
fi
}
}

Access within Same Package:

// File: UtilityManager.java
package com.utilities; // Same package

public class UtilityManager {


public void useHelper() {
Helper helper = new Helper(); // ✅ OK - same
package
helper.message = "Hello"; // ✅ OK - same
package
helper.printMessage(); // ✅ OK - same
package

boolean isValid = helper.validateInput("test"); // ✅


OK
}
}

Access from Different Package:

// File: TestHelper.java
package com.test; // Different package

// import com.utilities.Helper; // ❌ ERROR: Helper is not


public

public class TestHelper {


public static void main(String[] args) {
// Helper helper = new Helper(); // ❌ ERROR: Cannot
access
}
}

Access Modi er Rules Table


fi


































fi










































































ed
























public
default
private

protect








Access
Modi er
































s
e





















Yes
Yes
Yes
Yes

Memory Diagram:








Clas
Sam









































Complete Access Rules:

ge

























Same

❌ No
Packa









✅ Yes
✅ Yes








✅ Yes
















































PRIVATE
DEFAULT








❌ No
(Same

✅ Yes
✅ Yes
✅ Yes

PUBLIC








(Everywhere)
PROTECTED








Package)
Subclass

(Same Package)
















(Same Class Only)








































(Same Package + Subclasses)






































❌ No
❌ No

✅ Yes
✅ Yes

















Package)
Subclass
(Different







































❌ No
❌ No
❌ No

✅ Yes
Package
Different
When to Use Which Modi er

Decision Flowchart:

public class AccessDecisionExample {

// Use PRIVATE for:


private String internalState; // Internal
implementation details
private double sensitiveData; // Sensitive
information
private void helperMethod() { } // Internal helper
methods

// Use DEFAULT for:


String packageUtility; // Package-internal
utilities
void packageMethod() { } // Package-internal
methods

// Use PROTECTED for:


protected String inheritableField; // Data for subclasses
protected void templateMethod() { } // Methods for
inheritance

// Use PUBLIC for:


public String publicInterface; // Public API
public void publicMethod() { } // Public service
methods
public static void main(String[] args) { } // Entry point
}

Guidelines:

Use Private when:

• Internal implementation details


• Sensitive data (passwords, account numbers)
• Helper methods not needed outside
• Variables that shouldn't be modi ed directly
Use Default when:
fi
fi
• Package-internal utilities
• Classes/methods used only within package
• Implementation details shared within package
• Testing utilities for package
Use Protected when:

• Data/methods needed by subclasses


• Template methods for inheritance
• Extension points for framework users
• Controlled inheritance access
Use Public when:

• Public API of your class


• Methods intended for external use
• Constants that should be globally accessible
• Main method (required by JVM)

Important Points for Protected

Key Rule for Protected Access:

Protected members are accessible to subclasses in different packages, but NOT


to non-subclass instances in different packages.

Example Demonstrating the Rule:

// File: Parent.java
package com.base;

public class Parent {


protected int protectedValue = 100;

protected void protectedMethod() {


System.out.println("Protected method called");
}
}

// File: Child.java
package com.derived; // Different package

import com.base.Parent;

public class Child extends Parent {


public void testProtectedAccess() {
// ✅ OK - Accessing through inheritance
this.protectedValue = 200;
this.protectedMethod();

// ✅ OK - Direct access in subclass


protectedValue = 300;
protectedMethod();
}

public void testOtherInstances() {


Parent otherParent = new Parent();
Child otherChild = new Child();

// ❌ ERROR - Cannot access protected members of other


instances
// otherParent.protectedValue = 400; // Not allowed!
// otherParent.protectedMethod(); // Not allowed!

// ✅ OK - Same class instances


otherChild.protectedValue = 500;
otherChild.protectedMethod();
}
}

Why This Restriction Exists:

1. Encapsulation - Prevents breaking encapsulation through backdoor access


2. Design Intent - Protected is for inheritance, not general access
3. Security - Prevents misuse of protected members
Java Packages

What are Packages?

Packages are containers that organize related classes and interfaces. They provide:

• Namespace management
• Access control
• Code organization
• Collision avoidance
Types of Packages:

1. User-De ned Packages:

// Creating your own package


package com.company.project.utilities;

public class MyUtility {


public static void helper() {
System.out.println("Custom utility method");
}
}

2. Built-in Packages:

Java provides many built-in packages with pre-written classes.

Built-in Packages

1. java.lang Package

Most fundamental package - automatically imported in every Java program.

// These are from java.lang (auto-imported)


String name = "Java"; // String class
System.out.println("Hello"); // System class
Object obj = new Object(); // Object class
Math.sqrt(25); // Math class
Integer.parseInt("123"); // Integer wrapper class

Key Classes in java.lang:


fi
• Object - Root of all classes
• String - String manipulation
• System - System operations
• Math - Mathematical operations
• Thread - Threading support
• Wrapper classes (Integer, Double, etc.)
2. java.io Package

Input/Output operations - le handling, streams, etc.

import java.io.*;

public class FileExample {


public static void main(String[] args) {
try {
// File operations
File file = new File("example.txt");
FileWriter writer = new FileWriter(file);
writer.write("Hello World!");
writer.close();

// Reading file
FileReader reader = new FileReader(file);
BufferedReader bufferedReader = new
BufferedReader(reader);
String line = bufferedReader.readLine();
System.out.println(line);
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

3. java.util Package

Utility classes - collections, date/time, random numbers, etc.

import java.util.*;

public class UtilExample {


fi
public static void main(String[] args) {
// Collections
ArrayList<String> list = new ArrayList<>();
HashMap<String, Integer> map = new HashMap<>();

// Date and Time


Date now = new Date();
Calendar cal = Calendar.getInstance();

// Random numbers
Random random = new Random();
int randomNum = random.nextInt(100);

// Scanner for input


Scanner scanner = new Scanner(System.in);
}
}

4. java.awt Package

Abstract Window Toolkit - GUI components (mostly legacy).

import java.awt.*;

public class AWTExample {


public static void main(String[] args) {
Frame frame = new Frame("AWT Example");
Button button = new Button("Click Me");
frame.add(button);
frame.setSize(300, 200);
frame.setVisible(true);
}
}

5. java.net Package

Networking - URL handling, sockets, network connections.

import java.net.*;

public class NetworkExample {


public static void main(String[] args) {
try {
URL url = new URL("https://www.example.com");
URLConnection connection = url.openConnection();

// Socket programming
Socket socket = new Socket("localhost", 8080);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Object Class and Its Methods

The Object Class

Every class in Java inherits from Object class directly or indirectly.

// These are equivalent


public class MyClass {
// Implicitly extends Object
}

public class MyClass extends Object {


// Explicitly extends Object
}

Important Methods in Object Class:

1. toString() Method

public class Student {


private String name;
private int age;

public Student(String name, int age) {


this.name = name;
this.age = age;
}
// Without overriding toString()
// Output: Student@1a2b3c4d (className@hashCode)

// Overriding toString()
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}

public class ToStringDemo {


public static void main(String[] args) {
Student student = new Student("John", 20);
System.out.println(student); // Calls toString()
automatically
// Output: Student{name='John', age=20}
}
}

2. hashCode() Method

public class HashCodeExample {


public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();

System.out.println("obj1 hashCode: " + obj1.hashCode());


System.out.println("obj2 hashCode: " + obj2.hashCode());

// Different objects have different hash codes


// Hash code is NOT the memory address (common
misconception)
// It's a number generated by mathematical algorithm
}
}

3. equals() Method

public class EqualsExample {


public static void main(String[] args) {
String str1 = new String("Hello");
String str2 = new String("Hello");
String str3 = str1;

// == compares references
System.out.println(str1 == str2); // false
(different objects)
System.out.println(str1 == str3); // true (same
reference)

// equals() compares content (for String)


System.out.println(str1.equals(str2)); // true (same
content)
System.out.println(str1.equals(str3)); // true (same
content)
}
}

Custom equals() implementation:

public class Person {


private String name;
private int age;

public Person(String name, int age) {


this.name = name;
this.age = age;
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return
false;

Person person = (Person) obj;


return age == person.age && Objects.equals(name,
person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}

4. getClass() Method

public class GetClassExample {


public static void main(String[] args) {
String str = "Hello";
Integer num = 42;
Object obj = new Object();

System.out.println(str.getClass()); // class
java.lang.String
System.out.println(num.getClass()); // class
java.lang.Integer
System.out.println(obj.getClass()); // class
java.lang.Object

// Getting class name


System.out.println(str.getClass().getSimpleName()); //
String
}
}

5. instanceof Operator

public class InstanceOfExample {


public static void main(String[] args) {
String str = "Hello";
Object obj = str;

System.out.println(str instanceof String); // true


System.out.println(str instanceof Object); // true
System.out.println(obj instanceof String); // true

// Checking before casting


if (obj instanceof String) {
String s = (String) obj; // Safe casting
System.out.println("Length: " + s.length());
}
}
}

Practical Examples

Example 1: Complete Access Control System

// File: User.java
package com.security;

public class User {


private String username; // Private - sensitive
data
private String password; // Private - security
critical
protected String email; // Protected - for
subclasses
String department; // Default - package access
public String displayName; // Public - general access

public User(String username, String password, String email)


{
this.username = username;
this.password = hashPassword(password); // Hash before
storing
this.email = email;
this.displayName = username;
}

// Public methods for controlled access


public boolean authenticate(String inputPassword) {
return
this.password.equals(hashPassword(inputPassword));
}

public String getUsername() {


return username;
}
// Protected method for subclasses
protected void updateEmail(String newEmail) {
this.email = newEmail;
}

// Private helper method


private String hashPassword(String password) {
return "hashed_" + password; // Simplified hashing
}

// Package-private method
void setDepartment(String department) {
this.department = department;
}

@Override
public String toString() {
return "User{username='" + username + "', email='" +
email + "'}";
}
}

// File: Admin.java
package com.security;

public class Admin extends User {


private String adminLevel;

public Admin(String username, String password, String email,


String adminLevel) {
super(username, password, email);
this.adminLevel = adminLevel;
}

public void promoteUser(User user) {


// Can access protected members
user.updateEmail(user.email + ".admin"); // ✅ OK -
protected access
user.setDepartment("ADMIN"); // ✅ OK -
same package
}

// Cannot access private members


// user.password = "new"; // ❌ ERROR - private
}

Example 2: Package Organization Example

// File: com/company/models/Product.java
package com.company.models;

public class Product {


private String id;
protected String name;
protected double price;

public Product(String id, String name, double price) {


this.id = id;
this.name = name;
this.price = price;
}

public String getId() { return id; }


public String getName() { return name; }
public double getPrice() { return price; }

@Override
public String toString() {
return "Product{id='" + id + "', name='" + name + "',
price=" + price + "}";
}
}

// File: com/company/services/ProductService.java
package com.company.services;

import com.company.models.Product;
import java.util.ArrayList;
import java.util.List;

public class ProductService {


private List<Product> products = new ArrayList<>();

public void addProduct(Product product) {


products.add(product);
}

public Product findProductById(String id) {


return products.stream()
.filter(p -> p.getId().equals(id))
.findFirst()
.orElse(null);
}

public void displayAllProducts() {


products.forEach(System.out::println);
}
}

Best Practices:

1. Start with most restrictive (private) and open up as needed


2. Use public sparingly - only for intended interface
3. Protected for inheritance - template methods and extension points
4. Default for package utilities - internal implementations
5. Always override toString() for better debugging

Decision Matrix:

Need Use Reason

Hide implementation details private Maximum encapsulation

Share within package default Package-level cooperation

Enable inheritance protected Subclass extension


Provide public API public External interface

Memory of Access Levels:

private < default < protected < public


↑ ↑ ↑ ↑
Most Package Package + Everything
Secure Only Subclass Accessible

This comprehensive understanding of access control and packages forms the


foundation for writing secure, well-organized, and maintainable Java applications.

Custom ArrayList Implementation

Understanding ArrayList Internals

Before diving into the built-in ArrayList, let's understand how it works internally by
creating our own custom implementation.

Basic Custom ArrayList:

public class CustomArrayList {


private Object[] data;
private static int DEFAULT_SIZE = 10;
private int size = 0; // Number of elements currently in the
list

// Constructor
public CustomArrayList() {
this.data = new Object[DEFAULT_SIZE];
}

// Add element
public void add(Object item) {
if (isFull()) {
resize();
}
data[size++] = item;
}
// Check if array is full
private boolean isFull() {
return size == data.length;
}

// Resize array when full


private void resize() {
Object[] temp = new Object[data.length * 2];

// Copy all elements to new array


for (int i = 0; i < data.length; i++) {
temp[i] = data[i];
}

data = temp;
}

// Remove element
public void remove(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " +
index);
}

// Shift elements to the left


for (int i = index; i < size - 1; i++) {
data[i] = data[i + 1];
}
size--;
}

// Get element
public Object get(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " +
index);
}
return data[index];
}
// Set element
public void set(int index, Object value) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " +
index);
}
data[index] = value;
}

// Get size
public int size() {
return size;
}

// Display all elements


@Override
public String toString() {
StringBuilder result = new StringBuilder("[");
for (int i = 0; i < size; i++) {
result.append(data[i]);
if (i < size - 1) {
result.append(", ");
}
}
result.append("]");
return result.toString();
}
}

Usage Example:

public class CustomArrayListDemo {


public static void main(String[] args) {
CustomArrayList list = new CustomArrayList();

// Adding elements
list.add("Hello");
list.add("World");
list.add(123);
list.add(45.67);
System.out.println("List: " + list);
System.out.println("Size: " + list.size());

// Accessing elements
System.out.println("Element at index 1: " +
list.get(1));

// Removing elements
list.remove(2);
System.out.println("After removal: " + list);
}
}

Problems with Custom ArrayList:

1. Type Safety: Can store any type of object


2. Casting Required: Need to cast when retrieving elements
3. Runtime Errors: Type errors only caught at runtime
4. No Compile-time Checking: Cannot ensure type consistency

Java Generics

What are Generics?

Generics enable types (classes and interfaces) to be parameters when de ning


classes, interfaces, and methods. They provide:

• Type Safety at compile time


• Elimination of type casting
• Generic algorithms that work on collections of different types
Key Bene ts:

1. Compile-time Type Checking: Errors caught at compile time


2. No Type Casting: Eliminates explicit casting
3. Generic Algorithms: Code reusability across different types
Generic Custom ArrayList:

public class CustomArrayList<T> {


private Object[] data;
private static int DEFAULT_SIZE = 10;
private int size = 0;
fi
fi
public CustomArrayList() {
this.data = new Object[DEFAULT_SIZE];
}

public void add(T item) {


if (isFull()) {
resize();
}
data[size++] = item;
}

private boolean isFull() {


return size == data.length;
}

private void resize() {


Object[] temp = new Object[data.length * 2];
for (int i = 0; i < data.length; i++) {
temp[i] = data[i];
}
data = temp;
}

@SuppressWarnings("unchecked")
public T get(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " +
index);
}
return (T) data[index];
}

public void set(int index, T value) {


if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " +
index);
}
data[index] = value;
}
public int size() {
return size;
}

public void remove(int index) {


if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " +
index);
}
for (int i = index; i < size - 1; i++) {
data[i] = data[i + 1];
}
size--;
}

@Override
public String toString() {
StringBuilder result = new StringBuilder("[");
for (int i = 0; i < size; i++) {
result.append(data[i]);
if (i < size - 1) {
result.append(", ");
}
}
result.append("]");
return result.toString();
}
}

Usage with Type Safety:

public class GenericDemo {


public static void main(String[] args) {
// String list
CustomArrayList<String> stringList = new
CustomArrayList<>();
stringList.add("Hello");
stringList.add("World");
// stringList.add(123); // Compilation error!
// Integer list
CustomArrayList<Integer> intList = new
CustomArrayList<>();
intList.add(10);
intList.add(20);
// intList.add("Hello"); // Compilation error!

System.out.println("String List: " + stringList);


System.out.println("Integer List: " + intList);

// No casting required
String firstString = stringList.get(0);
Integer firstInt = intList.get(0);
}
}

Generic Type Parameters:

// Single type parameter


class Box<T> {
private T content;

public void set(T content) {


this.content = content;
}

public T get() {
return content;
}
}

// Multiple type parameters


class Pair<K, V> {
private K key;
private V value;

public Pair(K key, V value) {


this.key = key;
this.value = value;
}

public K getKey() { return key; }


public V getValue() { return value; }
}

Generic Methods:

public class GenericMethods {


// Generic static method
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}

// Generic instance method


public <E> void printArray(E[] array) {
for (E element : array) {
System.out.print(element + " ");
}
System.out.println();
}

public static void main(String[] args) {


String[] strings = {"Hello", "World", "Java"};
Integer[] numbers = {1, 2, 3, 4, 5};

GenericMethods gm = new GenericMethods();

System.out.println("Before swap:");
gm.printArray(strings);

swap(strings, 0, 2);

System.out.println("After swap:");
gm.printArray(strings);

gm.printArray(numbers);
}
}

Wildcards in Generics

What are Wildcards?

Wildcards are represented by the question mark (?) and are used to represent an
unknown type in generics.

Types of Wildcards:

1. Unbounded Wildcard (?):

import java.util.*;

public class WildcardDemo {


// Method that accepts list of any type
public static void printList(List<?> list) {
for (Object item : list) {
System.out.print(item + " ");
}
System.out.println();
}

public static void main(String[] args) {


List<String> stringList = Arrays.asList("Hello",
"World");
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);

printList(stringList); // Works
printList(intList); // Works
printList(doubleList); // Works
}
}

2. Upper Bounded Wildcard (? extends Type):

import java.util.*;
public class UpperBoundedWildcard {
// Only accepts lists of Number or its subtypes
public static double sumList(List<? extends Number> list) {
double sum = 0.0;
for (Number number : list) {
sum += number.doubleValue();
}
return sum;
}

public static void main(String[] args) {


List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
List<Float> floatList = Arrays.asList(1.1f, 2.2f, 3.3f);

System.out.println("Sum of integers: " +


sumList(intList));
System.out.println("Sum of doubles: " +
sumList(doubleList));
System.out.println("Sum of floats: " +
sumList(floatList));

// List<String> stringList = Arrays.asList("1", "2",


"3");
// sumList(stringList); // Compilation error!
}
}

3. Lower Bounded Wildcard (? super Type):

import java.util.*;

public class LowerBoundedWildcard {


// Only accepts lists of Integer or its supertypes
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
list.add(3);
}
public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
List<Number> numberList = new ArrayList<>();
List<Object> objectList = new ArrayList<>();

addNumbers(intList); // Works
addNumbers(numberList); // Works
addNumbers(objectList); // Works

System.out.println("Integer list: " + intList);


System.out.println("Number list: " + numberList);
System.out.println("Object list: " + objectList);
}
}

Comparable and Comparator

The Need for Comparison

When working with custom objects, we often need to sort or compare them. Java
provides two main interfaces for this purpose:

1. Comparable Interface

Comparable interface is used to de ne the natural ordering of objects.

public class Student implements Comparable<Student> {


private int rollNumber;
private String name;
private double marks;

public Student(int rollNumber, String name, double marks) {


this.rollNumber = rollNumber;
this.name = name;
this.marks = marks;
}

// Implementing compareTo method for natural ordering


@Override
public int compareTo(Student other) {
fi
// Sort by roll number (ascending)
return Integer.compare(this.rollNumber,
other.rollNumber);

// Alternative: Sort by marks (descending)


// return Double.compare(other.marks, this.marks);
}

// Getters
public int getRollNumber() { return rollNumber; }
public String getName() { return name; }
public double getMarks() { return marks; }

@Override
public String toString() {
return "Student{rollNumber=" + rollNumber + ", name='" +
name +
"', marks=" + marks + "}";
}
}

Using Comparable:

import java.util.*;

public class ComparableDemo {


public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student(3, "Kunal", 85.5));
students.add(new Student(1, "Rahul", 92.0));
students.add(new Student(2, "Arpit", 78.5));
students.add(new Student(5, "Karan", 89.0));
students.add(new Student(4, "Sachin", 95.5));

System.out.println("Before sorting:");
for (Student student : students) {
System.out.println(student);
}

// Sort using natural ordering (Comparable)


Collections.sort(students);

System.out.println("\nAfter sorting by roll number:");


for (Student student : students) {
System.out.println(student);
}
}
}

2. Comparator Interface

Comparator interface is used to de ne custom ordering logic separate from the class.

import java.util.*;

public class ComparatorDemo {


public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student(3, "Kunal", 85.5));
students.add(new Student(1, "Rahul", 92.0));
students.add(new Student(2, "Arpit", 78.5));
students.add(new Student(5, "Karan", 89.0));
students.add(new Student(4, "Sachin", 95.5));

// Sort by marks (descending)


Comparator<Student> markComparator = new
Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return Double.compare(s2.getMarks(),
s1.getMarks());
}
};

Collections.sort(students, markComparator);

System.out.println("Sorted by marks (descending):");


for (Student student : students) {
System.out.println(student);
}
fi
// Sort by name (ascending)
Comparator<Student> nameComparator = new
Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return s1.getName().compareTo(s2.getName());
}
};

Collections.sort(students, nameComparator);

System.out.println("\nSorted by name (ascending):");


for (Student student : students) {
System.out.println(student);
}
}
}

Comparable vs Comparator:

Aspect Comparable Comparator

Package java.lang java.util

Method compareTo(T obj) compare(T obj1, T obj2)

Sorting Logic Natural ordering Custom ordering

Implementation Inside the class Separate class/anonymous

Modi cation Modify original class No need to modify class

Multiple Sorts One way only Multiple sorting criteria

Lambda Functions

Lambda expressions provide a concise way to represent anonymous functions. They


enable functional programming in Java.

Syntax:

// Basic syntax
fi
(parameters) -> expression
(parameters) -> { statements; }

Functional Interfaces

Lambda expressions work with functional interfaces - interfaces with exactly one
abstract method.

// Custom functional interface


@FunctionalInterface
interface Operation {
int operate(int a, int b);
}

public class LambdaDemo {


public static void main(String[] args) {
// Traditional anonymous class approach
Operation addition = new Operation() {
@Override
public int operate(int a, int b) {
return a + b;
}
};

// Lambda expression approach


Operation additionLambda = (a, b) -> a + b;
Operation subtraction = (a, b) -> a - b;
Operation multiplication = (a, b) -> a * b;
Operation division = (a, b) -> {
if (b != 0) {
return a / b;
} else {
throw new ArithmeticException("Division by
zero");
}
};

System.out.println("Addition: " +
additionLambda.operate(10, 5));
System.out.println("Subtraction: " +
subtraction.operate(10, 5));
System.out.println("Multiplication: " +
multiplication.operate(10, 5));
System.out.println("Division: " + division.operate(10,
5));
}
}

Lambda with Collections:

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class LambdaCollections {


public static void main(String[] args) {
List<String> names = Arrays.asList("Kunal", "Rahul",
"Arpit", "Karan", "Sachin");

// Using lambda with forEach


System.out.println("Names:");
names.forEach(name -> System.out.println(name));

// Using method reference


names.forEach(System.out::println);

// Filtering with lambda


System.out.println("\nNames starting with 'K':");
names.stream()
.filter(name -> name.startsWith("K"))
.forEach(System.out::println);

// Sorting with lambda


List<String> sortedNames = new ArrayList<>(names);
sortedNames.sort((name1, name2) ->
name1.compareTo(name2));

System.out.println("\nSorted names:");
sortedNames.forEach(System.out::println);

// Using Consumer functional interface


Consumer<String> printer = item ->
System.out.println("Processing: " + item);
names.forEach(printer);
}
}

Common Functional Interfaces:

import java.util.function.*;

public class FunctionalInterfacesDemo {


public static void main(String[] args) {
// Consumer - takes input, returns nothing
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello World");

// Supplier - takes nothing, returns output


Supplier<Double> randomGenerator = () -> Math.random();
System.out.println("Random number: " +
randomGenerator.get());

// Function - takes input, returns output


Function<String, Integer> lengthCalculator = s ->
s.length();
System.out.println("Length of 'Java': " +
lengthCalculator.apply("Java"));

// Predicate - takes input, returns boolean


Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println("Is 10 even? " + isEven.test(10));
System.out.println("Is 7 even? " + isEven.test(7));

// BiFunction - takes two inputs, returns output


BiFunction<Integer, Integer, Integer> adder = (a, b) ->
a + b;
System.out.println("Sum: " + adder.apply(5, 3));
}
}

Exception Handling
What are Exceptions?

Exceptions are runtime errors that disrupt the normal ow of program execution. Java
provides a robust exception handling mechanism to handle these errors gracefully.

Exception Hierarchy:

Throwable
Error (System-level errors, usually not handled)
Exception
RuntimeException (Unchecked exceptions)
ArithmeticException
NullPointerException
IndexOutOfBoundsException
...
Checked Exceptions
IOException
SQLException
ClassNotFoundException
...

Basic Exception Handling:

public class ExceptionHandlingBasics {


public static void main(String[] args) {
// Basic try-catch
try {
int result = 10 / 0; // ArithmeticException
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Error: Cannot divide by zero!");
System.out.println("Exception message: " +
e.getMessage());
}

System.out.println("Program continues...");
}
}

Multiple Catch Blocks:










































fl
public class MultipleCatchDemo {
public static void main(String[] args) {
try {
String[] names = {"Kunal", "Rahul", "Arpit"};
System.out.println("Name at index 5: " +
names[5]); // ArrayIndexOutOfBoundsException

String name = null;


System.out.println("Length: " + name.length()); //
NullPointerException

int result = 10 / 0; // ArithmeticException

} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Array index out of bounds: " +
e.getMessage());
} catch (NullPointerException e) {
System.out.println("Null pointer exception: " +
e.getMessage());
} catch (ArithmeticException e) {
System.out.println("Arithmetic exception: " +
e.getMessage());
} catch (Exception e) {
System.out.println("General exception: " +
e.getMessage());
}
}
}

Try-Catch-Finally:

import java.io.*;

public class TryCatchFinallyDemo {


public static void main(String[] args) {
FileReader file = null;

try {
file = new FileReader("test.txt");
System.out.println("File opened successfully");
// File operations...

} catch (FileNotFoundException e) {
System.out.println("File not found: " +
e.getMessage());
} finally {
// This block always executes
System.out.println("Cleanup operations...");
if (file != null) {
try {
file.close();
System.out.println("File closed");
} catch (IOException e) {
System.out.println("Error closing file: " +
e.getMessage());
}
}
}

System.out.println("Program ended");
}
}

Throwing Exceptions:

public class ThrowingExceptions {


// Method that throws checked exception
public static void validateAge(int age) throws
IllegalArgumentException {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be
negative: " + age);
}
if (age > 150) {
throw new IllegalArgumentException("Age cannot be
greater than 150: " + age);
}
System.out.println("Valid age: " + age);
}
// Method that throws unchecked exception
public static int divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("Division by zero is
not allowed");
}
return a / b;
}

public static void main(String[] args) {


try {
validateAge(25); // Valid
validateAge(-5); // Throws exception
} catch (IllegalArgumentException e) {
System.out.println("Validation error: " +
e.getMessage());
}

try {
int result = divide(10, 2);
System.out.println("Result: " + result);

result = divide(10, 0); // Throws exception


System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Math error: " + e.getMessage());
}
}
}

Creating Custom Exceptions:

// Custom checked exception


class MyCustomException extends Exception {
public MyCustomException(String message) {
super(message);
}
}

// Custom unchecked exception


class MyRuntimeException extends RuntimeException {
public MyRuntimeException(String message) {
super(message);
}
}

public class CustomExceptionDemo {


public static void validateName(String name) throws
MyCustomException {
if (name == null || name.equals("Kunal")) {
throw new MyCustomException("Name cannot be null or
'Kunal'");
}
System.out.println("Valid name: " + name);
}

public static void checkPositive(int number) {


if (number <= 0) {
throw new MyRuntimeException("Number must be
positive: " + number);
}
System.out.println("Positive number: " + number);
}

public static void main(String[] args) {


// Handling custom checked exception
try {
validateName("Rahul"); // Valid
validateName("Kunal"); // Throws exception
} catch (MyCustomException e) {
System.out.println("Custom exception: " +
e.getMessage());
}

// Handling custom unchecked exception


try {
checkPositive(10); // Valid
checkPositive(-5); // Throws exception
} catch (MyRuntimeException e) {
System.out.println("Runtime exception: " +
e.getMessage());
}
}
}

Object Cloning

What is Object Cloning?

Object cloning is the process of creating an exact copy of an object. Java provides
the clone() method in the Object class for this purpose.

Cloneable Interface:

To clone an object, the class must implement the Cloneable interface and override
the clone() method.

public class Human implements Cloneable {


private String name;
private int age;
private int[] marks;

public Human(String name, int age) {


this.name = name;
this.age = age;
this.marks = new int[5];
}

// Getters and setters


public String getName() { return name; }
public void setName(String name) { this.name = name; }

public int getAge() { return age; }


public void setAge(int age) { this.age = age; }

public int[] getMarks() { return marks; }


public void setMarks(int[] marks) { this.marks = marks; }

// Override clone method


@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}

@Override
public String toString() {
return "Human{name='" + name + "', age=" + age +
", marks=" + java.util.Arrays.toString(marks) +
"}";
}
}

Using Object Cloning:

public class CloningDemo {


public static void main(String[] args) {
try {
Human original = new Human("Kunal", 25);
original.getMarks()[0] = 85;
original.getMarks()[1] = 90;

System.out.println("Original: " + original);

// Clone the object


Human cloned = (Human) original.clone();

System.out.println("Cloned: " + cloned);

// Modify original
original.setName("Kunal Kushwaha");
original.setAge(26);
original.getMarks()[0] = 95;

System.out.println("\nAfter modifying original:");


System.out.println("Original: " + original);
System.out.println("Cloned: " + cloned);

} catch (CloneNotSupportedException e) {
System.out.println("Clone not supported: " +
e.getMessage());
}
}
}

Shallow vs Deep Copy

Understanding the Difference

The difference between shallow and deep copy becomes apparent when dealing with
objects that contain references to other objects.

Shallow Copy

In shallow copy, only the object itself is copied, but the references to other objects are
shared.

public class ShallowCopyDemo {


public static void main(String[] args) {
try {
Human original = new Human("Kunal", 25);
original.getMarks()[0] = 85;
original.getMarks()[1] = 90;

// Shallow copy using clone()


Human shallowCopy = (Human) original.clone();

System.out.println("Before modification:");
System.out.println("Original: " + original);
System.out.println("Shallow Copy: " + shallowCopy);

// Modify primitive field


shallowCopy.setName("Changed Name");
shallowCopy.setAge(30);

// Modify array (reference type)


shallowCopy.getMarks()[0] = 100;

System.out.println("\nAfter modification:");
System.out.println("Original: " + original);
System.out.println("Shallow Copy: " + shallowCopy);

// Notice: Array changes affect both objects!

} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}

Deep Copy

In deep copy, both the object and all objects it references are copied recursively.

public class HumanDeepCopy implements Cloneable {


private String name;
private int age;
private int[] marks;

public HumanDeepCopy(String name, int age) {


this.name = name;
this.age = age;
this.marks = new int[5];
}

// Getters and setters


public String getName() { return name; }
public void setName(String name) { this.name = name; }

public int getAge() { return age; }


public void setAge(int age) { this.age = age; }

public int[] getMarks() { return marks; }


public void setMarks(int[] marks) { this.marks = marks; }

// Deep copy implementation


@Override
public Object clone() throws CloneNotSupportedException {
HumanDeepCopy cloned = (HumanDeepCopy) super.clone();
// Create new array and copy elements
cloned.marks = new int[this.marks.length];
for (int i = 0; i < this.marks.length; i++) {
cloned.marks[i] = this.marks[i];
}

return cloned;
}

@Override
public String toString() {
return "HumanDeepCopy{name='" + name + "', age=" + age +
", marks=" + java.util.Arrays.toString(marks) +
"}";
}
}

Deep Copy Demo:

public class DeepCopyDemo {


public static void main(String[] args) {
try {
HumanDeepCopy original = new HumanDeepCopy("Kunal",
25);
original.getMarks()[0] = 85;
original.getMarks()[1] = 90;

// Deep copy
HumanDeepCopy deepCopy = (HumanDeepCopy)
original.clone();

System.out.println("Before modification:");
System.out.println("Original: " + original);
System.out.println("Deep Copy: " + deepCopy);

// Modify both primitive and reference fields


deepCopy.setName("Changed Name");
deepCopy.setAge(30);
deepCopy.getMarks()[0] = 100;
System.out.println("\nAfter modification:");
System.out.println("Original: " + original);
System.out.println("Deep Copy: " + deepCopy);

// Notice: Array changes don't affect original


object!

} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}

Shallow vs Deep Copy Comparison:

Aspect Shallow Copy Deep Copy

Primitive
Copied independently Copied independently
Fields

Reference Reference copied (shared New objects created


Fields objects) (independent)

Memory
Less memory More memory
Usage

Performance Faster Slower

Side Effects Changes affect both objects Changes independent

Implementati Custom clone() implementat


Default clone() method
on ion

Alternative Approaches to Cloning:

1. Copy Constructor:

public class HumanCopyConstructor {


private String name;
private int age;
private int[] marks;
// Regular constructor
public HumanCopyConstructor(String name, int age) {
this.name = name;
this.age = age;
this.marks = new int[5];
}

// Copy constructor for deep copy


public HumanCopyConstructor(HumanCopyConstructor other) {
this.name = other.name;
this.age = other.age;
this.marks = new int[other.marks.length];
System.arraycopy(other.marks, 0, this.marks, 0,
other.marks.length);
}

// Getters, setters, toString...


}

2. Factory Method:

public class HumanFactory {


public static HumanDeepCopy createCopy(HumanDeepCopy
original) {
HumanDeepCopy copy = new
HumanDeepCopy(original.getName(), original.getAge());
System.arraycopy(original.getMarks(), 0,
copy.getMarks(), 0, original.getMarks().length);
return copy;
}
}

Best Practices:

1. Use generics for type safety and code reusability


2. Prefer Comparator over Comparable for exibility
3. Use lambda expressions for cleaner, more readable code
4. Handle exceptions properly with appropriate try-catch blocks
5. Understand shallow vs deep copy implications
6. Consider alternatives to cloning like copy constructors
This comprehensive coverage provides a solid foundation for understanding advanced
Java concepts including collections, generics, functional programming, exception
fl
handling, and object manipulation techniques essential for professional Java
development.

Abstract Classes and Interfaces in Java - Complete Guide

Multiple Inheritance Problem

Why Multiple Inheritance is Not Supported in Java

The Diamond Problem occurs when a class inherits from multiple parent classes that
have methods with the same name but different implementations.

Example of the Problem:

// This is NOT allowed in Java


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

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

// This would cause ambiguity - which display() method to


inherit?
class Child extends Parent1, Parent2 { // ERROR: Multiple
inheritance not allowed
// Which display() method should be inherited?
// Parent1's display() or Parent2's display()?
}

Why Java Doesn't Support Multiple Inheritance:

1. Ambiguity - Confusion about which method to call


2. Diamond Problem - Complex inheritance hierarchies
3. Complexity - Makes language harder to understand and maintain
4. Solution - Use Interfaces for achieving multiple inheritance functionality

Abstract Classes

What are Abstract Classes?

Abstract classes are classes that:

• Cannot be instantiated directly


• May contain abstract methods (methods without body)
• Can contain normal methods with implementation
• Provide a template for child classes
• Force child classes to implement speci c methods
Key Characteristics:

1. Cannot create objects of abstract classes


2. Must be extended by child classes
3. Child classes must implement all abstract methods
4. Can have constructors (called by child classes)
5. Can have both abstract and concrete methods
When to Use Abstract Classes:

• When you want to provide a common template for related classes


• When you want to force certain methods to be implemented by child classes
• When you have some common functionality that can be shared
• When you want to partially implement a class

Abstract Classes Example

Basic Abstract Class:

// Abstract parent class


abstract class Shape {
protected String color;
protected boolean filled;

// Constructor in abstract class


public Shape(String color, boolean filled) {
this.color = color;
this.filled = filled;
}

// Abstract method - must be implemented by child classes


fi
abstract double calculateArea();
abstract double calculatePerimeter();

// Concrete method - shared by all child classes


public void displayInfo() {
System.out.println("Color: " + color);
System.out.println("Filled: " + filled);
System.out.println("Area: " + calculateArea());
System.out.println("Perimeter: " +
calculatePerimeter());
}

// Concrete method with implementation


public String getColor() {
return color;
}
}

Child Classes Implementation:

// Child class 1 - Circle


class Circle extends Shape {
private double radius;

public Circle(double radius, String color, boolean filled) {


super(color, filled); // Call parent constructor
this.radius = radius;
}

// Must implement abstract methods


@Override
double calculateArea() {
return Math.PI * radius * radius;
}

@Override
double calculatePerimeter() {
return 2 * Math.PI * radius;
}
}
// Child class 2 - Rectangle
class Rectangle extends Shape {
private double length, width;

public Rectangle(double length, double width, String color,


boolean filled) {
super(color, filled);
this.length = length;
this.width = width;
}

// Must implement abstract methods


@Override
double calculateArea() {
return length * width;
}

@Override
double calculatePerimeter() {
return 2 * (length + width);
}
}

Usage Example:

public class AbstractDemo {


public static void main(String[] args) {
// Cannot create object of abstract class
// Shape shape = new Shape(); // ERROR!

// Can create objects of child classes


Shape circle = new Circle(5.0, "Red", true);
Shape rectangle = new Rectangle(4.0, 6.0, "Blue",
false);

// Polymorphism in action
circle.displayInfo();
System.out.println("---");
rectangle.displayInfo();
}
}

Output:

Color: Red
Filled: true
Area: 78.54
Perimeter: 31.42
---
Color: Blue
Filled: false
Area: 24.0
Perimeter: 20.0

Abstract Constructors

Can Abstract Classes Have Constructors?

Yes! Abstract classes can have constructors, but they cannot be called directly to create
objects.

How Abstract Constructors Work:

abstract class Vehicle {


protected String engine;
protected int wheels;

// Constructor in abstract class


public Vehicle(String engine, int wheels) {
this.engine = engine;
this.wheels = wheels;
System.out.println("Vehicle constructor called");
}

// Abstract method
abstract void start();
}

class Car extends Vehicle {


private String brand;

// Child constructor must call parent constructor


public Car(String engine, int wheels, String brand) {
super(engine, wheels); // Calls abstract class
constructor
this.brand = brand;
System.out.println("Car constructor called");
}

@Override
void start() {
System.out.println(brand + " car with " + engine + "
engine is starting");
}
}

Usage:

public class ConstructorDemo {


public static void main(String[] args) {
// Vehicle v = new Vehicle("V8", 4); // ERROR: Cannot
instantiate abstract class

Car car = new Car("V6", 4, "BMW"); // OK: Calls both


constructors
car.start();
}
}

Output:

Vehicle constructor called


Car constructor called
BMW car with V6 engine is starting

Object Creation in Abstract Classes

Why Can't We Create Objects of Abstract Classes?


abstract class Payment {
abstract void processPayment(double amount);

void validatePayment() {
System.out.println("Payment validation logic");
}
}

public class ObjectCreationDemo {


public static void main(String[] args) {
// This is NOT allowed
// Payment payment = new Payment(); // ERROR!

// Reason: Abstract methods have no implementation


// If we could create objects, what would happen when we
call:
// payment.processPayment(100); // Which implementation
to call?
}
}

Solution - Reference Variables:

class CreditCard extends Payment {


@Override
void processPayment(double amount) {
System.out.println("Processing credit card payment: $" +
amount);
}
}

class PayPal extends Payment {


@Override
void processPayment(double amount) {
System.out.println("Processing PayPal payment: $" +
amount);
}
}

public class ReferenceDemo {


public static void main(String[] args) {
// Can use abstract class as reference type
Payment payment1 = new CreditCard(); // Polymorphism
Payment payment2 = new PayPal(); // Polymorphism

payment1.processPayment(100); // Calls CreditCard


implementation
payment2.processPayment(200); // Calls PayPal
implementation
}
}

Abstract Static Methods

Can Abstract Classes Have Static Methods?

Yes, but with restrictions:

1. Static methods can be de ned in abstract classes


2. Static methods CANNOT be abstract
3. Static methods have implementation in abstract classes
abstract class MathUtils {
// Abstract method - must be implemented by child classes
abstract double calculate(double x, double y);

// Static method - has implementation


public static double add(double a, double b) {
return a + b;
}

// Static method - can be called without object


public static void printInfo() {
System.out.println("Math utility class");
}

// This is NOT allowed


// abstract static void invalidMethod(); // ERROR!
}

class Calculator extends MathUtils {


fi
@Override
double calculate(double x, double y) {
return x * y; // Multiplication
}
}

Usage:

public class StaticMethodDemo {


public static void main(String[] args) {
// Can call static methods without creating objects
MathUtils.printInfo();
double sum = MathUtils.add(10, 20);
System.out.println("Sum: " + sum);

// Need object for abstract method implementation


Calculator calc = new Calculator();
double result = calc.calculate(5, 6);
System.out.println("Result: " + result);
}
}

Final Keyword in Abstract Classes

Can Abstract Classes be Final?

No! This would be contradictory because:

// This is NOT allowed


final abstract class InvalidClass { // ERROR!
abstract void method();
}

Why not allowed:

1. nal means "cannot be extended"


2. abstract means "must be extended to be useful"
3. Contradiction - cannot both require and prevent inheritance
Final Methods in Abstract Classes:

abstract class BaseProcessor {


fi
// Final method - cannot be overridden
final void initialize() {
System.out.println("Processor initialized - this cannot
be changed");
}

// Abstract method - must be implemented


abstract void process();

// Normal method - can be overridden


void cleanup() {
System.out.println("Default cleanup");
}
}

class DataProcessor extends BaseProcessor {


@Override
void process() {
System.out.println("Processing data...");
}

// Cannot override final method


// void initialize() { } // ERROR!

// Can override normal method


@Override
void cleanup() {
System.out.println("Custom cleanup for data processor");
}
}

Multiple Inheritance with Abstract Classes

Limitation:

Java does not support multiple inheritance even with abstract classes:

abstract class Engine {


abstract void start();
}
abstract class MediaPlayer {
abstract void playMusic();
}

// This is NOT allowed


class Car extends Engine, MediaPlayer { // ERROR!
// Cannot extend multiple classes
}

Solution - Use Interfaces:

This limitation leads us to Interfaces, which solve the multiple inheritance problem.

Interfaces

What are Interfaces?

Interfaces are:

• Pure abstract classes (before Java 8)


• Contracts that specify what a class must do, not how
• Collections of abstract methods and constants
• Solution to multiple inheritance problem
Key Characteristics:

1. All methods are public and abstract by default (before Java 8)


2. All variables are public, static, and nal by default
3. Cannot be instantiated directly
4. Can be implemented by multiple classes
5. A class can implement multiple interfaces
6. Interfaces can extend other interfaces
Interface vs Abstract Class:

Interface Abstract Class

Pure abstraction Partial abstraction

Only abstract methods (before Java 8) Both abstract and concrete methods

Multiple inheritance supported Single inheritance only


fi
All methods public by default Various access modi ers

All variables static nal Normal variables allowed

implements keyword extends keyword

Interface Examples

Basic Interface De nition:

// Interface definition
public interface Engine {
// All methods are public abstract by default
void start();
void stop();
void accelerate();
}

// Another interface
public interface MediaPlayer {
void playMusic();
void stopMusic();
}

// Interface with constants


public interface Constants {
// All variables are public static final by default
int MAX_SPEED = 200;
String DEFAULT_BRAND = "Generic";
}

Implementing Interfaces:

// Class implementing single interface


class PetrolEngine implements Engine {
@Override
public void start() {
System.out.println("Petrol engine starting with
ignition");
}
fi
fi
fi
@Override
public void stop() {
System.out.println("Petrol engine stopping");
}

@Override
public void accelerate() {
System.out.println("Petrol engine accelerating");
}
}

// Class implementing multiple interfaces


class SmartCar implements Engine, MediaPlayer {
private String engineType;

public SmartCar(String engineType) {


this.engineType = engineType;
}

// Implementing Engine interface


@Override
public void start() {
System.out.println(engineType + " engine starting
silently");
}

@Override
public void stop() {
System.out.println(engineType + " engine stopping");
}

@Override
public void accelerate() {
System.out.println(engineType + " engine accelerating
smoothly");
}

// Implementing MediaPlayer interface


@Override
public void playMusic() {
System.out.println("Playing music in smart car");
}

@Override
public void stopMusic() {
System.out.println("Stopping music in smart car");
}
}

Usage Example:

public class InterfaceDemo {


public static void main(String[] args) {
// Create objects of implementing classes
Engine petrolEngine = new PetrolEngine();
SmartCar tesla = new SmartCar("Electric");

// Use Engine interface methods


petrolEngine.start();
petrolEngine.accelerate();
petrolEngine.stop();

System.out.println("---");

// Use both interfaces through SmartCar


tesla.start();
tesla.playMusic();
tesla.accelerate();
tesla.stopMusic();
tesla.stop();

// Interface reference variable


Engine carEngine = tesla; // Polymorphism
carEngine.start(); // Calls tesla's implementation

MediaPlayer player = tesla; // Another interface


reference
player.playMusic(); // Calls tesla's implementation
}
}
Variable Types in Interfaces

Interface Variables are Constants:

public interface CarConstants {


// All these are automatically: public static final
int MAX_SPEED = 300;
String DEFAULT_COLOR = "White";
boolean IS_ELECTRIC = false;

// Equivalent to:
// public static final int MAX_SPEED = 300;
// public static final String DEFAULT_COLOR = "White";
// public static final boolean IS_ELECTRIC = false;
}

class SportsCar implements CarConstants {


public void displaySpecs() {
System.out.println("Max Speed: " + MAX_SPEED);
System.out.println("Default Color: " + DEFAULT_COLOR);
System.out.println("Is Electric: " + IS_ELECTRIC);

// Cannot modify interface constants


// MAX_SPEED = 400; // ERROR: Cannot assign a value to
final variable
}
}

Accessing Interface Constants:

public class ConstantsDemo {


public static void main(String[] args) {
// Access through interface name
System.out.println("Max Speed: " +
CarConstants.MAX_SPEED);

// Access through implementing class


SportsCar car = new SportsCar();
car.displaySpecs();
}
}

Separate Classes with Same Interface

Multiple Independent Implementations:

// Common interface
interface MediaPlayer {
void playMedia();
void stopMedia();
}

// Different implementations
class CDPlayer implements MediaPlayer {
@Override
public void playMedia() {
System.out.println("Playing CD music");
}

@Override
public void stopMedia() {
System.out.println("Stopping CD player");
}
}

class DVDPlayer implements MediaPlayer {


@Override
public void playMedia() {
System.out.println("Playing DVD video");
}

@Override
public void stopMedia() {
System.out.println("Stopping DVD player");
}
}

class StreamingPlayer implements MediaPlayer {


@Override
public void playMedia() {
System.out.println("Streaming online content");
}

@Override
public void stopMedia() {
System.out.println("Stopping stream");
}
}

Polymorphic Usage:

public class PolymorphismDemo {


public static void main(String[] args) {
// Array of interface references
MediaPlayer[] players = {
new CDPlayer(),
new DVDPlayer(),
new StreamingPlayer()
};

// Polymorphic method calls


for (MediaPlayer player : players) {
player.playMedia(); // Different implementation
for each
player.stopMedia(); // Different implementation
for each
System.out.println("---");
}
}
}

Dynamic Engine Switching Example:

interface Engine {
void start();
void stop();
}

class PetrolEngine implements Engine {


@Override
public void start() {
System.out.println("Petrol engine roaring to life");
}

@Override
public void stop() {
System.out.println("Petrol engine shutting down");
}
}

class ElectricEngine implements Engine {


@Override
public void start() {
System.out.println("Electric engine starting silently");
}

@Override
public void stop() {
System.out.println("Electric engine powering down");
}
}

class ModularCar {
private Engine engine;

public ModularCar(Engine engine) {


this.engine = engine;
}

public void upgradeEngine(Engine newEngine) {


System.out.println("Upgrading engine...");
this.engine = newEngine;
}

public void startCar() {


engine.start();
}

public void stopCar() {


engine.stop();
}
}

Usage with Engine Switching:

public class EngineDemo {


public static void main(String[] args) {
// Start with petrol engine
ModularCar car = new ModularCar(new PetrolEngine());
car.startCar();
car.stopCar();

System.out.println("--- Upgrading to Electric ---");

// Upgrade to electric engine


car.upgradeEngine(new ElectricEngine());
car.startCar();
car.stopCar();
}
}

Extending Interfaces

Interface Inheritance:

// Base interface
interface BasicEngine {
void start();
void stop();
}

// Extended interface
interface AdvancedEngine extends BasicEngine {
void turboBoost();
void ecoMode();
}

// Further extended interface


interface SmartEngine extends AdvancedEngine {
void selfDiagnosis();
void autoOptimize();
}

Implementation of Extended Interfaces:

class SuperEngine implements SmartEngine {


// Must implement all methods from entire hierarchy

@Override
public void start() {
System.out.println("Super engine starting");
}

@Override
public void stop() {
System.out.println("Super engine stopping");
}

@Override
public void turboBoost() {
System.out.println("Activating turbo boost");
}

@Override
public void ecoMode() {
System.out.println("Switching to eco mode");
}

@Override
public void selfDiagnosis() {
System.out.println("Running self-diagnosis");
}

@Override
public void autoOptimize() {
System.out.println("Auto-optimizing performance");
}
}

Multiple Interface Extension:


interface Engine {
void start();
}

interface MediaSystem {
void playMusic();
}

// Interface extending multiple interfaces


interface SmartCarSystem extends Engine, MediaSystem {
void connectToInternet();
}

class TeslaSystem implements SmartCarSystem {


@Override
public void start() {
System.out.println("Tesla system starting");
}

@Override
public void playMusic() {
System.out.println("Playing premium audio");
}

@Override
public void connectToInternet() {
System.out.println("Connecting to Tesla network");
}
}

Annotations

@Override Annotation:

interface Vehicle {
void start();
void stop();
}

class Car implements Vehicle {


// Good practice - use @Override annotation
@Override
public void start() {
System.out.println("Car engine starting");
}

@Override
public void stop() {
System.out.println("Car engine stopping");
}
}

Bene ts of @Override:

1. Compiler checks - Ensures method actually overrides something


2. Prevents typos - Catches spelling mistakes in method names
3. Documentation - Clearly shows intent to override
4. Refactoring safety - Helps during code changes
Example of Error Prevention:

interface Calculator {
double calculate(double a, double b);
}

class AdditionCalculator implements Calculator {


// Without @Override - typo goes unnoticed
public double calcuate(double a, double b) { // Typo:
"calcuate"
return a + b;
}

// With @Override - compiler error for typo


@Override
public double calculate(double a, double b) { // Correct
return a + b;
}
}

Static Interface Methods


fi
Java 8+ Feature - Static Methods in Interfaces:

interface MathOperations {
// Abstract method
double calculate(double a, double b);

// Static method - can have implementation


static double add(double a, double b) {
return a + b;
}

static double multiply(double a, double b) {


return a * b;
}

// Static methods are NOT inherited


static void printInfo() {
System.out.println("Math operations interface");
}
}

class Calculator implements MathOperations {


@Override
public double calculate(double a, double b) {
return a - b; // Subtraction
}

// Cannot override static interface methods


// static double add(double a, double b) { } // This is
allowed but NOT overriding
}

Usage of Static Interface Methods:

public class StaticInterfaceDemo {


public static void main(String[] args) {
// Call static methods using interface name
MathOperations.printInfo();
double sum = MathOperations.add(10, 5);
double product = MathOperations.multiply(4, 3);
System.out.println("Sum: " + sum);
System.out.println("Product: " + product);

// Use implementation for abstract method


Calculator calc = new Calculator();
double difference = calc.calculate(10, 3);
System.out.println("Difference: " + difference);
}
}

Default Methods in Interfaces (Java 8+):

interface ModernInterface {
// Abstract method
void abstractMethod();

// Default method - has implementation


default void defaultMethod() {
System.out.println("Default implementation");
}

// Static method
static void staticMethod() {
System.out.println("Static method in interface");
}
}

class Implementation implements ModernInterface {


@Override
public void abstractMethod() {
System.out.println("Implemented abstract method");
}

// Optional - can override default method


@Override
public void defaultMethod() {
System.out.println("Custom implementation of default
method");
}
}

Nested Interfaces

Interface Inside Class:

class OuterClass {
// Nested interface
interface NestedInterface {
void nestedMethod();
}

// Implementation of nested interface


static class NestedImplementation implements NestedInterface
{
@Override
public void nestedMethod() {
System.out.println("Nested interface method
implementation");
}
}
}

Interface Inside Interface:

interface OuterInterface {
void outerMethod();

// Nested interface
interface InnerInterface {
void innerMethod();
}
}

class NestedImplementation implements


OuterInterface.InnerInterface {
@Override
public void innerMethod() {
System.out.println("Inner interface method");
}
}

Usage of Nested Interfaces:

public class NestedInterfaceDemo {


public static void main(String[] args) {
// Using nested interface from class
OuterClass.NestedInterface nested = new
OuterClass.NestedImplementation();
nested.nestedMethod();

// Using nested interface from interface


OuterInterface.InnerInterface inner = new
NestedImplementation();
inner.innerMethod();
}
}
Abstract Classes vs Interfaces - Complete Comparison:

Aspect Abstract Class Interface

abstract clas
Keyword interface
s

Implementation extends implements

Multiple
Inheritance ❌ No ✅ Yes

Abstract +
Method Types Abstract + Default + Static
Concrete

Variables All types Only constants (public static nal)


Access Public (methods), Public static nal
All types
Modi ers (variables)

Constructor ✅ Allowed ❌ Not allowed

❌ Cannot
Object Creation ❌ Cannot instantiate
instantiate

When to Use Partial abstraction Pure abstraction / Multiple inheritance


fi
fi
fi
Best Practices:

Use Abstract Classes When:

1. Related classes share common code


2. Partial implementation is bene cial
3. Protected/private methods are needed
4. Non-static, non- nal elds are required
5. Constructor logic is needed
Use Interfaces When:

1. Multiple inheritance is required


2. Pure abstraction is needed
3. Unrelated classes should implement same methods
4. Contract speci cation without implementation details
5. API design for maximum exibility
Real-World Example - Complete System:

// Interface for multiple inheritance


interface Flyable {
void fly();
default void land() {
System.out.println("Landing safely");
}
}

interface Swimmable {
void swim();
}

// Abstract class for common behavior


abstract class Animal {
protected String name;
protected int age;

public Animal(String name, int age) {


this.name = name;
this.age = age;
}

// Concrete method
public void sleep() {
fi
fi
fi
fl
fi
System.out.println(name + " is sleeping");
}

// Abstract method
abstract void makeSound();
}

// Concrete implementation
class Duck extends Animal implements Flyable, Swimmable {
public Duck(String name, int age) {
super(name, age);
}

@Override
void makeSound() {
System.out.println(name + " says: Quack!");
}

@Override
public void fly() {
System.out.println(name + " is flying");
}

@Override
public void swim() {
System.out.println(name + " is swimming");
}
}

class Fish extends Animal implements Swimmable {


public Fish(String name, int age) {
super(name, age);
}

@Override
void makeSound() {
System.out.println(name + " makes bubble sounds");
}

@Override
public void swim() {
System.out.println(name + " is swimming underwater");
}
}

Usage:

public class CompleteDemo {


public static void main(String[] args) {
Duck duck = new Duck("Donald", 3);
Fish fish = new Fish("Nemo", 1);

// Common animal behavior


duck.sleep();
duck.makeSound();

// Duck specific abilities


duck.fly();
duck.swim();
duck.land(); // Default interface method

System.out.println("---");

fish.sleep();
fish.makeSound();
fish.swim();

// Polymorphism with interfaces


Swimmable[] swimmers = {duck, fish};
for (Swimmable swimmer : swimmers) {
swimmer.swim();
}
}
}

Key Takeaways:

1. Abstract classes provide partial abstraction with shared implementation


2. Interfaces provide pure abstraction and multiple inheritance
3. Use both together for maximum exibility in design
4. Java 8+ features make interfaces more powerful with default and static methods
fl
5. Choose based on your speci c needs - inheritance vs contract speci cation
6. Polymorphism works with both abstract classes and interfaces
7. Design patterns often combine both for optimal solutions
This comprehensive understanding of abstract classes and interfaces provides the
foundation for advanced Java programming, design patterns, and building scalable,
maintainable applications.

200 Most Frequently Asked Java OOP Interview Questions

Q1: What is Object-Oriented Programming (OOP) in Java?


a: OOP is a programming paradigm based on objects and classes, focusing on
encapsulation, inheritance, polymorphism, and abstraction.

Q2: What are the four main principles of OOP?


a: Encapsulation, Inheritance, Polymorphism, and Abstraction.

Q3: What is a class in Java?


a: A class is a blueprint or template for creating objects, de ning properties and
behaviors.

Q4: What is an object in Java?


a: An object is an instance of a class, representing a real-world entity with state and
behavior.

Q5: How do you create an object in Java?


a: By using the new keyword, e.g., Student s = new Student();.

Q6: What is encapsulation?


a: Encapsulation is the bundling of data and methods that operate on that data within a
single unit (class), restricting direct access to some components.

Q7: How is encapsulation achieved in Java?


a: By declaring variables as private and providing public getter and setter methods.

Q8: What is inheritance?


a: Inheritance is the mechanism by which one class acquires the properties and
behaviors of another class.

Q9: What is polymorphism?


a: Polymorphism allows objects to be treated as instances of their parent class rather
than their actual class, enabling one interface with multiple implementations.
fi
fi
fi
Q10: What is abstraction?
a: Abstraction is hiding the complex implementation details and showing only the
necessary features of an object.

Q11: What is the difference between class and object?


a: A class is a blueprint; an object is an instance of a class.

Q12: What is a constructor?


a: A constructor is a special method used to initialize objects.

Q13: Can a class have multiple constructors?


a: Yes, through constructor overloading.

Q14: What is constructor overloading?


a: De ning multiple constructors with different parameter lists in the same class.

Q15: What is the this keyword?


a: this refers to the current object instance.

Q16: What is the purpose of the final keyword?


a: To restrict modi cation: nal variables cannot change, nal methods cannot be
overridden, nal classes cannot be extended.

Q17: What are wrapper classes?


a: Classes that encapsulate primitive data types as objects, e.g., Integer, Double.

Q18: What is garbage collection?


a: Automatic memory management that removes unused objects from heap memory.

Q19: What is the difference between stack and heap memory?


a: Stack stores local variables and references; heap stores objects.

Q20: What is the new keyword used for?


a: Allocating memory for objects in the heap and calling the constructor.

Q21: What is method overloading?


a: De ning multiple methods with the same name but different parameter lists in the
same class.

Q22: What is method overriding?


a: Providing a speci c implementation of a method in a subclass that is already de ned
in its superclass.
fi
fi
fi
fi
fi
fi
fi
fi
Q23: Can static methods be overridden?
a: No, static methods are hidden, not overridden.

Q24: What is dynamic method dispatch?


a: The process by which a call to an overridden method is resolved at runtime.

Q25: What is the difference between compile-time and runtime polymorphism?


a: Compile-time polymorphism is achieved by method overloading; runtime
polymorphism is achieved by method overriding.

Q26: What is an abstract class?


a: A class that cannot be instantiated and may contain abstract methods.

Q27: What is an abstract method?


a: A method without a body, meant to be implemented by subclasses.

Q28: Can abstract classes have constructors?


a: Yes.

Q29: Can abstract classes have static methods?


a: Yes, but static methods cannot be abstract.

Q30: What is an interface?


a: A contract that speci es methods a class must implement; supports multiple
inheritance.

Q31: Can interfaces have variables?


a: Yes, but they are implicitly public, static, and nal.

Q32: Can interfaces have default methods?


a: Yes, since Java 8.

Q33: Can interfaces have static methods?


a: Yes, since Java 8.

Q34: What is the difference between abstract class and interface?


a: Abstract classes can have concrete methods and state; interfaces are pure contracts
(before Java 8), support multiple inheritance, and all variables are constants.

Q35: Can a class implement multiple interfaces?


a: Yes.

Q36: Can a class extend multiple classes?


a: No, Java does not support multiple inheritance for classes.
fi
fi
Q37: What is multiple inheritance?
a: When a class inherits from more than one class; not supported in Java for classes,
but possible with interfaces.

Q38: What is the diamond problem?


a: Ambiguity caused by multiple inheritance when two parent classes have the same
method.

Q39: How does Java solve the diamond problem?


a: By not allowing multiple inheritance for classes and using interfaces.

Q40: What is a package in Java?


a: A namespace for organizing classes and interfaces.

Q41: What are access modi ers?


a: Keywords that set the visibility of classes, methods, and variables: private, default,
protected, public.

Q42: What is the default access modi er?


a: Package-private; accessible within the same package.

Q43: What is the protected access modi er?


a: Accessible within the same package and subclasses.

Q44: What is the private access modi er?


a: Accessible only within the same class.

Q45: What is the public access modi er?


a: Accessible from anywhere.

Q46: What is the super keyword?


a: Refers to the immediate parent class object.

Q47: What is upcasting?


a: Assigning a subclass object to a superclass reference.

Q48: What is downcasting?


a: Casting a superclass reference back to a subclass type.

Q49: What is the instanceof operator?


a: Checks if an object is an instance of a speci c class or interface.

Q50: What is the Object class?


a: The root class of all Java classes.
fi
fi
fi
fi
fi
fi
Q51: What is the toString() method?
a: Returns a string representation of an object.

Q52: What is the equals() method?


a: Checks if two objects are equal based on their content.

Q53: What is the hashCode() method?


a: Returns a hash code value for the object.

Q54: What is the clone() method?


a: Creates and returns a copy of the object.

Q55: What is shallow copy?


a: Copies the object but not the objects it references.

Q56: What is deep copy?


a: Copies the object and all objects it references.

Q57: What is a static variable?


a: A variable shared among all instances of a class.

Q58: What is a static method?


a: A method that belongs to the class, not to any object.

Q59: Can static methods access instance variables?


a: No, static methods can only access static variables directly.

Q60: What is a static block?


a: A block of code that runs once when the class is loaded.

Q61: What is an inner class?


a: A class de ned within another class.

Q62: What is a static nested class?


a: A static class de ned inside another class.

Q63: What is an anonymous inner class?


a: An inner class without a name, de ned and instantiated in a single expression.

Q64: What is the Singleton pattern?


a: A design pattern that restricts a class to a single instance.
fi
fi
fi
Q65: How do you implement a thread-safe Singleton?
a: By synchronizing the method that returns the instance or using double-checked
locking.

Q66: What is the difference between == and equals()?


a: == compares references; equals() compares object content.

Q67: What is autoboxing?


a: Automatic conversion of primitive types to their corresponding wrapper classes.

Q68: What is unboxing?


a: Automatic conversion of wrapper class objects to their corresponding primitive types.

Q69: What is a generic class?


a: A class that can operate on objects of various types speci ed as parameters.

Q70: What are wildcards in generics?


a: Special symbols (?, ? extends, ? super) used to specify unknown types.

Q71: What is the difference between ? extends T and ? super T?


a: ? extends T allows T or its subclasses; ? super T allows T or its superclasses.

Q72: What is the Collections Framework?


a: A set of classes and interfaces for storing and manipulating groups of data as a single
unit.

Q73: What is the difference between ArrayList and LinkedList?


a: ArrayList uses dynamic arrays; LinkedList uses doubly linked lists.

Q74: What is the difference between HashMap and TreeMap?


a: HashMap is unordered; TreeMap is sorted by keys.

Q75: What is the Comparable interface?


a: Allows objects to be compared for natural ordering.

Q76: What is the Comparator interface?


a: Allows custom ordering of objects.

Q77: What is a lambda expression?


a: A concise way to represent an anonymous function.

Q78: What is a functional interface?


a: An interface with exactly one abstract method.
fi
Q79: What is exception handling?
a: Mechanism to handle runtime errors using try-catch- nally blocks.

Q80: What is the difference between checked and unchecked exceptions?


a: Checked exceptions are checked at compile time; unchecked at runtime.

Q81: What is a custom exception?


a: A user-de ned exception class extending Exception or RuntimeException.

Q82: What is the finally block?


a: A block that always executes after try-catch, regardless of exceptions.

Q83: What is the throw keyword?


a: Used to explicitly throw an exception.

Q84: What is the throws keyword?


a: Declares that a method may throw exceptions.

Q85: What is serialization?


a: Converting an object into a byte stream for storage or transmission.

Q86: What is deserialization?


a: Reconstructing an object from a byte stream.

Q87: What is the transient keyword?


a: Prevents a variable from being serialized.

Q88: What is the volatile keyword?


a: Ensures visibility of changes to variables across threads.

Q89: What is the difference between composition and inheritance?


a: Composition is "has-a" relationship; inheritance is "is-a" relationship.

Q90: What is the purpose of access modi ers in encapsulation?


a: To restrict access and protect data.

Q91: What is a package-private class?


a: A class without an explicit access modi er, accessible only within its package.

Q92: What is the difference between public, protected, and private constructors?
a: Public constructors allow instantiation from anywhere; protected from subclasses and
package; private only within the class.
fi
fi
fi
fi
Q93: What is method hiding?
a: When a static method in a subclass has the same signature as one in its superclass.

Q94: What is the use of the super() constructor call?


a: To call the parent class constructor.

Q95: What is the difference between interface inheritance and class inheritance?
a: Interface inheritance allows multiple inheritance; class inheritance does not.

Q96: What is a marker interface?


a: An interface with no methods, used to mark classes for special behavior (e.g.,
Serializable).

Q97: What is the default method in an interface?


a: A method with a default implementation in an interface (Java 8+).

Q98: What is the static method in an interface?


a: A method with a body, callable via the interface name (Java 8+).

Q99: What is the difference between abstract class and interface in Java 8+?
a: Interfaces can have default and static methods; abstract classes can have
constructors and state.

Q100: What is the purpose of the @Override annotation?


a: To indicate that a method is intended to override a method in a superclass or
interface.

Q101: What is the difference between shallow and deep copy in cloning?
a: Shallow copy copies references; deep copy copies actual objects.

Q102: What is the use of the transient keyword in serialization?


a: To exclude elds from serialization.

Q103: What is the use of the volatile keyword?


a: To ensure visibility of changes to variables across threads.

Q104: What is the difference between ArrayList and Vector?


a: Vector is synchronized; ArrayList is not.

Q105: What is the difference between HashSet and TreeSet?


a: HashSet is unordered; TreeSet is sorted.
fi
Q106: What is the difference between HashMap and Hashtable?
a: Hashtable is synchronized and does not allow null keys/values; HashMap is not
synchronized and allows one null key and multiple null values.

Q107: What is the use of the instanceof operator?


a: To test whether an object is an instance of a speci c class or interface.

Q108: What is the difference between == and equals() for strings?


a: == checks reference equality; equals() checks value equality.

Q109: What is the purpose of the super keyword in method overriding?


a: To call the superclass version of an overridden method.

Q110: What is the difference between method overloading and overriding?


a: Overloading is same method name, different parameters; overriding is same method
signature in subclass.

Q111: What is the use of the finalize() method?


a: To perform cleanup before an object is garbage collected.

Q112: What is the difference between throw and throws?


a: throw is used to throw an exception; throws declares exceptions in method
signature.

Q113: What is the difference between List, Set, and Map?


a: List allows duplicates and is ordered; Set is unordered and does not allow duplicates;
Map stores key-value pairs.

Q114: What is autoboxing and unboxing?


a: Autoboxing is automatic conversion of primitives to wrapper objects; unboxing is the
reverse.

Q115: What is the purpose of the default keyword in interfaces?


a: To provide default method implementations.

Q116: What is the difference between abstract and interface?


a: Abstract classes can have state and constructors; interfaces cannot (before Java 8).

Q117: What is the use of the static keyword in Java?


a: To declare class-level variables and methods.
fi
Q118: What is the difference between static and non-static members?
a: Static members belong to the class; non-static to the object.

Q119: What is the use of the this keyword in constructors?


a: To refer to the current object and resolve naming con icts.

Q120: What is the difference between super() and this()?


a: super() calls the parent constructor; this() calls another constructor in the same
class.

Q121: What is the difference between checked and unchecked exceptions?


a: Checked exceptions must be handled or declared; unchecked exceptions do not.

Q122: What is the use of the try-catch-finally block?


a: To handle exceptions and ensure cleanup.

Q123: What is the use of the throws clause?


a: To declare that a method may throw exceptions.

Q124: What is the difference between throw and throws?


a: throw is used to throw an exception; throws is used in method signatures.

Q125: What is the use of the @FunctionalInterface annotation?


a: To indicate that an interface is intended to be a functional interface.

Q126: What is the difference between Comparator and Comparable?


a: Comparable is for natural ordering; Comparator is for custom ordering.

Q127: What is the use of the Optional class in Java 8?


a: To avoid null pointer exceptions by representing optional values.

Q128: What is the difference between ArrayList and LinkedList?


a: ArrayList is backed by an array; LinkedList by a doubly linked list.

Q129: What is the use of the Collections utility class?


a: Provides static methods for collection operations like sorting and searching.

Q130: What is the difference between Collection and Collections?


a: Collection is an interface; Collections is a utility class.

Q131: What is the use of the Map interface?


a: To store key-value pairs.
fl
Q132: What is the difference between HashMap and LinkedHashMap?
a: LinkedHashMap maintains insertion order; HashMap does not.

Q133: What is the use of the Set interface?


a: To store unique elements.

Q134: What is the use of the List interface?


a: To store ordered collections that allow duplicates.

Q135: What is the use of the Queue interface?


a: To store elements in a FIFO ( rst-in, rst-out) order.

Q136: What is the use of the Deque interface?


a: To store elements in a double-ended queue.

Q137: What is the difference between Iterator and ListIterator?


a: ListIterator allows bidirectional traversal; Iterator is unidirectional.

Q138: What is the use of the forEach method in Java 8?


a: To iterate over collections using lambda expressions.

Q139: What is the use of the stream() method in Java 8?


a: To process collections using functional-style operations.

Q140: What is the difference between peek() and poll() in Queue?


a: peek() retrieves but does not remove; poll() retrieves and removes.

Q141: What is the use of the removeIf method in Java 8?


a: To remove elements from a collection based on a predicate.

Q142: What is the use of the Comparator.comparing() method?


a: To create comparators for sorting.

Q143: What is the use of the Collectors utility class?


a: To collect stream results into collections.

Q144: What is the use of the flatMap method in streams?


a: To atten nested collections.

Q145: What is the use of the filter method in streams?


a: To lter elements based on a predicate.
fi
fl
fi
fi
Q146: What is the use of the map method in streams?
a: To transform elements.

Q147: What is the use of the reduce method in streams?


a: To aggregate elements into a single result.

Q148: What is the use of the distinct method in streams?


a: To remove duplicate elements.

Q149: What is the use of the sorted method in streams?


a: To sort elements.

Q150: What is the use of the limit and skip methods in streams?
a: To limit the number of elements and skip elements.

Q151: What is the difference between findFirst and findAny in streams?


a: findFirst returns the rst element; findAny may return any element.

Q152: What is the use of the Optional class?


a: To represent a value that may or may not be present.

Q153: What is the use of the orElse and orElseGet methods in Optional?
a: To provide default values if the Optional is empty.

Q154: What is the use of the ifPresent method in Optional?


a: To execute an action if a value is present.

Q155: What is the use of the Predicate functional interface?


a: To represent a boolean-valued function.

Q156: What is the use of the Function functional interface?


a: To represent a function that takes one argument and returns a result.

Q157: What is the use of the Consumer functional interface?


a: To represent an operation that takes a single argument and returns no result.

Q158: What is the use of the Supplier functional interface?


a: To represent a supplier of results.

Q159: What is the use of the BiFunction functional interface?


a: To represent a function that takes two arguments and returns a result.
fi
Q160: What is the use of the UnaryOperator and BinaryOperator interfaces?
a: UnaryOperator operates on a single operand; BinaryOperator on two operands of the
same type.

Q161: What is the use of the @Override annotation?


a: To indicate that a method overrides a superclass or interface method.

Q162: What is the use of the @FunctionalInterface annotation?


a: To indicate that an interface is intended to be a functional interface.

Q163: What is the use of the @SuppressWarnings annotation?


a: To suppress compiler warnings.

Q164: What is the use of the @Deprecated annotation?


a: To indicate that a method or class should not be used.

Q165: What is the use of the @SafeVarargs annotation?


a: To suppress warnings about unsafe varargs usage.

Q166: What is the use of the @Retention and @Target annotations?


a: To specify how long annotations are retained and where they can be applied.

Q167: What is the use of the @Documented annotation?


a: To indicate that an annotation should be included in Javadoc.

Q168: What is the use of the @Inherited annotation?


a: To indicate that an annotation is inherited by subclasses.

Q169: What is the use of the @Repeatable annotation?


a: To allow multiple annotations of the same type.

Q170: What is the use of the @Override annotation?


a: To indicate method overriding.

Q171: What is the use of the @Test annotation in JUnit?


a: To mark a method as a test method.

Q172: What is the use of the @Before and @After annotations in JUnit?
a: To mark setup and teardown methods.

Q173: What is the use of the @BeforeClass and @AfterClass annotations in JUnit?
a: To mark methods that run before and after all tests.
Q174: What is the use of the @Ignore annotation in JUnit?
a: To ignore a test method.

Q175: What is the use of the @ParameterizedTest annotation in JUnit 5?


a: To run a test multiple times with different parameters.

Q176: What is the use of the @DisplayName annotation in JUnit 5?


a: To provide a custom name for a test.

Q177: What is the use of the @Nested annotation in JUnit 5?


a: To group related tests.

Q178: What is the use of the @Tag annotation in JUnit 5?


a: To categorize tests.

Q179: What is the use of the @Disabled annotation in JUnit 5?


a: To disable a test.

Q180: What is the use of the @BeforeEach and @AfterEach annotations in JUnit 5?
a: To mark setup and teardown methods for each test.

Q181: What is the use of the @Data annotation in Lombok?


a: To generate getters, setters, toString, equals, and hashCode methods.

Q182: What is the use of the @Getter and @Setter annotations in Lombok?
a: To generate getter and setter methods.

Q183: What is the use of the @Builder annotation in Lombok?


a: To implement the builder pattern.

Q184: What is the use of


the @AllArgsConstructor and @NoArgsConstructor annotations in Lombok?
a: To generate constructors with all or no arguments.

Q185: What is the use of the @Slf4j annotation in Lombok?


a: To generate a logger eld.

Q186: What is the use of the @Value annotation in Lombok?


a: To create immutable classes.
fi
Q187: What is the use of the @NonNull annotation in Lombok?
a: To generate null checks.

Q188: What is the use of the @Synchronized annotation in Lombok?


a: To generate synchronized methods.

Q189: What is the use of the @Cleanup annotation in Lombok?


a: To automatically clean up resources.

Q190: What is the use of the @ToString annotation in Lombok?


a: To generate a toString method.

Q191: What is the use of the @EqualsAndHashCode annotation in Lombok?


a: To generate equals and hashCode methods.

Q192: What is the use of the @RequiredArgsConstructor annotation in Lombok?


a: To generate a constructor for nal elds.

Q193: What is the use of the @Accessors annotation in Lombok?


a: To customize getter/setter naming.

Q194: What is the use of the @With annotation in Lombok?


a: To generate withX methods for immutable objects.

Q195: What is the use of the @Singular annotation in Lombok?


a: To support singular addition in builders.

Q196: What is the use of the @SuperBuilder annotation in Lombok?


a: To support builder pattern with inheritance.

Q197: What is the use of the @FieldDefaults annotation in Lombok?


a: To set default eld modi ers.

Q198: What is the use of the @Getter(lazy=true) annotation in Lombok?


a: To generate lazy-initialized getters.

Q199: What is the use of the @UtilityClass annotation in Lombok?


a: To create utility classes with static methods.

Q200: What is the use of the @ExtensionMethod annotation in Lombok?


a: To add extension methods to existing types.
fi
fi
fi
fi

You might also like