KEMBAR78
Java Previous Year Paper Solved | PDF | Method (Computer Programming) | Inheritance (Object Oriented Programming)
0% found this document useful (0 votes)
33 views61 pages

Java Previous Year Paper Solved

The document explains the concepts of abstraction and abstract classes in Java, highlighting their role in Object-Oriented Programming by allowing complex implementation details to be hidden while exposing essential features. It details the characteristics of abstract classes and methods, providing a Java program example demonstrating their application through a Shape class and its concrete subclasses Circle and Rectangle. Additionally, it covers polymorphism, differentiating between compile-time and run-time polymorphism, and illustrates method overriding with a Shape superclass and a Rectangle subclass.

Uploaded by

Apex Shamsh
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)
33 views61 pages

Java Previous Year Paper Solved

The document explains the concepts of abstraction and abstract classes in Java, highlighting their role in Object-Oriented Programming by allowing complex implementation details to be hidden while exposing essential features. It details the characteristics of abstract classes and methods, providing a Java program example demonstrating their application through a Shape class and its concrete subclasses Circle and Rectangle. Additionally, it covers polymorphism, differentiating between compile-time and run-time polymorphism, and illustrates method overriding with a Shape superclass and a Rectangle subclass.

Uploaded by

Apex Shamsh
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/ 61

Apna Engineering Wallah

UNIT 1: Java Fundamentals and


OOP Concepts

Q2(a): Explain abstraction and abstract classes in


Java. Describe abstract method. With a suitable ex-
ample demonstrate the application of abstract classes.
Step 1: Detailed Explanation
Abstraction
Abstraction is one of the fundamental principles of Object-Oriented Programming (OOP).
It is the process of hiding the complex implementation details and showing only the
essential features of an object to the user. In simpler terms, it deals with "what" an
object does rather than "how" it does it. Abstraction helps in managing complexity by
breaking down a system into smaller, more manageable parts. It focuses on the relevant
information and ignores the irrelevant.
Consider a car: when you drive a car, you interact with the steering wheel, accelerator,
and brakes. You don’t need to know the intricate details of how the engine works, how the
fuel is ignited, or how the braking system operates. The internal complexities are hidden
from you, and you only interact with the essential functionalities. This is abstraction in
action.
In Java, abstraction can be achieved in two main ways:
1. Abstract Classes: These are classes that cannot be instantiated directly and may
contain abstract methods.
2. Interfaces: These define a contract of methods that a class must implement.
Abstract Classes
An abstract class is a class that is declared with the abstract keyword. It can have
abstract methods (methods without a body) as well as concrete methods (methods with
a body). Key characteristics of abstract classes:
• Cannot be instantiated: You cannot create an object of an abstract class. It
must be subclassed, and its abstract methods must be implemented by the concrete
subclass.
• May contain abstract methods: An abstract method is a method declared
without an implementation (without a body), followed by a semicolon.
• May contain concrete methods: Abstract classes can have regular methods with
full implementations.

5 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• Can have constructors: Abstract classes can have constructors, which are called
when an instance of a concrete subclass is created.

• Can have final methods: An abstract class can have final methods, which
means these methods cannot be overridden by subclasses.

• Can have static methods: Abstract classes can have static methods.

Abstract Method
An abstract method is a method that is declared without an implementation (i.e., without
a method body) in an abstract class. It is declared using the abstract keyword. Syn-
tax: abstract returnType methodName(parameters); Key characteristics of abstract
methods:

• No body: An abstract method has no curly braces {} and ends with a semicolon
;.

• Must be in an abstract class: If a class contains an abstract method, the class


itself must be declared as abstract.

• Must be implemented by concrete subclasses: Any concrete (non-abstract)


class that extends an abstract class must provide an implementation (override) for
all the abstract methods declared in the abstract superclass. If a subclass does not
implement all abstract methods, it must also be declared abstract.

• Enforces a contract: Abstract methods define a contract that all concrete sub-
classes must adhere to, ensuring that specific functionalities are provided by them.

Step 3: Java Program to Demonstrate the Application of Ab-


stract Classes
This example demonstrates how an abstract class Shape defines an abstract method
calculateArea() and a concrete method display(). The concrete subclasses Circle
and Rectangle provide their specific implementations for calculateArea().
1 // Shape.java - Abstract class
2 abstract class Shape {
3 String color; // Instance variable
4

5 // Constructor for the abstract class


6 public Shape(String color) {
7 this.color = color;
8 System.out.println("Shape␣constructor␣called.␣Color:␣" + this.color);
9 }
10

11 // Abstract method: must be implemented by concrete subclasses


12 // This method doesn’t have a body and ends with a semicolon.
13 public abstract double calculateArea();
14

15 // Concrete method: has an implementation


16 public void display() {
17 System.out.println("This␣is␣a␣" + color + "␣shape.");

6 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

18 }
19 }
20

21 // Circle.java - Concrete subclass of Shape


22 class Circle extends Shape {
23 double radius;
24

25 // Constructor for Circle


26 public Circle(String color, double radius) {
27 super(color); // Call the constructor of the superclass (Shape)
28 this.radius = radius;
29 System.out.println("Circle␣constructor␣called.␣Radius:␣" +
this.radius);
30 }
31

32 // Implementation of the abstract method calculateArea() for Circle


33 @Override
34 public double calculateArea() {
35 return Math.PI * radius * radius;
36 }
37 }
38

39 // Rectangle.java - Concrete subclass of Shape


40 class Rectangle extends Shape {
41 double length;
42 double width;
43

44 // Constructor for Rectangle


45 public Rectangle(String color, double length, double width) {
46 super(color); // Call the constructor of the superclass (Shape)
47 this.length = length;
48 this.width = width;
49 System.out.println("Rectangle␣constructor␣called.␣Length:␣" +
this.length + ",␣Width:␣" + this.width);
50 }
51

52 // Implementation of the abstract method calculateArea() for Rectangle


53 @Override
54 public double calculateArea() {
55 return length * width;
56 }
57 }
58

59 // Main.java - Main class to demonstrate abstract classes


60 public class Main {
61 public static void main(String[] args) {
62 // We cannot instantiate an abstract class directly:
63 // Shape myShape = new Shape("Generic"); // This would cause a
compile-time error
64

65 // Create objects of concrete subclasses

7 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

66 Circle circle = new Circle("Red", 5.0);


67 Rectangle rectangle = new Rectangle("Blue", 4.0, 6.0);
68

69 System.out.println("\n---␣Circle␣Details␣---");
70 circle.display(); % Call concrete method from abstract class
71 System.out.println("Area␣of␣Circle:␣" + circle.calculateArea()); %
Call implemented abstract method
72

73 System.out.println("\n---␣Rectangle␣Details␣---");
74 rectangle.display(); % Call concrete method from abstract class
75 System.out.println("Area␣of␣Rectangle:␣" + rectangle.calculateArea());
% Call implemented abstract method
76

77 % Demonstrate polymorphism with abstract class reference


78 System.out.println("\n---␣Polymorphism␣with␣Abstract␣Class␣---");
79 Shape s1 = new Circle("Green", 7.0);
80 Shape s2 = new Rectangle("Yellow", 8.0, 3.0);
81

82 System.out.println("Area␣of␣s1␣(Green␣Circle):␣" + s1.calculateArea());
83 System.out.println("Area␣of␣s2␣(Yellow␣Rectangle):␣" +
s2.calculateArea());
84 }
85 }
Listing 1: Shape, Circle, Rectangle, and Main classes

Line-by-Line Code Explanation:


• abstract class Shape { ... }: Declares an abstract class named Shape. This
class cannot be instantiated directly.

• String color;: A common instance variable for all shapes.

• public Shape(String color) { ... }: A constructor for the Shape class. This
constructor will be called by the constructors of its subclasses using super(color);.

• public abstract double calculateArea();: This is an abstract method. It has


no body and is terminated by a semicolon. Any concrete class extending Shape
must provide an implementation for this method.

• public void display() { ... }: This is a concrete method. It has a body and
provides a default implementation that can be used by subclasses or overridden.

• class Circle extends Shape { ... }: Declares a concrete class Circle that
extends the abstract Shape class.

• public Circle(String color, double radius) { super(color); this.radius


= radius; }: The constructor for Circle. super(color) calls the constructor of
the Shape class to initialize the color attribute.

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


}: This method provides the specific implementation for calculateArea() for a

8 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

Circle. The @Override annotation is optional but good practice, indicating that
this method overrides a method from the superclass (in this case, the abstract
method).

• class Rectangle extends Shape { ... }: Declares another concrete class Rectangle
that extends Shape.

• public Rectangle(String color, double length, double width) { super(color);


this.length = length; this.width = width; }: The constructor for Rectangle,
also calling super(color).

• @Override public double calculateArea() { return length * width; }: Pro-


vides the specific implementation for calculateArea() for a Rectangle.

• public class Main { ... }: The main class to run the demonstration.

• // Shape myShape = new Shape("Generic");: This commented-out line shows


that you cannot directly instantiate an abstract class.

• Circle circle = new Circle("Red", 5.0);: Creates an object of the Circle


class.

• Rectangle rectangle = new Rectangle("Blue", 4.0, 6.0);: Creates an ob-


ject of the Rectangle class.

• circle.display();: Calls the concrete method inherited from Shape.

• circle.calculateArea();: Calls the overridden calculateArea() method spe-


cific to Circle.

• Shape s1 = new Circle("Green", 7.0);: Demonstrates polymorphism. A Shape


reference variable can hold an object of its concrete subclass (Circle).

• s1.calculateArea();: When calculateArea() is called on s1, the appropriate


calculateArea() method (from Circle in this case) is invoked at runtime, demon-
strating runtime polymorphism.

Output:
Shape constructor called. Color: Red
Circle constructor called. Radius: 5.0
Shape constructor called. Color: Blue
Rectangle constructor called. Length: 4.0, Width: 6.0

--- Circle Details ---


This is a Red shape.
Area of Circle: 78.53981633974483

--- Rectangle Details ---


This is a Blue shape.
Area of Rectangle: 24.0

9 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

--- Polymorphism with Abstract Class ---


Shape constructor called. Color: Green
Circle constructor called. Radius: 7.0
Shape constructor called. Color: Yellow
Rectangle constructor called. Length: 8.0, Width: 3.0
Area of s1 (Green Circle): 153.93804002589985
Area of s2 (Yellow Rectangle): 24.0

Q3(a): Illustrate polymorphism and its types in Java.


Differentiate between run-time and compile-time poly-
morphism. Write super class Shape with method dis-
playArea() and sub class Rectangle. Demonstrate method
overriding with this example.
Step 1: Detailed Explanation
Polymorphism
Polymorphism, meaning "many forms," is a core concept in Object-Oriented Programming
(OOP) that allows objects of different classes to be treated as objects of a common type.
It enables a single interface to represent different underlying forms or data types. The
most common use of polymorphism in Java occurs when a parent class reference is used
to refer to a child class object.
The key idea is that a single method call can behave differently depending on the type
of object it is invoked on. This flexibility makes code more extensible, maintainable, and
readable.
Types of Polymorphism in Java:
Java supports two main types of polymorphism:

1. Compile-time Polymorphism (Static Polymorphism / Method Overload-


ing)

• Definition: This type of polymorphism is resolved at compile time. It occurs


when there are multiple methods with the same name but different parameters
(number of parameters, type of parameters, or order of parameters) within the
same class.
• Mechanism: The compiler determines which method to call based on the
method signature (name and parameter list) at compile time.
• Example: Method Overloading.

2. Run-time Polymorphism (Dynamic Polymorphism / Method Overriding)

• Definition: This type of polymorphism is resolved at runtime. It occurs


when a subclass provides a specific implementation for a method that is al-
ready defined in its superclass. The method signature (name, return type, and
parameters) must be the same in both the superclass and subclass.

10 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• Mechanism: The Java Virtual Machine (JVM) determines which method to


call based on the actual object type at runtime, not the reference type. This
is achieved through dynamic method dispatch.
• Example: Method Overriding.

Step 2: Comparison Table - Compile-time vs. Run-time Poly-


morphism

Table 1: Comparison: Compile-time vs. Run-time Poly-


morphism

Feature Compile-time Polymor- Run-time Polymorphism


phism (Method Overload- (Method Overriding)
ing)
Resolution Time Compile Time Run Time
Mechanism Method Overloading Method Overriding (Dynamic
Method Dispatch)
Method Signa- Same method name, different Same method name, same pa-
ture parameter list (number, type, rameter list, same return type
order) (or covariant)
Scope Within the same class Between a superclass and its
subclass
Keywords No specific keyword; relies on @Override annotation (op-
method signature tional, but recommended)
Inheritance Not directly related to inheri- Requires inheritance (parent-
tance (can be in a single class) child relationship)
Example add(int a, int b), add(int Animal.makeSound(),
a, int b, int c) Dog.makeSound()
Flexibility Provides flexibility in calling Provides flexibility in imple-
methods with varying inputs menting common behaviors dif-
ferently in subclasses

Step 3: Java Program to Demonstrate Method Overriding (Run-


time Polymorphism)
This example demonstrates method overriding using a Shape superclass and a Rectangle
subclass. The displayArea() method is overridden in the Rectangle class.
1 // Shape.java - Superclass
2 class Shape {
3 // Method to display area (generic implementation)
4 public void displayArea() {
5 System.out.println("Calculating␣area␣for␣a␣generic␣shape.");
6 }
7 }
8

9 // Rectangle.java - Subclass

11 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

10 class Rectangle extends Shape {


11 double length;
12 double width;
13

14 // Constructor for Rectangle


15 public Rectangle(double length, double width) {
16 this.length = length;
17 this.width = width;
18 }
19

20 // Method Overriding: Providing a specific implementation for displayArea()


21 // The @Override annotation is optional but helps the compiler check for
correct overriding.
22 @Override
23 public void displayArea() {
24 double area = length * width;
25 System.out.println("Area␣of␣Rectangle␣(Length:␣" + length + ",␣Width:␣
" + width + "):␣" + area);
26 }
27

28 // Example of Method Overloading (Compile-time Polymorphism)


29 public void displayArea(String unit) {
30 double area = length * width;
31 System.out.println("Area␣of␣Rectangle␣in␣" + unit + ":␣" + area + "␣"
+ unit + "^2");
32 }
33 }
34

35 // Main.java - Main class to demonstrate polymorphism


36 public class Main {
37 public static void main(String[] args) {
38 // Create an object of the superclass
39 Shape genericShape = new Shape();
40 System.out.println("---␣Generic␣Shape␣---");
41 genericShape.displayArea(); % Calls Shape’s␣displayArea()
42

43 ␣␣␣␣␣␣␣␣System.out.println("\n---␣Rectangle␣Object␣---");
44 ␣␣␣␣␣␣␣␣%␣Create␣an␣object␣of␣the␣subclass
45 ␣␣␣␣␣␣␣␣Rectangle␣myRectangle␣=␣new␣Rectangle(10.0,␣5.0);
46 ␣␣␣␣␣␣␣␣myRectangle.displayArea();␣%␣Calls␣Rectangle’s overridden displayArea()
47

48 % Demonstrate Run-time Polymorphism (Dynamic Method Dispatch)


49 % A superclass reference variable pointing to a subclass object
50 System.out.println("\n---␣Polymorphism␣in␣action␣(Shape␣reference,␣
Rectangle␣object)␣---");
51 Shape polymorphicShape = new Rectangle(7.0, 3.0);
52 % At compile time, the compiler sees a Shape reference.
53 % At run time, the JVM determines the actual object type (Rectangle)
54 % and calls Rectangle’s␣displayArea()␣method.
55 ␣␣␣␣␣␣␣␣polymorphicShape.displayArea();
56

12 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

57 ␣␣␣␣␣␣␣␣%␣Demonstrate␣Compile-time␣Polymorphism␣(Method␣Overloading)
58 ␣␣␣␣␣␣␣␣System.out.println("\n---␣Method␣Overloading␣Example␣(Compile-time␣
Polymorphism)␣---");
59 ␣␣␣␣␣␣␣␣Rectangle␣anotherRectangle␣=␣new␣Rectangle(6.0,␣4.0);
60 ␣␣␣␣␣␣␣␣anotherRectangle.displayArea();␣%␣Calls␣the␣overridden␣method␣without␣
parameters
61 ␣␣␣␣␣␣␣␣anotherRectangle.displayArea("cm");␣%␣Calls␣the␣overloaded␣method␣with␣
a␣String␣parameter
62 ␣␣␣␣}
63 }
Listing 2: Shape, Rectangle, and Main classes

Line-by-Line Code Explanation:


• class Shape { ... }: Defines the superclass Shape.
• public void displayArea() { ... }: This is a method in the Shape class. It
provides a generic message.
• class Rectangle extends Shape { ... }: Defines the subclass Rectangle that
inherits from Shape.
• double length; double width;: Instance variables specific to Rectangle.
• public Rectangle(double length, double width) { ... }: Constructor for
Rectangle to initialize its dimensions.
• @Override public void displayArea() { ... }: This method overrides the
displayArea() method from the Shape superclass. The @Override annotation is
a good practice to ensure that you are indeed overriding a method. This method
provides a specific implementation for calculating and displaying the area of a rect-
angle.
• public void displayArea(String unit) { ... }: This is an overloaded method.
It has the same name as displayArea() but a different parameter list (String
unit). This demonstrates compile-time polymorphism.
• public class Main { ... }: The main class to execute the program.
• Shape genericShape = new Shape();: Creates an object of the Shape class.
• genericShape.displayArea();: Calls the displayArea() method of the Shape
class.
• Rectangle myRectangle = new Rectangle(10.0, 5.0);: Creates an object of
the Rectangle class.
• myRectangle.displayArea();: Calls the displayArea() method of the Rectangle
class (the overridden version).
• Shape polymorphicShape = new Rectangle(7.0, 3.0);: This is the core of run-
time polymorphism. A reference variable of type Shape is made to refer to an object
of type Rectangle.

13 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• polymorphicShape.displayArea();: When this line executes, the JVM looks at


the actual type of the object (which is Rectangle) at runtime and invokes the
displayArea() method defined in the Rectangle class, not the Shape class. This
is dynamic method dispatch.

• anotherRectangle.displayArea();: Calls the displayArea() method with no


arguments (the overridden version).

• anotherRectangle.displayArea("cm");: Calls the displayArea() method with


a String argument (the overloaded version). The compiler resolves this call at
compile time based on the method signature.

Output:
--- Generic Shape ---
Calculating area for a generic shape.

--- Rectangle Object ---


Area of Rectangle (Length: 10.0, Width: 5.0): 50.0

--- Polymorphism in action (Shape reference, Rectangle object) ---


Area of Rectangle (Length: 7.0, Width: 3.0): 21.0

--- Method Overloading Example (Compile-time Polymorphism) ---


Area of Rectangle in cm: 24.0 cm^2

14 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

UNIT 2: Exception Handling and


Multithreading

Q2(b): Describe the ways to create the threads in Java


with suitable code. Also explain which method is more
suitable to create threads.
Step 1: Detailed Explanation
Threads in Java
A thread is a lightweight subprocess, the smallest unit of a program that can be executed.
It’s a path of execution within a program. Multithreading is a concept where multiple
threads run concurrently within a single program, allowing different parts of a program
to execute simultaneously. This can lead to more efficient use of CPU resources, im-
proved responsiveness, and better performance for applications that can be divided into
independent tasks.
In Java, threads are managed by the Java Virtual Machine (JVM). The java.lang.Thread
class and the java.lang.Runnable interface are the core components for handling threads.
Ways to Create Threads in Java:
There are two primary ways to create threads in Java:
1. By Extending the Thread Class:
• Mechanism: You create a new class that extends java.lang.Thread and
override its run() method. The run() method contains the code that will be
executed by the new thread.
• Steps:
(a) Create a class that extends Thread.
(b) Override the run() method in your class. This method contains the logic
that the thread will execute.
(c) Create an instance of your custom thread class.
(d) Call the start() method on the thread object. The start() method
internally calls the run() method in a new thread of execution.
2. By Implementing the Runnable Interface:
• Mechanism: You create a class that implements the java.lang.Runnable in-
terface and provide an implementation for its run() method. Then, you create
an instance of Thread by passing an object of your Runnable implementation
to its constructor.

15 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• Steps:
(a) Create a class that implements Runnable.
(b) Implement the run() method.
(c) Create an instance of your Runnable implementation.
(d) Create a Thread object, passing your Runnable instance to its constructor:
Thread thread = new Thread(myRunnableInstance);
(e) Call the start() method on the Thread object.

Which method is more suitable to create threads?


Implementing the Runnable interface is generally considered more suitable and
is the preferred approach for creating threads in Java. Here’s why:

• Java does not support multiple inheritance of classes: If your class already
extends another class, it cannot extend Thread as well. By implementing Runnable,
your class can still extend another class while also having thread capabilities. This
promotes better design and flexibility.

• Separation of concerns: Implementing Runnable separates the task (the code to


be executed by the thread) from the thread control mechanism (Thread class). This
makes your code cleaner and more modular. The Runnable object represents the
"job," and the Thread object represents the "worker" that performs the job.

• Resource sharing: When multiple threads need to process the same data, it’s
easier to share a single Runnable object among multiple Thread objects. Each
Thread object can then execute the run() method of the same Runnable instance,
allowing them to work on shared resources. If you extend Thread, each thread would
typically have its own instance, making sharing more complex.

• Flexibility with Thread Pools: When working with ExecutorService and


ThreadPoolExecutor (which are common for managing threads in larger appli-
cations), you typically submit Runnable or Callable tasks, not Thread objects
directly. This makes Runnable a more natural fit for modern concurrent program-
ming practices.

While extending Thread is simpler for very basic, standalone thread creation, implement-
ing Runnable offers greater flexibility, better design, and adheres to the principle of "favor
composition over inheritance."

Step 3: Java Program to Demonstrate Thread Creation

1 // Method 1: By Extending the Thread Class


2 class MyThread extends Thread {
3 private String threadName;
4

5 public MyThread(String name) {


6 this.threadName = name;
7 System.out.println("Creating␣" + threadName );
8 }
9

16 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

10 // The run method contains the code that will be executed by the thread
11 @Override
12 public void run() {
13 System.out.println("Running␣" + threadName );
14 try {
15 for(int i = 4; i > 0; i--) {
16 System.out.println("Thread:␣" + threadName + ",␣" + i);
17 // Pause thread for a short duration
18 Thread.sleep(50);
19 }
20 } catch (InterruptedException e) {
21 System.out.println("Thread␣" + threadName + "␣interrupted.");
22 }
23 System.out.println("Thread␣" + threadName + "␣exiting.");
24 }
25 }
26

27 // Method 2: By Implementing the Runnable Interface


28 class MyRunnable implements Runnable {
29 private String threadName;
30

31 public MyRunnable(String name) {


32 this.threadName = name;
33 System.out.println("Creating␣" + threadName );
34 }
35

36 // The run method contains the code that will be executed by the thread
37 @Override
38 public void run() {
39 System.out.println("Running␣" + threadName );
40 try {
41 for(int i = 4; i > 0; i--) {
42 System.out.println("Runnable:␣" + threadName + ",␣" + i);
43 // Pause thread for a short duration
44 Thread.sleep(50);
45 }
46 } catch (InterruptedException e) {
47 System.out.println("Runnable␣" + threadName + "␣interrupted.");
48 }
49 System.out.println("Runnable␣" + threadName + "␣exiting.");
50 }
51 }
52

53 // Main.java - Main class to demonstrate thread creation


54 public class Main {
55 public static void main(String[] args) {
56 System.out.println("---␣Demonstrating␣Thread␣Creation␣---");
57

58 // Creating thread by extending Thread class


59 System.out.println("\nStarting␣Thread␣by␣extending␣Thread␣class:");
60 MyThread thread1 = new MyThread("Thread-Extends");

17 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

61 thread1.start(); % This calls the run() method in a new thread


62

63 % Creating thread by implementing Runnable interface


64 System.out.println("\nStarting␣Thread␣by␣implementing␣Runnable␣
interface:");
65 MyRunnable runnable1 = new MyRunnable("Thread-Implements");
66 Thread thread2 = new Thread(runnable1); % Pass the Runnable instance
to Thread constructor
67 thread2.start(); % This calls the run() method of the Runnable in a
new thread
68

69 System.out.println("\nMain␣thread␣continues␣its␣execution...");
70

71 % You can also create threads using anonymous classes or lambda


expressions for Runnable
72 System.out.println("\nStarting␣Thread␣using␣Lambda␣for␣Runnable:");
73 Thread lambdaThread = new Thread(() → {
74 System.out.println("Running␣Lambda␣Thread");
75 try {
76 for(int i = 3; i > 0; i--) {
77 System.out.println("Lambda␣Thread:␣" + i);
78 Thread.sleep(70);
79 }
80 } catch (InterruptedException e) {
81 System.out.println("Lambda␣Thread␣interrupted.");
82 }
83 System.out.println("Lambda␣Thread␣exiting.");
84 }, "Thread-Lambda"); % Naming the thread
85 lambdaThread.start();
86

87 System.out.println("\nAll␣threads␣launched.␣Main␣thread␣is␣
finishing.");
88 }
89 }
Listing 3: Thread Creation Example

Line-by-Line Code Explanation:


MyThread Class (Extending Thread)

• class MyThread extends Thread { ... }: Declares a class MyThread that in-
herits from java.lang.Thread.

• private String threadName;: An instance variable to store the name of the


thread.

• public MyThread(String name) { ... }: Constructor to initialize the thread’s


name.

• @Override public void run() { ... }: This is the core method. The code
inside run() will be executed when the thread starts.

18 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• try { ... } catch (InterruptedException e) { ... }: A try-catch block


to handle InterruptedException, which can occur if the thread is interrupted while
sleeping.

• Thread.sleep(50);: Pauses the current thread’s execution for 50 milliseconds.


This helps in observing the concurrent execution.

MyRunnable Class (Implementing Runnable)

• class MyRunnable implements Runnable { ... }: Declares a class MyRunnable


that implements the java.lang.Runnable interface.

• private String threadName;: Similar instance variable for naming.

• public MyRunnable(String name) { ... }: Constructor for MyRunnable.

• @Override public void run() { ... }: Similar to MyThread, this method con-
tains the thread’s execution logic. The Runnable interface only has this single ab-
stract method.

Main Class

• public static void main(String[] args) { ... }: The entry point of the
program.

• MyThread thread1 = new MyThread("Thread-Extends");: Creates an instance


of MyThread.

• thread1.start();: Crucial step. This method starts a new thread of execution


and calls the run() method of thread1 in that new thread. Never call run()
directly (e.g., thread1.run()), as that would execute the run() method in the
current thread (main thread) and not create a new thread.

• MyRunnable runnable1 = new MyRunnable("Thread-Implements");: Creates an


instance of MyRunnable.

• Thread thread2 = new Thread(runnable1);: Creates a Thread object, passing


the MyRunnable instance to its constructor. This Thread object now "knows" which
Runnable’s run() method to execute.

• thread2.start();: Starts the new thread, which in turn executes runnable1.run().

• Thread lambdaThread = new Thread(() -> { ... }, "Thread-Lambda");: Demon-


strates creating a thread using a lambda expression for the Runnable interface. This
is a concise way to define the run() method inline. The second argument is the
thread name.

• lambdaThread.start();: Starts the lambda-based thread.

• The System.out.println statements in main and within the run methods illustrate
that the threads execute concurrently with the main thread.

19 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

Output (Note: Output order may vary due to thread scheduling):


--- Demonstrating Thread Creation ---

Starting Thread by extending Thread class:


Creating Thread-Extends
Starting Thread by implementing Runnable interface:
Creating Thread-Implements

Main thread continues its execution...


Running Thread-Extends
Running Thread-Implements
Starting Thread using Lambda for Runnable:
Creating Thread-Lambda
Thread: Thread-Extends, 4
Runnable: Thread-Implements, 4
Running Lambda Thread
Lambda Thread: 3
Thread: Thread-Extends, 3
Runnable: Thread-Implements, 3
Lambda Thread: 2
Thread: Thread-Extends, 2
Runnable: Thread-Implements, 2
Lambda Thread: 1
Lambda Thread exiting.
Thread: Thread-Extends, 1
Thread Thread-Extends exiting.
Runnable: Thread-Implements, 1
Runnable Thread-Implements exiting.

All threads launched. Main thread is finishing.

*(The exact interweaving of "Thread", "Runnable", and "Lambda Thread" output lines
will vary each time the program is run due to the nature of thread scheduling.)*

Q4(b): Differentiate between checked and unchecked


exceptions in Java. Write a Java program to demon-
strate Arithmetic Exception handlings.
Step 1: Detailed Explanation
Exceptions in Java
An exception is an event that disrupts the normal flow of a program. It is an object that
is thrown at runtime. When an error occurs within a method, it can create an object
and hand it off to the runtime system. This object, called an exception object, contains
information about the error, including its type and the state of the program when the
error occurred.

20 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

Java’s exception handling mechanism is built upon five keywords: try, catch, finally,
throw, and throws.
Types of Exceptions in Java:
Java exceptions are broadly categorized into two types:

1. Checked Exceptions:

• Definition: These are exceptions that are checked at compile time. The com-
piler forces the programmer to handle these exceptions. If a method is capable
of throwing a checked exception, it must either handle the exception (using
try-catch) or declare that it throws the exception (using throws keyword in
the method signature).
• Inheritance: Checked exceptions are direct subclasses of java.lang.Exception
(excluding RuntimeException and its subclasses).
• Examples: IOException, SQLException, ClassNotFoundException, FileNotFoundExcept
• Purpose: They represent conditions that a well-written application should
anticipate and recover from. They typically indicate problems outside the
immediate control of the program (e.g., file not found, network issues).

2. Unchecked Exceptions:

• Definition: These are exceptions that are not checked at compile time. The
compiler does not force the programmer to handle them. They occur due to
programming errors (e.g., logic errors, invalid arguments).
• Inheritance: Unchecked exceptions are subclasses of java.lang.RuntimeException.
• Examples: ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsExce
NumberFormatException.
• Purpose: They typically indicate bugs in the code that should be fixed rather
than caught and recovered from. They are often unexpected and can be
avoided by proper input validation and defensive programming. tab:checked-
vs-unchecked

Step 2: Comparison Table - Checked vs. Unchecked Exceptions

Table 2: Comparison: Checked vs. Unchecked Excep-


tions

Feature Checked Exceptions Unchecked Exceptions


Compile-time Yes (compiler forces handling) No (compiler does not force
Check handling)
Inheritance Subclasses of Exception (ex- Subclasses of
cluding RuntimeException) RuntimeException
Handling Re- Must be handled (using Optional to handle; often indi-
quirement try-catch) or declared (using cate programming errors to be
throws) fixed
Continued on next page

21 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

Table 2 – continued from previous page


Feature Checked Exceptions Unchecked Exceptions
Recovery Program is expected to recover Program usually cannot re-
from these errors cover; indicates a bug
Examples IOException, SQLException, ArithmeticException,
FileNotFoundException NullPointerException,
ArrayIndexOutOfBoundsException
Occurrence External circumstances (I/O, Programming errors (logic
network, database) flaws, invalid operations)

Step 3: Java Program to Demonstrate ArithmeticException Han-


dling
ArithmeticException is an unchecked exception that occurs when an exceptional arith-
metic condition arises, such as division by zero.
1 public class ArithmeticExceptionDemo {
2

3 public static void main(String[] args) {


4 System.out.println("---␣Demonstrating␣ArithmeticException␣Handling␣
---");
5

6 // Scenario 1: Division by zero - handled using try-catch


7 System.out.println("\nScenario␣1:␣Division␣by␣zero␣(handled)");
8 int numerator1 = 10;
9 int denominator1 = 0;
10 try {
11 // This line will throw an ArithmeticException
12 int result = numerator1 / denominator1;
13 System.out.println("Result␣of␣division:␣" + result); % This line
will not be executed
14 } catch (ArithmeticException e) {
15 % The catch block catches the ArithmeticException
16 System.err.println("Error:␣An␣ArithmeticException␣occurred!");
17 System.err.println("Reason:␣" + e.getMessage()); % Prints the
specific error message (e.g., "/␣by␣zero")
18 System.err.println("Please␣ensure␣the␣denominator␣is␣not␣zero.");
19 } finally {
20 % The finally block always executes, regardless of whether an
exception occurred or not
21 System.out.println("Finally␣block␣executed␣for␣Scenario␣1.");
22 }
23 System.out.println("Program␣continues␣after␣handling␣Scenario␣1.");
24

25 % Scenario 2: Another division by zero - demonstrating multiple catch


blocks
26 System.out.println("\nScenario␣2:␣Multiple␣catch␣blocks␣(handled)");
27 int[] numbers = {10, 2, 0, 5};
28 int index = 2; % This index will cause division by zero
29 int invalidIndex = 5; % This index will cause

22 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

ArrayIndexOutOfBoundsException
30

31 try {
32 System.out.println("Accessing␣element␣at␣index␣" + index + ":␣" +
numbers[index]);
33 int value = numbers[index];
34 int result = 100 / value; % Will throw ArithmeticException if
value is 0
35 System.out.println("Result␣of␣division:␣" + result);
36

37 System.out.println("Accessing␣element␣at␣invalid␣index␣" +
invalidIndex + ":␣" + numbers[invalidIndex]);
38 } catch (ArithmeticException e) {
39 System.err.println("Caught␣ArithmeticException:␣" +
e.getMessage());
40 System.err.println("Cannot␣divide␣by␣zero.␣Please␣check␣array␣
values.");
41 } catch (ArrayIndexOutOfBoundsException e) {
42 System.err.println("Caught␣ArrayIndexOutOfBoundsException:␣" +
e.getMessage());
43 System.err.println("Array␣index␣is␣out␣of␣bounds.␣Please␣check␣the␣
index.");
44 } catch (Exception e) { % Generic catch block for any other unexpected
exceptions
45 System.err.println("Caught␣a␣generic␣Exception:␣" +
e.getMessage());
46 } finally {
47 System.out.println("Finally␣block␣executed␣for␣Scenario␣2.");
48 }
49 System.out.println("Program␣continues␣after␣handling␣Scenario␣2.");
50

51 % Scenario 3: Division by zero - unhandled (will terminate program)


52 System.out.println("\nScenario␣3:␣Division␣by␣zero␣(unhandled␣-␣will␣
cause␣program␣termination)");
53 int numerator3 = 20;
54 int denominator3 = 0;
55 % If the following line is uncommented, the program will terminate here
56 % int result3 = numerator3 / denominator3;
57 % System.out.println("Result␣of␣unhandled␣division:␣" + result3);
58 System.out.println("This␣line␣will␣not␣be␣reached␣if␣unhandled␣
exception␣occurs␣above.");
59 }
60 }
Listing 4: ArithmeticException Handling Example

Line-by-Line Code Explanation:


• public class ArithmeticExceptionDemo { ... }: Defines the main class.
• public static void main(String[] args) { ... }: The entry point of the
program.

23 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• Scenario 1 (Handled ArithmeticException):

– int numerator1 = 10; int denominator1 = 0;: Initializes variables, with


denominator1 set to 0 to cause an ArithmeticException.
– try { ... }: The try block encloses the code that might throw an exception.
– int result = numerator1 / denominator1;: This is the line that will cause
ArithmeticException because division by zero is not allowed.
– System.out.println("Result of division: " + result);: This line will
not be executed if an exception occurs in the previous line, as control immedi-
ately jumps to the catch block.
– catch (ArithmeticException e) { ... }: This catch block specifically
handles ArithmeticException. When ArithmeticException is thrown, con-
trol transfers here.
– System.err.println("Error: An ArithmeticException occurred!");: Prints
an error message to the standard error stream.
– System.err.println("Reason: " + e.getMessage());: e.getMessage()
retrieves a concise description of the exception (e.g., "/ by zero").
– finally { ... }: The finally block contains code that will always be exe-
cuted, regardless of whether an exception was thrown or caught. It’s typically
used for cleanup operations (e.g., closing resources).
– System.out.println("Program continues after handling Scenario 1.");:
This demonstrates that the program’s execution continues normally after the
exception is handled.

• Scenario 2 (Multiple catch blocks):

– int[] numbers = {10, 2, 0, 5};: An array containing numbers, including


a zero.
– int index = 2;: Index pointing to the zero, which will cause ArithmeticException.
– int invalidIndex = 5;: An index that is out of bounds, intended to show
ArrayIndexOutOfBoundsException if reached.
– try { ... }: Contains code that might throw ArithmeticException or
ArrayIndexOutOfBoundsException.
– int result = 100 / value;: If value is 0 (from numbers[2]), this throws
ArithmeticException.
– System.out.println("Accessing element at invalid index " + invalidIndex
+ ": " + numbers[invalidIndex]);: This line would throw ArrayIndexOutOfBoundsExc
if the ArithmeticException didn’t occur first.
– catch (ArithmeticException e) { ... }: Catches ArithmeticException.
– catch (ArrayIndexOutOfBoundsException e) { ... }: Catches ArrayIndexOutOfBound
– catch (Exception e) { ... }: A generic catch block that can catch any
Exception type not caught by previous specific catch blocks. It’s good prac-
tice to place more specific catch blocks before more general ones.

24 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

– finally { ... }: Executes regardless of which catch block (if any) was
executed.

• Scenario 3 (Unhandled ArithmeticException):

– int result3 = numerator3 / denominator3;: If uncommented, this line


would throw an ArithmeticException.
– // int result3 = numerator3 / denominator3;: This line is commented
out to prevent program termination during the demonstration. If uncom-
mented, the program would crash at this point, and subsequent lines in main
would not be executed. This illustrates what happens when an unchecked
exception is not handled.

Output:
--- Demonstrating ArithmeticException Handling ---

Scenario 1: Division by zero (handled)


Error: An ArithmeticException occurred!
Reason: / by zero
Please ensure the denominator is not zero.
Finally block executed for Scenario 1.
Program continues after handling Scenario 1.

Scenario 2: Multiple catch blocks (handled)


Accessing element at index 2: 0
Caught ArithmeticException: / by zero
Cannot divide by zero. Please check array values.
Finally block executed for Scenario 2.
Program continues after handling Scenario 2.

Scenario 3: Division by zero (unhandled - will cause program termination)


This line will not be reached if unhandled exception occurs above.

25 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

2 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

UNIT 3: Java Stream API and


Control Flow

Q5(a): Explain Java stream API and its applications.


Describe Intermediate and terminal operations with
suitable example. Write a program to print sum of all
even numbers form an ArrayList<Integer> containing
all integers from 1 to 10.
Step 1: Detailed Explanation
Java Stream API
The Java Stream API, introduced in Java 8, provides a powerful and functional way to
process collections of objects. It is not a data structure itself; rather, it is a sequence of
elements from a source (like a collection, array, or I/O channel) that supports aggregate
operations. Streams allow you to perform complex data processing queries in a declarative
and concise manner, often leading to more readable and efficient code, especially for
parallel processing.
Key Characteristics of Java Streams:

• Declarative: You describe what you want to achieve, not how to achieve it.

• Functional: Operations are often expressed as lambda expressions, promoting func-


tional programming style.

• Non-mutating: Stream operations do not modify the source data structure. They
produce new streams or a final result.

• Lazy Evaluation: Intermediate operations are lazy; they are not executed until a
terminal operation is invoked. This allows for optimization.

• Pipelining: Stream operations can be chained together to form a pipeline.

• Can be parallelized: Streams can be easily converted to parallel streams, lever-


aging multiple CPU cores for faster processing.

• Single-use: A stream can be traversed only once. After a terminal operation is


performed, the stream is consumed and cannot be reused.

Applications of Java Stream API:

3 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• Data Filtering: Easily filter elements based on certain criteria (e.g., filter()).

• Data Transformation/Mapping: Transform elements from one type to another


(e.g., map()).

• Data Aggregation: Perform aggregate functions like sum, average, min, max (e.g.,
reduce(), sum(), count()).

• Data Sorting: Sort elements in ascending or descending order (e.g., sorted()).

• Data Grouping: Group elements based on a common characteristic (e.g., Collectors.groupingB

• Parallel Processing: Process large datasets efficiently using parallel streams.

• Eliminating Boilerplate Code: Reduces the need for explicit loops and condi-
tional statements, making code more concise.

Intermediate Operations:
Intermediate operations are operations that transform a stream into another stream.
They are "lazy," meaning they are not executed until a terminal operation is invoked.
This allows for optimizations where operations can be combined or short-circuited. You
can chain multiple intermediate operations together.
Common Intermediate Operations:

• filter(Predicate<T> predicate): Returns a stream consisting of the elements


of this stream that match the given predicate.

• map(Function<T, R> mapper): Returns a stream consisting of the results of ap-


plying the given function to the elements of this stream.

• flatMap(Function<T, Stream<R» mapper): Returns a stream consisting of the


results of replacing each element of this stream with the contents of a mapped
stream produced by applying the provided mapping function to each element.

• distinct(): Returns a stream consisting of the distinct elements (based on equals()).

• sorted(): Returns a stream consisting of the elements of this stream, sorted ac-
cording to natural order.

• peek(Consumer<T> action): Performs an action on each element as elements are


consumed from the stream. Useful for debugging.

• limit(long maxSize): Returns a stream consisting of at most the given number


of elements.

• skip(long n): Returns a stream consisting of the remaining elements after dis-
carding the first n elements.

Terminal Operations:
Terminal operations are operations that produce a result or a side-effect. Once a ter-
minal operation is invoked, the stream is consumed, and its intermediate operations are
executed. A stream can only have one terminal operation.
Common Terminal Operations:

4 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• forEach(Consumer<T> action): Performs an action for each element of this stream.

• collect(Collector<T, A, R> collector): Performs a mutable reduction oper-


ation on the elements of this stream using a Collector. Used to convert stream
elements into a collection (e.g., toList(), toSet(), toMap()).

• reduce(T identity, BinaryOperator<T> accumulator): Performs a reduction


on the elements of this stream, using an associative accumulation function, and
returns an Optional describing the reduced value.

• count(): Returns the count of elements in this stream.

• min(Comparator<T> comparator): Returns an Optional describing the minimum


element of this stream according to the provided Comparator.

• max(Comparator<T> comparator): Returns an Optional describing the maximum


element of this stream according to the provided Comparator.

• anyMatch(Predicate<T> predicate): Returns whether any elements of this stream


match the provided predicate.

• allMatch(Predicate<T> predicate): Returns whether all elements of this stream


match the provided predicate.

• noneMatch(Predicate<T> predicate): Returns whether no elements of this stream


match the provided predicate.

• findFirst(): Returns an Optional describing the first element of this stream, or


an empty Optional if the stream is empty.

• findAny(): Returns an Optional describing any element of this stream, or an


empty Optional if the stream is empty.

• sum(), average(), summaryStatistics(): Specific to primitive streams (IntStream,


LongStream, DoubleStream).

Step 3: Java Program to Print Sum of All Even Numbers from


an ArrayList<Integer> (1 to 10)

1 import java.util.ArrayList;
2 import java.util.Arrays;
3 import java.util.List;
4 import java.util.OptionalInt; % For sum() on IntStream
5

6 public class StreamApiEvenSum {


7

8 public static void main(String[] args) {


9 System.out.println("---␣Demonstrating␣Java␣Stream␣API␣---");
10

11 % 1. Create an ArrayList<Integer> containing all integers from 1 to 10


12 List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5,
6, 7, 8, 9, 10));

5 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

13

14 System.out.println("Original␣ArrayList:␣" + numbers);
15

16 % Using Stream API to find the sum of all even numbers


17 % Step 1: Get a stream from the ArrayList
18 % Step 2: Filter the stream to keep only even numbers (Intermediate
Operation)
19 % Step 3: Map the stream to an IntStream (Optional, but good for
sum/average)
20 % Step 4: Sum the elements (Terminal Operation)
21 int sumOfEvenNumbers = numbers.stream() % Creates a Stream<Integer>
22 .filter(n → n % 2 == 0) % Intermediate
operation: filters for even numbers
23 .mapToInt(Integer::intValue) %
Intermediate operation: converts Stream<Integer> to IntStream
24 .sum(); % Terminal operation: calculates
the sum of elements in IntStream
25

26 System.out.println("Sum␣of␣even␣numbers␣(using␣Stream␣API):␣" +
sumOfEvenNumbers);
27

28 System.out.println("\n---␣Another␣example:␣Filtering␣and␣collecting␣
---");
29 % Example: Filter numbers greater than 5 and collect them into a new
List
30 List<Integer> greaterThanFive = numbers.stream()
31 .filter(n → n > 5) %
Intermediate operation
32

.collect(java.util.stream.Collectors.toList()); % Terminal operation


33 System.out.println("Numbers␣greater␣than␣5:␣" + greaterThanFive);
34

35 System.out.println("\n---␣Example:␣Mapping␣and␣printing␣---");
36 % Example: Map numbers to their squares and print them
37 System.out.print("Squares␣of␣numbers:␣");
38 numbers.stream()
39 .map(n → n * n) % Intermediate operation: transforms each
number to its square
40 .forEach(n → System.out.print(n + "␣")); % Terminal operation:
prints each element
41 System.out.println(); % New line for formatting
42 }
43 }

Listing 1: Stream API Even Sum Example

Line-by-Line Code Explanation:


• import java.util.ArrayList; import java.util.Arrays; import java.util.List;
import java.util.OptionalInt;: Imports necessary utility classes for ArrayList,
Arrays, List, and OptionalInt.

6 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• public class StreamApiEvenSum { ... }: Defines the main class.

• public static void main(String[] args) { ... }: The entry point of the
program.

• List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6,


7, 8, 9, 10));: Creates an ArrayList named numbers and initializes it with in-
tegers from 1 to 10.

• numbers.stream(): This is the starting point of the stream pipeline. It converts


the ArrayList into a Stream<Integer>.

• .filter(n -> n % 2 == 0): This is an intermediate operation. It takes a


Predicate (a functional interface that returns a boolean) as an argument. The
lambda expression n -> n % 2 == 0 filters the stream, allowing only elements for
which the condition (number is even) is true to pass through to the next operation.

• .mapToInt(Integer::intValue): This is another intermediate operation. It


transforms the Stream<Integer> into an IntStream. Integer::intValue is a
method reference that effectively unwraps the Integer object to its primitive int
value. Using IntStream is more efficient for numerical operations like sum().

• .sum(): This is a terminal operation. It calculates the sum of all elements in the
IntStream and returns an int value. Once sum() is called, the stream is consumed,
and all the preceding intermediate operations (filter, mapToInt) are executed.

• System.out.println("Sum of even numbers (using Stream API): " + sumOfEvenNumbers)


Prints the calculated sum.

• Second Example (Filtering and Collecting):

– numbers.stream().filter(n -> n > 5): Creates a stream and filters for


numbers greater than 5.
– .collect(java.util.stream.Collectors.toList()): This is a terminal
operation that collects the elements of the stream into a new List. Collectors.toList()
is a common collector provided by the java.util.stream.Collectors class.

• Third Example (Mapping and Printing):

– numbers.stream().map(n -> n * n): This is an intermediate operation


that transforms each element n into n * n (its square). The stream now con-
tains the squares of the original numbers.
– .forEach(n -> System.out.print(n + " ")): This is a terminal opera-
tion that iterates over each element in the stream (which are now the squares)
and prints them to the console.

Output:
--- Demonstrating Java Stream API ---
Original ArrayList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Sum of even numbers (using Stream API): 30

7 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

--- Another example: Filtering and collecting ---


Numbers greater than 5: [6, 7, 8, 9, 10]

--- Example: Mapping and printing ---


Squares of numbers: 1 4 9 16 25 36 49 64 81 100

Q5(b): Compare and contrast switch-case statement


with switch-expression in Java. Explain with suitable
example.
Step 1: Detailed Explanation
switch-case Statement vs. switch-expression in Java
Traditionally, Java has used the switch-case statement for multi-way branching. Java
12 introduced switch-expression (as a preview feature, finalized in Java 14) to provide
a more concise and powerful way to handle conditional logic, especially when the switch
is used to produce a value.
switch-case Statement:
• Purpose: Primarily used for control flow, allowing the program to execute different
blocks of code based on the value of an expression.
• Syntax: Uses case labels, break statements (to prevent fall-through), and an
optional default case.
• Fall-through: By default, if a break statement is omitted, execution "falls through"
to the next case label. This can lead to subtle bugs if not handled carefully.
• No Value Production: A switch-case statement does not produce a value itself;
it executes a block of code. If you need a value, you typically assign it within each
case block to a variable declared outside the switch.
• Scope: Variables declared within a case block are typically scoped to that block,
but can sometimes lead to issues if not carefully managed.
switch-expression:
• Purpose: Designed to produce a value, making it useful in contexts where an
expression is expected (e.g., assignment, return statements). It can also be used for
control flow.
• Syntax: Uses case L -> syntax (arrow operator) instead of case L:. This ->
syntax implies a break, eliminating fall-through. For multiple statements, a yield
keyword is used to return a value from a block.
• No Fall-through: The -> operator implicitly handles break, so fall-through is not
an issue.
• Value Production: It can directly return a value. This value can be assigned to
a variable, returned from a method, or used in another expression.

8 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• yield Keyword: If a case needs to execute multiple statements, it can use a block
{} and then use the yield keyword to produce the value for the switch-expression.

• Exhaustiveness: When used as an expression, it often requires exhaustiveness,


meaning all possible cases must be covered (either explicitly or via a default case).
This helps prevent bugs.

Step 2: Comparison Table - switch-case Statement vs. switch-expression

Table 1: Comparison: switch-case Statement vs.


switch-expression

Feature switch-case Statement switch-expression (Java


14+)
Purpose Control flow (executes code Produces a value (can also be
blocks) used for control flow)
Syntax case L: (colon), requires case L -> (arrow operator), or
break; case L: with yield
Fall-through Default behavior; must use No fall-through; -> implies
break to prevent break
Value Production No; value must be assigned to Yes; directly produces a value
an external variable
Conciseness Can be verbose due to break More concise, especially for
statements simple cases
Exhaustiveness Not strictly enforced by com- Often enforced; requires all
piler for all types cases or default for value-
producing
Use Cases Executing different actions Assigning a value based on a
based on a value condition, return statements
Block Scope Variables declared in case Variables declared within a
blocks are generally visible un- case block (with -> or yield)
til the end of the switch state- are scoped only to that block.
ment, which can sometimes
lead to issues.

Step 3: Java Program to Demonstrate switch-case Statement


and switch-expression

1 public class SwitchComparison {


2

3 public static void main(String[] args) {


4 System.out.println("---␣Demonstrating␣switch-case␣Statement␣vs.␣
switch-expression␣---");
5

6 % Example 1: Using switch-case statement (Traditional)


7 int dayOfWeek = 3; % Wednesday
8 String dayTypeStatement;

9 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

10 System.out.println("\n---␣Using␣Traditional␣switch-case␣Statement␣
---");
11 switch (dayOfWeek) {
12 case 1:
13 dayTypeStatement = "Sunday";
14 break; % Essential to prevent fall-through
15 case 2:
16 dayTypeStatement = "Monday";
17 break;
18 case 3:
19 dayTypeStatement = "Tuesday"; % Oops, typo here for
demonstration of fall-through potential
20 % No break here for demonstration of fall-through
21 case 4:
22 dayTypeStatement = "Wednesday"; % This would be executed if
dayOfWeek was 3 and no break above
23 break;
24 case 5:
25 dayTypeStatement = "Thursday";
26 break;
27 case 6:
28 dayTypeStatement = "Friday";
29 break;
30 case 7:
31 dayTypeStatement = "Saturday";
32 break;
33 default:
34 dayTypeStatement = "Invalid␣day";
35 break;
36 }
37 System.out.println("Day␣type␣(statement,␣day␣" + dayOfWeek + "):␣" +
dayTypeStatement);
38

39 % Corrected switch-case statement


40 System.out.println("\n---␣Corrected␣Traditional␣switch-case␣Statement␣
---");
41 dayOfWeek = 3; % Wednesday
42 String correctedDayTypeStatement;
43 switch (dayOfWeek) {
44 case 1:
45 correctedDayTypeStatement = "Sunday";
46 break;
47 case 2:
48 correctedDayTypeStatement = "Monday";
49 break;
50 case 3:
51 correctedDayTypeStatement = "Tuesday";
52 break;
53 case 4:
54 correctedDayTypeStatement = "Wednesday";

10 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

55 break;
56 case 5:
57 correctedDayTypeStatement = "Thursday";
58 break;
59 case 6:
60 correctedDayTypeStatement = "Friday";
61 break;
62 case 7:
63 correctedDayTypeStatement = "Saturday";
64 break;
65 default:
66 correctedDayTypeStatement = "Invalid␣day";
67 break;
68 }
69 System.out.println("Day␣type␣(corrected␣statement,␣day␣" + dayOfWeek +
"):␣" + correctedDayTypeStatement);
70

71

72 % Example 2: Using switch-expression (Java 14+)


73 int month = 7; % July
74 String seasonExpression = switch (month) {
75 case 12, 1, 2 → "Winter";
76 case 3, 4, 5 → "Spring";
77 case 6, 7, 8 → "Summer";
78 case 9, 10, 11 → "Autumn";
79 default → "Unknown"; % Default case is mandatory for
exhaustiveness in switch expressions returning a value
80 };
81 System.out.println("\n---␣Using␣switch-expression␣(Arrow␣syntax)␣---");
82 System.out.println("Month␣" + month + "␣is␣in␣season:␣" +
seasonExpression);
83

84 % Example 3: switch-expression with block and yield


85 char grade = ’B’;
86 String feedback = switch (grade) {
87 case ’A’ → {
88 System.out.println("Excellent␣work!");
89 yield "Outstanding"; % Use yield to return a value from a block
90 }
91 case ’B’ → {
92 System.out.println("Good␣effort!");
93 yield "Very␣Good";
94 }
95 case ’C’ → {
96 System.out.println("Needs␣improvement.");
97 yield "Satisfactory";
98 }
99 case ’D’, ’F’ → {
100 System.out.println("Please␣review␣the␣material.");
101 yield "Poor";
102 }

11 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

103 default → {
104 System.out.println("Invalid␣grade␣entered.");
105 yield "N/A";
106 }
107 };
108 System.out.println("Feedback␣for␣grade␣" + grade + ":␣" + feedback);
109 }
110 }
Listing 2: Switch Statement vs. Expression Example

Line-by-Line Code Explanation:


• public class SwitchComparison { ... }: Defines the main class.
• public static void main(String[] args) { ... }: The entry point of the
program.
• Example 1: Traditional switch-case Statement (with fall-through demo):
– int dayOfWeek = 3;: The variable whose value will be evaluated by the
switch.
– String dayTypeStatement;: A variable to store the result, declared outside
the switch block.
– switch (dayOfWeek) { ... }: The switch statement.
– case 3:: The case label for dayOfWeek = 3.
– dayTypeStatement = "Tuesday";: Assigns a value.
– // No break here for demonstration of fall-through: This comment high-
lights the intentional omission of break. Because there’s no break, execution
will "fall through" to case 4.
– case 4: dayTypeStatement = "Wednesday"; break;: This line will also be
executed if dayOfWeek was 3 due to fall-through. The break here prevents
further fall-through.
– default: ... break;: The default case handles values not matched by
any case label. break is also needed here.
– The "Corrected Traditional switch-case Statement" section shows how break
statements are typically used to prevent fall-through and ensure only the in-
tended block executes.
• Example 2: switch-expression (Arrow syntax ->):
– int month = 7;: The variable for evaluation.
– String seasonExpression = switch (month) { ... };: This is a switch-expression.
Notice it’s on the right side of an assignment operator, meaning it produces a
value.
– case 12, 1, 2 -> "Winter";: This is the new case L -> value syntax. If
month is 12, 1, or 2, the switch-expression evaluates to "Winter". The ->
implicitly handles the break. Multiple case labels can be comma-separated.

12 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

– default -> "Unknown";: The default case is crucial for exhaustiveness when
the switch is used as an expression that returns a value. It handles any value
not explicitly covered by other cases.

• Example 3: switch-expression with block and yield:

– char grade = ’B’;: Variable for evaluation.


– String feedback = switch (grade) { ... };: Another switch-expression
producing a String value.
– case ’A’ -> { ... yield "Outstanding"; }: When a case needs to ex-
ecute multiple statements before producing a value, it uses a block {}. The
yield keyword is then used within that block to specify the value that the
switch-expression should return for that case.
– System.out.println("Excellent work!");: A side effect within the case
block.
– default -> { ... yield "N/A"; }: The default case also uses a block
and yield if multiple statements are needed.

Output:
--- Demonstrating switch-case Statement vs. switch-expression ---

--- Using Traditional switch-case Statement ---


Day type (statement, day 3): Wednesday

--- Corrected Traditional switch-case Statement ---


Day type (corrected statement, day 3): Tuesday

--- Using switch-expression (Arrow syntax) ---


Month 7 is in season: Summer

--- Example: switch-expression with block and yield ---


Good effort!
Feedback for grade B: Very Good

13 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

UNIT 4: Collections Framework

Q6(a): Describe Linked List in Java collection frame-


work. With suitable example describe any five meth-
ods available in Linked Lists.
Step 1: Detailed Explanation
Linked List in Java Collections Framework
The LinkedList class in Java is a part of the Java Collections Framework and implements
both the List interface and the Deque (Double-Ended Queue) interface. It uses a doubly
linked list data structure internally. This means that each element (node) in the list
stores not only the data but also a reference (or link) to the next element and a reference
to the previous element in the sequence.
Key Characteristics of LinkedList:

• Doubly Linked: Each node has pointers to both its predecessor and successor.
This allows for efficient traversal in both forward and backward directions.

• Non-contiguous Memory Allocation: Unlike ArrayList (which uses a dynamic


array), LinkedList elements are not stored in contiguous memory locations. Each
node can be anywhere in memory, and they are linked together by references.

• Dynamic Size: LinkedList can grow or shrink dynamically as elements are added
or removed.

• Efficient Insertions/Deletions: Adding or removing elements at any position


(especially at the beginning or end) is very efficient (O(1) time complexity) because
it only requires updating a few pointers.

• Inefficient Random Access: Accessing an element by its index (e.g., get(index))


is inefficient (O(n) time complexity) because the list must be traversed from the
beginning or end to reach the desired index.

• Implements List and Deque: This dual implementation provides List operations
(like add(index, element), get(index)) and Deque operations (like addFirst(),
addLast(), removeFirst(), removeLast()).

• Not Synchronized: LinkedList is not thread-safe. If used in a multi-threaded


environment, external synchronization must be applied.

When to use LinkedList:

15 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• When frequent insertions or deletions are required, especially at the beginning or


end of the list.

• When you need to implement a stack (LIFO) or a queue (FIFO) because LinkedList
provides Deque methods like push(), pop(), offer(), poll().

• When random access to elements is not a frequent operation.

Step 3: Java Program to Demonstrate LinkedList Methods

1 import java.util.LinkedList;
2 import java.util.Iterator;
3

4 public class LinkedListMethodsDemo {


5

6 public static void main(String[] args) {


7 System.out.println("---␣Demonstrating␣LinkedList␣Methods␣---");
8

9 % 1. Creating a LinkedList
10 LinkedList<String> fruits = new LinkedList<>();
11 System.out.println("Initial␣LinkedList:␣" + fruits); % Output: []
12

13 % 2. add(E e) - Appends the specified element to the end of this list.


14 fruits.add("Apple");
15 fruits.add("Banana");
16 fruits.add("Cherry");
17 System.out.println("After␣add(\"Apple\"),␣add(\"Banana\"),␣
add(\"Cherry\"):␣" + fruits);
18 % Output: [Apple, Banana, Cherry]
19

20 % 3. addFirst(E e) - Inserts the specified element at the beginning of


this list.
21 fruits.addFirst("Grape");
22 System.out.println("After␣addFirst(\"Grape\"):␣" + fruits);
23 % Output: [Grape, Apple, Banana, Cherry]
24

25 % 4. addLast(E e) - Appends the specified element to the end of this


list. (Same as add(E e))
26 fruits.addLast("Mango");
27 System.out.println("After␣addLast(\"Mango\"):␣" + fruits);
28 % Output: [Grape, Apple, Banana, Cherry, Mango]
29

30 % 5. get(int index) - Returns the element at the specified position in


this list.
31 String firstFruit = fruits.get(0);
32 String thirdFruit = fruits.get(2);
33 System.out.println("Element␣at␣index␣0:␣" + firstFruit); % Output:
Grape
34 System.out.println("Element␣at␣index␣2:␣" + thirdFruit); % Output:
Banana
35

16 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

36 % 6. remove(Object o) - Removes the first occurrence of the specified


element from this list, if it is present.
37 fruits.remove("Banana");
38 System.out.println("After␣remove(\"Banana\"):␣" + fruits);
39 % Output: [Grape, Apple, Cherry, Mango]
40

41 % 7. removeFirst() - Removes and returns the first element from this


list.
42 String removedFirst = fruits.removeFirst();
43 System.out.println("Removed␣first␣element:␣" + removedFirst); %
Output: Grape
44 System.out.println("After␣removeFirst():␣" + fruits);
45 % Output: [Apple, Cherry, Mango]
46

47 % 8. size() - Returns the number of elements in this list.


48 System.out.println("Current␣size␣of␣LinkedList:␣" + fruits.size()); %
Output: 3
49

50 % 9. contains(Object o) - Returns true if this list contains the


specified element.
51 System.out.println("Does␣list␣contain␣’Apple’?␣" +
fruits.contains("Apple")); % Output: true
52 System.out.println("Does␣list␣contain␣’Orange’?␣" +
fruits.contains("Orange")); % Output: false
53

54 % 10. Iterating through the LinkedList using an Iterator


55 System.out.println("\nIterating␣through␣LinkedList:");
56 Iterator<String> iterator = fruits.iterator();
57 while (iterator.hasNext()) {
58 System.out.println("␣␣" + iterator.next());
59 }
60

61 % 11. clear() - Removes all of the elements from this list.


62 fruits.clear();
63 System.out.println("After␣clear():␣" + fruits); % Output: []
64 System.out.println("Is␣list␣empty␣after␣clear()?␣" +
fruits.isEmpty()); % Output: true
65 }
66 }

Listing 3: LinkedList Methods Example

Line-by-Line Code Explanation:


• import java.util.LinkedList; import java.util.Iterator;: Imports the LinkedList
class and Iterator interface.

• public class LinkedListMethodsDemo { ... }: Defines the main class.

• public static void main(String[] args) { ... }: The entry point of the
program.

17 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• LinkedList<String> fruits = new LinkedList<>();: Creates an empty LinkedList


that will store String objects.

• fruits.add("Apple");: Adds "Apple" to the end of the list. This is a List interface
method.

• fruits.addFirst("Grape");: Adds "Grape" to the beginning of the list. This is a


Deque interface method, specific to LinkedList.

• fruits.addLast("Mango");: Adds "Mango" to the end of the list. This is also a


Deque interface method, equivalent to add().

• fruits.get(0); fruits.get(2);: Retrieves elements at specific indices. This op-


eration is O(n) for LinkedList.

• fruits.remove("Banana");: Removes the first occurrence of "Banana" from the


list.

• fruits.removeFirst();: Removes and returns the first element. This is an O(1)


operation.

• fruits.size();: Returns the number of elements in the list.

• fruits.contains("Apple");: Checks if the list contains a specific element.

• Iterator<String> iterator = fruits.iterator();: Obtains an Iterator to


traverse the list.

• while (iterator.hasNext()) { System.out.println(" " + iterator.next());


}: Loop to print each element using the iterator.

• fruits.clear();: Removes all elements from the list.

• fruits.isEmpty();: Checks if the list is empty.

Output:
--- Demonstrating LinkedList Methods ---
Initial LinkedList: []
After add("Apple"), add("Banana"), add("Cherry"): [Apple, Banana, Cherry]
After addFirst("Grape"): [Grape, Apple, Banana, Cherry]
After addLast("Mango"): [Grape, Apple, Banana, Cherry, Mango]
Element at index 0: Grape
Element at index 2: Banana
After remove("Banana"): [Grape, Apple, Cherry, Mango]
Removed first element: Grape
After removeFirst(): [Apple, Cherry, Mango]
Current size of LinkedList: 3
Does list contain ’Apple’? true
Does list contain ’Orange’? false

Iterating through LinkedList:

18 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

Apple
Cherry
Mango
After clear(): []
Is list empty after clear()? true

Q6(b): Describe HashMap in Java collection frame-


work. With suitable example describe any five meth-
ods available in HashMaps.
Step 1: Detailed Explanation
HashMap in Java Collections Framework
The HashMap class in Java is a part of the Java Collections Framework and implements
the Map interface. It provides a way to store data in key-value pairs. Each key-value
pair is known as an entry. HashMap uses a hash table for storage, which allows for very
fast retrieval of values based on their keys.
Key Characteristics of HashMap:

• Key-Value Pairs: Stores data as unique keys mapped to values. Each key must
be unique, but values can be duplicated.

• No Order Guarantee: HashMap does not maintain any insertion order or sorted
order of its elements. The order of elements can change over time, especially after
operations like put() or remove().

• Allows Nulls: HashMap allows one null key and multiple null values.

• Performance: Provides nearly constant-time (O(1) on average) performance for


basic operations like get(), put(), and remove(), assuming a good hash function
and minimal collisions. In the worst case (many collisions), performance can degrade
to O(n).

• Not Synchronized: HashMap is not thread-safe. If used in a multi-threaded envi-


ronment, external synchronization must be applied (e.g., using Collections.synchronizedMap()
or ConcurrentHashMap).

• Hashing: It uses the hashCode() and equals() methods of the keys to determine
where to store and retrieve elements. For custom objects used as keys, it’s crucial
to properly override hashCode() and equals().

How HashMap Works (Simplified):

1. When you call put(key, value), HashMap first calculates the hash code of the key
using key.hashCode().

2. This hash code is then used to determine the "bucket" (an index in an internal array)
where the key-value pair will be stored.

19 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

3. If multiple keys hash to the same bucket (a collision), HashMap handles this by
storing them in a linked list (or a balanced tree in Java 8+ for high collision rates)
within that bucket.

4. When you call get(key), HashMap again calculates the hash code of the provided
key to find the correct bucket. It then iterates through the elements in that bucket,
using key.equals() to find the exact matching key and return its associated value.

When to use HashMap:

• When you need to store data as key-value pairs.

• When fast lookup, insertion, and deletion operations are critical.

• When the order of elements is not important.

• When you need to quickly retrieve a value based on a unique identifier.

Step 3: Java Program to Demonstrate HashMap Methods

1 import java.util.HashMap;
2 import java.util.Map; % For Map.Entry
3 import java.util.Set; % For keySet() and entrySet()
4

5 public class HashMapMethodsDemo {


6

7 public static void main(String[] args) {


8 System.out.println("---␣Demonstrating␣HashMap␣Methods␣---");
9

10 % 1. Creating a HashMap
11 % Key: Integer (Employee ID), Value: String (Employee Name)
12 HashMap<Integer, String> employeeMap = new HashMap<>();
13 System.out.println("Initial␣HashMap:␣" + employeeMap); % Output: {}
14

15 % 2. put(K key, V value) - Inserts a key-value pair into the map.


16 employeeMap.put(101, "Alice␣Smith");
17 employeeMap.put(102, "Bob␣Johnson");
18 employeeMap.put(103, "Charlie␣Brown");
19 System.out.println("After␣put␣operations:␣" + employeeMap);
20 % Output (order may vary): {101=Alice Smith, 102=Bob Johnson,
103=Charlie Brown}
21

22 % 3. get(Object key) - Returns the value to which the specified key is


mapped, or null if this map contains no mapping for the key.
23 String employeeName102 = employeeMap.get(102);
24 String employeeName105 = employeeMap.get(105); % Key not present
25 System.out.println("Employee␣with␣ID␣102:␣" + employeeName102); %
Output: Bob Johnson
26 System.out.println("Employee␣with␣ID␣105:␣" + employeeName105); %
Output: null
27

20 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

28 % 4. containsKey(Object key) - Returns true if this map contains a


mapping for the specified key.
29 System.out.println("Does␣map␣contain␣key␣101?␣" +
employeeMap.containsKey(101)); % Output: true
30 System.out.println("Does␣map␣contain␣key␣104?␣" +
employeeMap.containsKey(104)); % Output: false
31

32 % 5. containsValue(Object value) - Returns true if this map maps one


or more keys to the specified value.
33 System.out.println("Does␣map␣contain␣value␣’Alice␣Smith’?␣" +
employeeMap.containsValue("Alice␣Smith")); % Output: true
34 System.out.println("Does␣map␣contain␣value␣’David␣Lee’?␣" +
employeeMap.containsValue("David␣Lee")); % Output: false
35

36 % 6. remove(Object key) - Removes the mapping for the specified key


from this map if it is present.
37 String removedName = employeeMap.remove(101);
38 System.out.println("Removed␣employee␣with␣ID␣101:␣" + removedName); %
Output: Alice Smith
39 System.out.println("HashMap␣after␣removing␣101:␣" + employeeMap);
40 % Output (order may vary): {102=Bob Johnson, 103=Charlie Brown}
41

42 % 7. size() - Returns the number of key-value mappings in this map.


43 System.out.println("Current␣size␣of␣HashMap:␣" + employeeMap.size());
% Output: 2
44

45 % 8. Iterating through HashMap (Entry Set)


46 System.out.println("\nIterating␣through␣HashMap␣(Key-Value␣pairs):");
47 for (Map.Entry<Integer, String> entry : employeeMap.entrySet()) {
48 System.out.println("␣␣ID:␣" + entry.getKey() + ",␣Name:␣" +
entry.getValue());
49 }
50

51 % 9. Iterating through HashMap (Key Set)


52 System.out.println("\nIterating␣through␣HashMap␣(Keys␣only):");
53 Set<Integer> keys = employeeMap.keySet();
54 for (Integer id : keys) {
55 System.out.println("␣␣Employee␣ID:␣" + id);
56 }
57

58 % 10. putAll(Map<? extends K, ? extends V> m) - Copies all of the


mappings from the specified map to this map.
59 HashMap<Integer, String> newEmployees = new HashMap<>();
60 newEmployees.put(104, "Diana␣Prince");
61 newEmployees.put(105, "Clark␣Kent");
62 employeeMap.putAll(newEmployees);
63 System.out.println("HashMap␣after␣putAll:␣" + employeeMap);
64 % Output (order may vary): {102=Bob Johnson, 103=Charlie Brown,
104=Diana Prince, 105=Clark Kent}
65

66 % 11. clear() - Removes all of the mappings from this map.

21 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

67 employeeMap.clear();
68 System.out.println("After␣clear():␣" + employeeMap); % Output: {}
69 System.out.println("Is␣map␣empty␣after␣clear()?␣" +
employeeMap.isEmpty()); % Output: true
70 }
71 }
Listing 4: HashMap Methods Example

Line-by-Line Code Explanation:


• import java.util.HashMap; import java.util.Map; import java.util.Set;:
Imports necessary classes for HashMap, Map.Entry, and Set.

• public class HashMapMethodsDemo { ... }: Defines the main class.

• public static void main(String[] args) { ... }: The entry point of the
program.

• HashMap<Integer, String> employeeMap = new HashMap<>();: Creates an empty


HashMap where keys are Integer (employee IDs) and values are String (employee
names).

• employeeMap.put(101, "Alice Smith");: Adds a key-value pair to the map. If


the key already exists, its value is updated.

• employeeMap.get(102);: Retrieves the value associated with the key 102. Returns
null if the key is not found.

• employeeMap.containsKey(101);: Checks if the map contains the specified key.

• employeeMap.containsValue("Alice Smith");: Checks if the map contains the


specified value.

• employeeMap.remove(101);: Removes the key-value pair associated with the key


101. Returns the removed value, or null if the key was not found.

• employeeMap.size();: Returns the number of key-value pairs in the map.

• for (Map.Entry<Integer, String> entry : employeeMap.entrySet()) { ...


}: This is the most common and efficient way to iterate over a HashMap to access
both keys and values. entrySet() returns a Set of Map.Entry objects, where each
Entry represents a key-value pair.

• for (Integer id : employeeMap.keySet()) { ... }: Iterates only over the


keys of the HashMap. keySet() returns a Set containing all the keys. You can then
use get(id) to retrieve the value for each key.

• employeeMap.putAll(newEmployees);: Copies all mappings from newEmployees


HashMap into employeeMap.

• employeeMap.clear();: Removes all key-value mappings from the map.

• employeeMap.isEmpty();: Checks if the map is empty.

22 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

Output (Order of elements in HashMap output may vary):


--- Demonstrating HashMap Methods ---
Initial HashMap: {}
After put operations: {101=Alice Smith, 102=Bob Johnson, 103=Charlie Brown}
Employee with ID 102: Bob Johnson
Employee with ID 105: null
Does map contain key 101? true
Does map contain key 104? false
Does map contain value ’Alice Smith’? true
Does map contain value ’David Lee’? false
Removed employee with ID 101: Alice Smith
HashMap after removing 101: {102=Bob Johnson, 103=Charlie Brown}
Current size of HashMap: 2

Iterating through HashMap (Key-Value pairs):


ID: 102, Name: Bob Johnson
ID: 103, Name: Charlie Brown

Iterating through HashMap (Keys only):


Employee ID: 102
Employee ID: 103
HashMap after putAll: {102=Bob Johnson, 103=Charlie Brown, 104=Diana Prince, 105=Clark
After clear(): {}
Is map empty after clear()? true

23 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

UNIT 5: Spring Framework

Q7(a): Describe following: 1. Spring container 2.


Spring bean life cycle
Step 1: Detailed Explanation
1. Spring Container
The Spring Container is the core of the Spring Framework. It is responsible for creating,
configuring, and managing the lifecycle of Java objects (known as Spring Beans). It
uses Dependency Injection (DI) to manage the dependencies between objects, making
applications loosely coupled, modular, and easy to test.
Think of the Spring Container as a sophisticated factory that knows how to build,
assemble, and manage all the components (beans) of your application. Instead of you
manually creating objects and wiring them together, you tell the Spring Container what
objects you need and what their dependencies are, and it takes care of the rest.
Key Responsibilities of the Spring Container:

• Bean Instantiation: Creates instances of beans based on configuration.

• Bean Configuration: Injects dependencies into beans (Dependency Injection) and


configures their properties.

• Bean Lifecycle Management: Manages the entire lifecycle of a bean, from cre-
ation to destruction.

• Dependency Resolution: Identifies and resolves the dependencies between dif-


ferent beans.

• Scope Management: Manages the scope of beans (e.g., singleton, prototype).

• Aspect-Oriented Programming (AOP) Integration: Integrates AOP func-


tionalities.

Types of Spring Containers:


Spring provides two main types of containers:

• BeanFactory (Basic Container):

– This is the simplest form of the container.


– It provides basic DI functionality.
– It loads beans lazily, meaning a bean is instantiated only when it’s explicitly
requested (getBean()).

25 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

– It’s typically used for simpler applications or resource-constrained environ-


ments.
– Example: XmlBeanFactory (deprecated in favor of ApplicationContext).

• ApplicationContext (Advanced Container):

– This is a sub-interface of BeanFactory and is the recommended and most


commonly used container in Spring applications.
– It provides all the functionalities of BeanFactory plus more enterprise-specific
features.
– Eager Loading: By default, it loads all singleton beans eagerly at startup.
– Internationalization (I18n): Support for message sources.
– Event Propagation: Publishes and listens to application events.
– Resource Loading: Provides generic resource loading capabilities.
– Annotation Support: Better support for annotations (e.g., @Autowired,
@Component).
– Web Application Support: Specific implementations for web applications
(e.g., WebApplicationContext).
– Common implementations: ClassPathXmlApplicationContext, FileSystemXmlApplicatio
AnnotationConfigApplicationContext, WebApplicationContext.

Configuration Metadata:
The Spring Container needs to know which beans to manage and how to configure them.
This information is provided through configuration metadata, which can be in various
forms:
• XML-based configuration: Using XML files to define beans and their dependen-
cies.

• Annotation-based configuration: Using annotations (e.g., @Component, @Service,


@Repository, @Autowired) directly in Java classes.

• Java-based configuration: Using Java classes with @Configuration and @Bean


annotations to programmatically define beans.
Inversion of Control (IoC) and Dependency Injection (DI):
The Spring Container is built on the principle of Inversion of Control (IoC). IoC means
that the control of object creation and dependency management is inverted from the
application code to the framework (the Spring Container).
Dependency Injection (DI) is a specific implementation of IoC. Instead of objects
creating their dependencies or looking them up, the Spring Container "injects" (provides)
the required dependencies into the objects. This can be done via:
• Constructor Injection: Dependencies are provided through the class constructor.

• Setter Injection: Dependencies are provided through setter methods.

• Field Injection: Dependencies are injected directly into fields (less recommended
for testability).

26 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

2. Spring Bean Lifecycle


The Spring Container manages the entire lifecycle of a Spring Bean, from its instantiation
to its destruction. Understanding the bean lifecycle is crucial for performing actions at
specific stages, such as initialization, resource setup, or cleanup.
Here are the main phases and callback methods involved in a Spring Bean’s lifecycle:
Phases of a Spring Bean Lifecycle:

1. Instantiation:

• The Spring Container finds the bean definition (from XML, annotations, or
Java config).
• It creates an instance of the bean using its constructor (usually the default
constructor, or a specific one if configured).

2. Populate Properties (Dependency Injection):

• After instantiation, the container injects the dependencies (properties) into the
bean. This is where @Autowired, XML <property>, or constructor arguments
are used to set values for fields or call setters.

3. Bean Name Awareness (BeanNameAware):

• If the bean implements the BeanNameAware interface, the container calls its
setBeanName() method, passing the ID of the bean as defined in the configu-
ration.

4. Bean Factory Awareness (BeanFactoryAware):

• If the bean implements the BeanFactoryAware interface, the container calls its
setBeanFactory() method, passing the BeanFactory instance that created it.
(Less common with ApplicationContext).

5. Application Context Awareness (ApplicationContextAware):

• If the bean implements the ApplicationContextAware interface, the container


calls its setApplicationContext() method, passing the ApplicationContext
instance that created it.

6. Post-Processor Pre-Initialization (BeanPostProcessor.postProcessBeforeInitializatio

• If any BeanPostProcessor implementations are registered, their postProcessBeforeInitial


method is called. This allows for custom logic to be applied to beans before
their initialization callbacks.

7. Initialization Callbacks:

• This is a crucial phase for performing initialization logic, setting up resources,


or validating properties. There are three ways to define initialization callbacks:
– @PostConstruct (JSR-250 annotation): If present on a method, this
method is called after dependency injection is complete. (Recommended
for annotation-based config).

27 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

– InitializingBean interface (afterPropertiesSet()): If the bean im-


plements InitializingBean, its afterPropertiesSet() method is called.
– Custom init-method (XML/Java config): A method specified in the
bean definition (e.g., <bean init-method="myInitMethod"> or @Bean(initMethod
= "myInitMethod")) is called.
• Order: @PostConstruct → afterPropertiesSet() → Custom init-method.

8. Post-Processor Post-Initialization (BeanPostProcessor.postProcessAfterInitializatio

• If any BeanPostProcessor implementations are registered, their postProcessAfterInitiali


method is called. This allows for custom logic to be applied to beans after their
initialization callbacks. This is often used for proxying beans (e.g., AOP).

9. Ready for Use:

• The bean is now fully initialized and configured and is ready to be used by the
application. It resides in the Spring Container.

10. Pre-Destroy Callbacks:

• When the Spring Container is shut down, it calls destruction callbacks for
singleton-scoped beans (prototype beans are not managed for destruction by
the container). This phase is used for releasing resources, closing connections,
etc. There are three ways to define destruction callbacks:
– @PreDestroy (JSR-250 annotation): If present on a method, this method
is called before the bean is destroyed. (Recommended for annotation-based
config).
– DisposableBean interface (destroy()): If the bean implements DisposableBean,
its destroy() method is called.
– Custom destroy-method (XML/Java config): A method specified in
the bean definition (e.g., <bean destroy-method="myDestroyMethod">
or @Bean(destroyMethod = "myDestroyMethod")) is called.
• Order: @PreDestroy → destroy() → Custom destroy-method.

11. Destruction:

• The bean instance is finally garbage collected by the JVM.

Diagrammatic Representation (Conceptual Flow):

+---------------------+
| 1. Instantiation |
| (Constructor Call) |
+---------------------+
|
V
+---------------------+
| 2. Populate |
| Properties |
| (Dependency Inj.) |

28 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

+---------------------+
|
V
+---------------------+
| 3. BeanNameAware |
| 4. BeanFactoryAware |
| 5. AppContextAware |
+---------------------+
|
V
+---------------------+
| 6. BeanPostProcessor|
| (Before Init) |
+---------------------+
|
V
+---------------------+
| 7. Initialization |
| - @PostConstruct |
| - InitializingBean |
| - init-method |
+---------------------+
|
V
+---------------------+
| 8. BeanPostProcessor|
| (After Init) |
+---------------------+
|
V
+---------------------+
| 9. Bean is Ready |
| for Use |
+---------------------+
| (Container Shutdown)
V
+---------------------+
| 10. Pre-Destroy |
| - @PreDestroy |
| - DisposableBean |
| - destroy-method |
+---------------------+
|
V
+---------------------+
| 11. Destruction |
| (Garbage Collection)|
+---------------------+

29 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

Example (Conceptual Code Snippets):

1 % Example Bean demonstrating lifecycle callbacks


2 import org.springframework.beans.factory.DisposableBean;
3 import org.springframework.beans.factory.InitializingBean;
4 import org.springframework.beans.factory.BeanNameAware;
5 import org.springframework.context.ApplicationContextAware;
6 import org.springframework.context.ApplicationContext;
7 import javax.annotation.PostConstruct;
8 import javax.annotation.PreDestroy;
9

10 public class MySpringBean implements InitializingBean, DisposableBean,


BeanNameAware, ApplicationContextAware {
11

12 private String message;


13

14 % 1. Constructor
15 public MySpringBean() {
16 System.out.println("1.␣MySpringBean:␣Constructor␣called.");
17 }
18

19 % 2. Setter for property injection


20 public void setMessage(String message) {
21 this.message = message;
22 System.out.println("2.␣MySpringBean:␣Set␣’message’␣property:␣" +
message);
23 }
24

25 % 3. BeanNameAware callback
26 @Override
27 public void setBeanName(String name) {
28 System.out.println("3.␣MySpringBean:␣setBeanName␣called.␣Bean␣Name:␣"
+ name);
29 }
30

31 % 5. ApplicationContextAware callback
32 @Override
33 public void setApplicationContext(ApplicationContext applicationContext) {
34 System.out.println("5.␣MySpringBean:␣setApplicationContext␣called.");
35 }
36

37 % 7. Initialization Callback 1: @PostConstruct


38 @PostConstruct
39 public void postConstructMethod() {
40 System.out.println("7.1␣MySpringBean:␣@PostConstruct␣method␣called.");
41 }
42

43 % 7. Initialization Callback 2: InitializingBean interface


44 @Override
45 public void afterPropertiesSet() throws Exception {

30 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

46 System.out.println("7.2␣MySpringBean:␣afterPropertiesSet()␣called␣
(InitializingBean).");
47 }
48

49 % 7. Initialization Callback 3: Custom init-method


50 public void customInitMethod() {
51 System.out.println("7.3␣MySpringBean:␣customInitMethod()␣called␣
(configured␣via␣XML/Java␣config).");
52 }
53

54 % Method to be used after initialization


55 public void doSomething() {
56 System.out.println("9.␣MySpringBean:␣doSomething()␣called.␣Message:␣"
+ message);
57 }
58

59 % 10. Pre-Destroy Callback 1: @PreDestroy


60 @PreDestroy
61 public void preDestroyMethod() {
62 System.out.println("10.1␣MySpringBean:␣@PreDestroy␣method␣called.");
63 }
64

65 % 10. Pre-Destroy Callback 2: DisposableBean interface


66 @Override
67 public void destroy() throws Exception {
68 System.out.println("10.2␣MySpringBean:␣destroy()␣called␣
(DisposableBean).");
69 }
70

71 % 10. Pre-Destroy Callback 3: Custom destroy-method


72 public void customDestroyMethod() {
73 System.out.println("10.3␣MySpringBean:␣customDestroyMethod()␣called␣
(configured␣via␣XML/Java␣config).");
74 }
75 }
Listing 5: MySpringBean Class

Configuration (e.g., Java Config):

1 import
org.springframework.context.annotation.AnnotationConfigApplicationContext;
2 import org.springframework.context.annotation.Bean;
3 import org.springframework.context.annotation.Configuration;
4

5 @Configuration
6 public class AppConfig {
7

8 @Bean(initMethod = "customInitMethod", destroyMethod =


"customDestroyMethod")
9 public MySpringBean mySpringBean() {

31 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

10 MySpringBean bean = new MySpringBean();


11 bean.setMessage("Hello␣from␣Spring␣Bean!");
12 return bean;
13 }
14

15 % Optional: A BeanPostProcessor to observe steps 6 and 8


16 @Bean
17 public MyBeanPostProcessor myBeanPostProcessor() {
18 return new MyBeanPostProcessor();
19 }
20 }
21

22 % Example BeanPostProcessor
23 import org.springframework.beans.BeansException;
24 import org.springframework.beans.factory.config.BeanPostProcessor;
25

26 class MyBeanPostProcessor implements BeanPostProcessor {


27 @Override
28 public Object postProcessBeforeInitialization(Object bean, String
beanName) throws BeansException {
29 if (bean instanceof MySpringBean) {
30 System.out.println("6.␣BeanPostProcessor:␣Before␣initialization␣of␣
" + beanName);
31 }
32 return bean;
33 }
34

35 @Override
36 public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
37 if (bean instanceof MySpringBean) {
38 System.out.println("8.␣BeanPostProcessor:␣After␣initialization␣of␣
" + beanName);
39 }
40 return bean;
41 }
42 }
43

44 % Main class to run the application


45 public class MainApp {
46 public static void main(String[] args) {
47 System.out.println("---␣Starting␣Spring␣Application␣Context␣---");
48 % Create ApplicationContext
49 AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(AppConfig.class);
50

51 System.out.println("\n---␣Getting␣Bean␣from␣Context␣---");
52 MySpringBean bean = context.getBean(MySpringBean.class);
53 bean.doSomething();
54

55 System.out.println("\n---␣Closing␣Spring␣Application␣Context␣---");

32 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

56 % Close the context to trigger destroy methods


57 context.close();
58 System.out.println("---␣Spring␣Application␣Context␣Closed␣---");
59 }
60 }
Listing 6: AppConfig, MyBeanPostProcessor, and MainApp Classes

Conceptual Output Order (Illustrative, actual order of PostProcessors and


Awareness interfaces might vary slightly depending on Spring version and
specific setup):
--- Starting Spring Application Context ---
1. MySpringBean: Constructor called.
2. MySpringBean: Set ’message’ property: Hello from Spring Bean!
3. MySpringBean: setBeanName called. Bean Name: mySpringBean
5. MySpringBean: setApplicationContext called.
6. BeanPostProcessor: Before initialization of mySpringBean
7.1 MySpringBean: @PostConstruct method called.
7.2 MySpringBean: afterPropertiesSet() called (InitializingBean).
7.3 MySpringBean: customInitMethod() called (configured via XML/Java config).
8. BeanPostProcessor: After initialization of mySpringBean

--- Getting Bean from Context ---


9. MySpringBean: doSomething() called. Message: Hello from Spring Bean!

--- Closing Spring Application Context ---


10.1 MySpringBean: @PreDestroy method called.
10.2 MySpringBean: destroy() called (DisposableBean).
10.3 MySpringBean: customDestroyMethod() called (configured via XML/Java config).
--- Spring Application Context Closed ---

Q7(b): Describe following: 1. Spring boot framework


and its benefits 2. RESTFUL API with Spring boot
Step 1: Detailed Explanation
1. Spring Boot Framework and its Benefits
Spring Boot is an opinionated, convention-over-configuration framework built on top of
the Spring Framework. Its primary goal is to simplify the development of production-
ready, stand-alone Spring applications by reducing boilerplate configuration and providing
sensible defaults. It allows developers to "just run" their applications.
While the core Spring Framework provides powerful features like Dependency Injection
and AOP, it often requires a significant amount of configuration (XML or Java config) to
set up even a basic application. Spring Boot addresses this complexity by providing:

• Auto-configuration: Automatically configures your Spring application based on


the JARs present on your classpath. For example, if spring-webmvc is on the class-

33 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

path, Spring Boot assumes you’re building a web application and auto-configures a
DispatcherServlet.

• Starter Dependencies: Provides a set of convenient dependency descriptors that


you can include in your build. These starters bundle common dependencies together,
simplifying your pom.xml (for Maven) or build.gradle (for Gradle). For example,
spring-boot-starter-web includes Spring MVC, Tomcat, Jackson, etc.

• Embedded Servers: Allows you to embed popular web servers like Tomcat, Jetty,
or Undertow directly into your executable JAR, eliminating the need for separate
WAR deployments.

• Production-Ready Features: Offers features for monitoring, health checks, ex-


ternalized configuration, and more, making it easier to move applications to pro-
duction.

• No XML Configuration (mostly): Strongly favors annotation-based and Java-


based configuration over XML, further reducing setup complexity.

Benefits of Spring Boot:

1. Rapid Application Development (RAD):

• Reduced Boilerplate: Significantly cuts down on the amount of configura-


tion code (XML or verbose Java config) you need to write.
• Sensible Defaults: Provides intelligent defaults for common scenarios, allow-
ing you to get started quickly.
• Embedded Servers: No need to deploy WAR files to external application
servers; just run the JAR.

2. Simplified Dependency Management:

• Starter POMs: spring-boot-starter-* dependencies automatically pull in


all necessary transitive dependencies, ensuring compatible versions and reduc-
ing dependency conflicts. This saves a lot of time and effort.

3. Increased Productivity:

• Developers can focus more on business logic rather than infrastructure setup.
• Faster development cycles due to less configuration and quick restarts.

4. Production-Ready Features:

• Actuator: Provides endpoints for monitoring and managing your application


in production (e.g., health, metrics, info).
• Externalized Configuration: Easy to manage application properties from
various sources (properties files, YAML, environment variables, command-line
arguments).
• Logging: Default logging setup with Logback, easily configurable.

5. Microservices Friendly:

34 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

• Its stand-alone, embedded server nature makes it an excellent choice for build-
ing small, independent microservices that can be deployed and scaled indepen-
dently.

6. No Code Generation & No XML Configuration:

• Spring Boot doesn’t generate code.


• It generally avoids XML configuration, favoring annotations and Java-based
configuration, leading to cleaner and more maintainable code.

7. Opinionated Approach:

• While opinionated, it’s also highly customizable. You can easily override auto-
configurations and defaults if needed.

In summary, Spring Boot makes developing Spring-based applications much faster, easier,
and more efficient, especially for building microservices and RESTful APIs.
2. RESTFUL API with Spring Boot
REST (Representational State Transfer) is an architectural style for designing net-
worked applications. It’s not a protocol or a standard, but a set of principles that guide
the design of scalable, stateless, and cacheable web services.
Key Principles of REST:

• Client-Server Architecture: Separation of concerns between client and server.

• Statelessness: Each request from client to server must contain all the information
needed to understand the request. The server should not store any client context
between requests.

• Cacheability: Responses can be explicitly or implicitly defined as cacheable or


non-cacheable.

• Layered System: A client cannot ordinarily tell whether it is connected directly


to the end server, or to an intermediary along the way.

• Uniform Interface: This is the most crucial principle, defining how clients interact
with the service. It involves:

– Resource Identification: Resources are identified by URIs (e.g., /products,


/users/123).
– Resource Manipulation through Representations: Resources are ma-
nipulated using representations (e.g., JSON, XML).
– Self-descriptive Messages: Each message includes enough information to
describe how to process the message.
– Hypermedia as the Engine of Application State (HATEOAS): Re-
sources contain links to related resources, guiding the client through the ap-
plication. (Often considered an advanced REST principle, not always strictly
followed in all "RESTful" APIs).

35 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

Building RESTful APIs with Spring Boot:


Spring Boot, combined with Spring MVC, makes it incredibly easy and efficient to develop
RESTful APIs.
Core Components and Annotations in Spring Boot for REST:

• @RestController:

– A convenience annotation that combines @Controller and @ResponseBody.


– @Controller marks a class as a Spring MVC controller.
– @ResponseBody indicates that the return value of a method should be bound
directly to the web response body. This means Spring will automatically con-
vert your Java objects (e.g., a Product object) into a suitable format (like
JSON or XML) and send it as the response.

• @RequestMapping:

– Used to map web requests to specific handler methods or classes.


– Can be used at the class level (to define a base URI for all methods in the
controller) or at the method level.
– Attributes like method (e.g., RequestMethod.GET, RequestMethod.POST) or
more specific annotations like @GetMapping, @PostMapping, @PutMapping, @DeleteMapping
are used to specify HTTP methods.

• @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping:

– Shorthand, specialized versions of @RequestMapping for common HTTP meth-


ods. They are more readable and preferred.
– @GetMapping("/users/{id}"): Handles HTTP GET requests for retrieving
resources.
– @PostMapping("/users"): Handles HTTP POST requests for creating new
resources.
– @PutMapping("/users/{id}"): Handles HTTP PUT requests for updating
existing resources (full replacement).
– @DeleteMapping("/users/{id}"): Handles HTTP DELETE requests for delet-
ing resources.
– @PatchMapping("/users/{id}"): Handles HTTP PATCH requests for partial
updates of resources.

• @PathVariable:

– Used to extract values from the URI path. For example, in /users/{id},
@PathVariable("id") int userId would extract the id value.

• @RequestParam:

– Used to extract values from query parameters. For example, in /products?category=electro


@RequestParam("category") String category.

• @RequestBody:

36 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

– Used to bind the HTTP request body to a Java object. Typically used with
@PostMapping and @PutMapping to receive JSON or XML data from the client
and convert it into a Java object.

• ResponseEntity:

– A class that represents the entire HTTP response, including status code, head-
ers, and body. It provides more control over the response than just returning
a Java object.

Example of a Simple RESTful API with Spring Boot:


Let’s imagine an API for managing Product resources.
1 % Product.java (Model/Entity)
2 public class Product {
3 private Long id;
4 private String name;
5 private double price;
6

7 % Constructors
8 public Product() {}
9

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


11 this.id = id;
12 this.name = name;
13 this.price = price;
14 }
15

16 % Getters and Setters


17 public Long getId() { return id; }
18 public void setId(Long id) { this.id = id; }
19 public String getName() { return name; }
20 public void setName(String name) { this.name = name; }
21 public double getPrice() { return price; }
22 public void setPrice(double price) { this.price = price; }
23

24 @Override
25 public String toString() {
26 return "Product{" +
27 "id=" + id +
28 ",␣name=’" + name + ’\’’ +
29 ",␣price=" + price +
30 ’}’;
31 }
32 }
Listing 7: Product Class

1 % ProductService.java (Service Layer - Business Logic)


2 import org.springframework.stereotype.Service;
3 import java.util.ArrayList;

37 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

4 import java.util.List;
5 import java.util.Optional;
6 import java.util.concurrent.atomic.AtomicLong;
7

8 @Service
9 public class ProductService {
10 private final List<Product> products = new ArrayList<>();
11 private final AtomicLong counter = new AtomicLong();
12

13 public ProductService() {
14 % Initialize with some dummy data
15 products.add(new Product(counter.incrementAndGet(), "Laptop",
1200.00));
16 products.add(new Product(counter.incrementAndGet(), "Mouse", 25.00));
17 products.add(new Product(counter.incrementAndGet(), "Keyboard",
75.00));
18 }
19

20 public List<Product> getAllProducts() {


21 return products;
22 }
23

24 public Optional<Product> getProductById(Long id) {


25 return products.stream().filter(p → p.getId().equals(id)).findFirst();
26 }
27

28 public Product createProduct(Product product) {


29 product.setId(counter.incrementAndGet());
30 products.add(product);
31 return product;
32 }
33

34 public Optional<Product> updateProduct(Long id, Product updatedProduct) {


35 return getProductById(id).map(existingProduct → {
36 existingProduct.setName(updatedProduct.getName());
37 existingProduct.setPrice(updatedProduct.getPrice());
38 return existingProduct;
39 });
40 }
41

42 public boolean deleteProduct(Long id) {


43 return products.removeIf(p → p.getId().equals(id));
44 }
45 }
Listing 8: ProductService Class

1 % ProductController.java (REST Controller)


2 import org.springframework.beans.factory.annotation.Autowired;
3 import org.springframework.http.HttpStatus;
4 import org.springframework.http.ResponseEntity;
5 import org.springframework.web.bind.annotation.*;

38 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

6 import java.util.List;
7

8 @RestController % Combines @Controller and @ResponseBody


9 @RequestMapping("/api/products") % Base URI for all endpoints in this
controller
10 public class ProductController {
11

12 private final ProductService productService;


13

14 @Autowired % Injects ProductService


15 public ProductController(ProductService productService) {
16 this.productService = productService;
17 }
18

19 % GET /api/products - Get all products


20 @GetMapping
21 public ResponseEntity<List<Product>> getAllProducts() {
22 List<Product> products = productService.getAllProducts();
23 return new ResponseEntity<>(products, HttpStatus.OK);
24 }
25

26 % GET /api/products/{id} - Get product by ID


27 @GetMapping("/{id}")
28 public ResponseEntity<Product> getProductById(@PathVariable Long id) {
29 return productService.getProductById(id)
30 .map(product → new ResponseEntity<>(product, HttpStatus.OK))
31 .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
32 }
33

34 % POST /api/products - Create a new product


35 @PostMapping
36 public ResponseEntity<Product> createProduct(@RequestBody Product product)
{
37 Product createdProduct = productService.createProduct(product);
38 return new ResponseEntity<>(createdProduct, HttpStatus.CREATED);
39 }
40

41 % PUT /api/products/{id} - Update an existing product


42 @PutMapping("/{id}")
43 public ResponseEntity<Product> updateProduct(@PathVariable Long id,
@RequestBody Product product) {
44 return productService.updateProduct(id, product)
45 .map(updatedProduct → new ResponseEntity<>(updatedProduct,
HttpStatus.OK))
46 .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
47 }
48

49 % DELETE /api/products/{id} - Delete a product


50 @DeleteMapping("/{id}")
51 public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
52 if (productService.deleteProduct(id)) {

39 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

53 return new ResponseEntity<>(HttpStatus.NO_CONTENT); % 204 No


Content
54 } else {
55 return new ResponseEntity<>(HttpStatus.NOT_FOUND); % 404 Not Found
56 }
57 }
58 }
Listing 9: ProductController Class

1 % Main Spring Boot Application Class


2 import org.springframework.boot.SpringApplication;
3 import org.springframework.boot.autoconfigure.SpringBootApplication;
4

5 @SpringBootApplication % Combines @Configuration, @EnableAutoConfiguration,


@ComponentScan
6 public class RestApiApplication {
7 public static void main(String[] args) {
8 SpringApplication.run(RestApiApplication.class, args);
9 }
10 }
Listing 10: Main Spring Boot Application Class

Explanation of the RESTful API Example:


• Product.java: A simple POJO (Plain Old Java Object) representing the data
structure for a product. It has id, name, and price fields, along with constructors,
getters, and setters.
• ProductService.java: This class acts as the business logic layer. It’s annotated
with @Service to indicate it’s a service component in Spring. It contains meth-
ods for performing CRUD (Create, Read, Update, Delete) operations on Product
objects. In a real application, this layer would interact with a database.
• ProductController.java: This is the REST controller.
– @RestController: Marks this class as a REST controller, meaning its methods
return data directly (usually JSON/XML), not view names.
– @RequestMapping("/api/products"): All endpoints in this controller will
start with /api/products.
– @Autowired ProductService productService;: Spring automatically injects
an instance of ProductService into this controller.
– @GetMapping, @PostMapping, @PutMapping, @DeleteMapping: These anno-
tations map HTTP requests to specific methods.
∗ @GetMapping methods handle GET requests (retrieving data).
∗ @PostMapping methods handle POST requests (creating data). @RequestBody
is used to convert the incoming JSON request body into a Product object.
∗ @PutMapping methods handle PUT requests (updating data). @PathVariable
extracts the ID from the URL.

40 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

∗ @DeleteMapping methods handle DELETE requests (deleting data).


– ResponseEntity<T>: Used to return a full HTTP response, including the status
code (e.g., HttpStatus.OK, HttpStatus.CREATED, HttpStatus.NOTF OU N D,

• RestApiApplication.java: This is the main Spring Boot application class.

– @SpringBootApplication: This is a convenience annotation that combines:


∗ @Configuration: Tags the class as a source of bean definitions.
∗ @EnableAutoConfiguration: Tells Spring Boot to start adding beans
based on classpath settings, other beans, and various property settings.
∗ @ComponentScan: Tells Spring to look for other components, configura-
tions, and services in the com.example.restapi package (or current pack-
age and sub-packages), allowing it to find and register ProductService
and ProductController.
– SpringApplication.run(RestApiApplication.class, args);: This static
method runs the Spring Boot application. It sets up the ApplicationContext,
performs auto-configuration, and starts the embedded Tomcat server.

To run this example:

1. You would need a Java Development Kit (JDK) 17+ and Maven or Gradle.

2. Create a Spring Boot project (e.g., using Spring Initializr: start.spring.io with
"Spring Web" dependency).

3. Copy these Java files into the appropriate src/main/java package.

4. Run the RestApiApplication’s main method.

5. The application will start on http://localhost:8080 (by default). You can then
use tools like Postman, Insomnia, or curl to test the API endpoints.

Example API Calls (using curl):

• Get all products: curl http://localhost:8080/api/products (Expected JSON


array of products)

• Get product by ID (e.g., ID 1): curl http://localhost:8080/api/products/1


(Expected JSON for Laptop)

• Create a new product: curl -X POST -H "Content-Type: application/json"


-d ’"name":"Monitor", "price":250.00’ http://localhost:8080/api/products
(Expected JSON of the newly created product with an assigned ID)

• Update an existing product (e.g., ID 1): curl -X PUT -H "Content-Type:


application/json" -d ’"name":"Gaming Laptop", "price":1500.00’ http://localhost:8
(Expected JSON of the updated product)

• Delete a product (e.g., ID 2): curl -X DELETE http://localhost:8080/api/products/2


(Expected HTTP Status 204 No Content)

41 PYQs!
Download our app for FREE Notes and
Apna Engineering Wallah

Apna Engineering Wallah

Download our app for FREE Notes and PYQs!


Visit our website: www.apnaengineeringwallah.com
Or scan the QR code to download:

Thank you for choosing Apna Engineering Wallah!

42 PYQs!
Download our app for FREE Notes and

You might also like