Group B — Descriptive Questions (5 marks)
Q1. Discuss the four fundamental pillars of Object-Oriented Programming (OOP) with real-world examples.
Object-Oriented Programming is a paradigm based on the concept of 'objects', which can contain data (attributes/fields) and code (methods). The four fundamental pillars that define OOP are:
- Encapsulation: This is the mechanism of wrapping data (variables) and code acting on the data (methods) together as a single unit (a class). In encapsulation, the variables of a class are hidden from other classes and can only be accessed through the methods of their current class. Therefore, it is also known as data hiding.
Real-World Example: A medicinal capsule. The medicine (data/variables) is safely hidden inside the capsule cover (methods). Or a Bank Account where thebalanceis private and only updated viadeposit()orwithdraw()methods. - Inheritance: It is a mechanism wherein a new class is derived from an existing class. The new class (subclass) inherits the attributes and methods of the existing class (superclass). This promotes code reusability and establishes an IS-A relationship.
Real-World Example: AVehicleclass might have properties likewheelsand methods likestartEngine(). ACarclass inherits fromVehicle, automatically gaining those features, but can add its own specific features likeairConditioning(). - Polymorphism: Poly means "many" and morphism means "forms". Polymorphism allows us to perform a single action in different ways. In Java, this occurs via method overloading (compile-time) and method overriding (run-time).
Real-World Example: A person can have different roles at the same time. A man can be a father, a husband, and an employee. Thus, the same person behaves differently depending on the situation (context). In code, adraw()method will behave differently for aCircleobject vs aSquareobject. - Abstraction: It is the property of hiding complex implementation details and showing only the essential features of the object. It helps to reduce programming complexity and effort.
Real-World Example: Driving a car. You know that pressing the accelerator increases speed and pressing the brake stops it. You don't need to know the complex internal combustion engine mechanics to drive the car. In Java, this is achieved using abstract classes and interfaces.
Q2. Compare and contrast Procedural Programming with Object-Oriented Programming (OOP).
Programming paradigms dictate how we approach problem-solving in code. Procedural Oriented Programming (POP) and Object-Oriented Programming (OOP) are the two most prominent paradigms.
| Feature | Procedural Programming (POP) | Object-Oriented Programming (OOP) |
|---|---|---|
| Core Concept | Focuses on functions and procedures. The program is divided into smaller parts called functions. | Focuses on data and objects. The program is divided into objects that interact with each other. |
| Approach | Follows a Top-Down approach in program design. | Follows a Bottom-Up approach in program design. |
| Data Security | Poor security. Data moves freely around the system from function to function. Most data is global. | High security. Data is hidden (encapsulated) and cannot be accessed by external functions without permission. |
| Real-world modeling | Difficult to map to real-world scenarios. | Extremely easy to map real-world entities into software objects. |
| Reusability | No direct mechanism for reusing code, though functions help to some extent. | Inheritance provides a powerful mechanism to reuse existing code. |
| Modifiability | Adding new data or functions is difficult as it might affect the entire program. | Highly extensible. Adding new classes or modifying existing ones is easier without breaking the system. |
| Examples | C, Pascal, FORTRAN, BASIC. | Java, C++, Python, C#. |
Conclusion: While POP is suitable for small, simple scripts where execution speed is critical, OOP is indispensable for building large, complex, maintainable, and scalable enterprise applications.
Q3. What is Constructor Chaining in Java? Explain with a relevant code snippet.
Constructor chaining is the process of calling one constructor from another constructor within the same class (or from a child class to a parent class) during the object creation process. This technique is primarily used to prevent code duplication and to ensure that multiple constructors share common initialization logic.
Types of Constructor Chaining:
- Within the same class: Achieved using the
this()keyword. It must always be the first statement inside the constructor. - From a child class to a parent class: Achieved using the
super()keyword. It also must be the first statement. If not explicitly written, the Java compiler implicitly inserts a defaultsuper()call.
Code Example:
class Employee {
String name;
int id;
String department;
// Constructor 1 (Takes only name)
public Employee(String name) {
// Calls Constructor 2
this(name, 0);
System.out.println("Inside Constructor 1");
}
// Constructor 2 (Takes name and id)
public Employee(String name, int id) {
// Calls Constructor 3 (The main initialization constructor)
this(name, id, "Unassigned");
System.out.println("Inside Constructor 2");
}
// Constructor 3 (Takes all parameters)
public Employee(String name, int id, String department) {
// This constructor actually sets the values
this.name = name;
this.id = id;
this.department = department;
System.out.println("Inside Constructor 3");
}
}
public class Main {
public static void main(String[] args) {
// When we call the single-parameter constructor, it triggers the chain.
Employee emp = new Employee("Alice");
}
}
Output of the above code:
Inside Constructor 3
Inside Constructor 2
Inside Constructor 1
As seen in the output, the this() call immediately pauses the execution of the current constructor and jumps to the target constructor, meaning the most parameterized constructor finishes executing first.
Q4. Explain Dynamic Method Dispatch (Runtime Polymorphism) in Java with an example.
Dynamic Method Dispatch is a mechanism by which a call to an overridden method is resolved at runtime, rather than at compile-time. This is how Java implements runtime polymorphism. When an overridden method is called through a superclass reference, Java determines which version of that method to execute based on the actual type of the object being referred to at the time the call occurs, not the type of the reference variable.
Key Rules:
- Upcasting is required: A superclass reference variable must point to a subclass object.
- The method must be overridden in the subclass.
Code Example:
class Animal {
public void sound() {
System.out.println("Animal makes a generic sound");
}
}
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Dog barks: Woof Woof!");
}
}
class Cat extends Animal {
@Override
public void sound() {
System.out.println("Cat meows: Meow!");
}
}
public class RuntimePolymorphismDemo {
public static void main(String[] args) {
// Superclass reference points to Animal object
Animal myAnimal = new Animal();
myAnimal.sound(); // Output: Animal makes a generic sound
// Superclass reference points to Dog object (Upcasting)
Animal myDog = new Dog();
// At runtime, JVM checks the actual object type (Dog)
myDog.sound(); // Output: Dog barks: Woof Woof!
// Superclass reference points to Cat object (Upcasting)
Animal myCat = new Cat();
myCat.sound(); // Output: Cat meows: Meow!
}
}
Advantages: It allows Java to support overriding, which is central to OOP. It enables you to write robust, extensible code because you can write methods that accept a superclass parameter and they will automatically behave correctly for any subclass passed to them.
Q5. Differentiate between Abstract Class and Interface in Java.
Both abstract classes and interfaces are used to achieve abstraction in Java, but they have distinct differences in their design and usage.
| Feature | Abstract Class | Interface |
|---|---|---|
| Keyword & Definition | Declared using the abstract keyword. Can have both abstract (no body) and concrete (with body) methods. | Declared using the interface keyword. (Before Java 8) Could only have abstract methods. Now can have default and static methods. |
| Variables | Can have final, non-final, static, and non-static variables. | Variables are implicitly public static final (constants). |
| Inheritance | A class can extend only ONE abstract class (Single Inheritance). | A class can implement MULTIPLE interfaces (Multiple Inheritance). |
| Constructors | Can have a constructor (used during subclass object creation). | Cannot have a constructor. |
| Access Modifiers | Methods and variables can have any access modifier (private, protected, etc.). | Methods are implicitly public abstract. |
Code Example:
// Interface
interface Drawable {
void draw(); // implicitly public and abstract
}
// Abstract Class
abstract class Shape {
String color;
// Constructor in abstract class
public Shape(String color) { this.color = color; }
// Concrete method
public void displayColor() { System.out.println("Color: " + color); }
// Abstract method
abstract double calculateArea();
}
// Concrete Class implementing Interface AND extending Abstract Class
class Circle extends Shape implements Drawable {
double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public void draw() { System.out.println("Drawing a Circle."); }
@Override
double calculateArea() { return Math.PI * radius * radius; }
}
Q6. Explain Access Specifiers (Modifiers) in Java with examples.
Access specifiers determine the scope or visibility of classes, variables, methods, and constructors in Java. They are crucial for implementing Encapsulation (data hiding). Java provides four access specifiers:
- 1. private: The access level is restricted strictly within the class. It cannot be accessed from outside the class, not even by subclasses. It provides the highest level of security.
- 2. default (no keyword): If you don't explicitly specify an access modifier, it is considered 'default'. The access level is restricted within the same package. It cannot be accessed from outside the package.
- 3. protected: The access level is within the same package, and outside the package only through child classes (inheritance). If a class outside the package does not extend it, it cannot access protected members.
- 4. public: The access level is everywhere. It can be accessed from within the class, within the package, outside the package by subclasses, and outside the package by non-subclasses.
Code Summary:
package com.example.pack1;
public class Parent {
private int privateVar = 1; // Visible ONLY inside Parent
int defaultVar = 2; // Visible ONLY inside pack1
protected int protectedVar = 3; // Visible inside pack1 AND subclasses everywhere
public int publicVar = 4; // Visible everywhere
public void show() {
System.out.println(privateVar); // OK
}
}
// Different package scenario:
package com.example.pack2;
import com.example.pack1.Parent;
class Child extends Parent {
public void testAccess() {
// System.out.println(privateVar); // Error: private
// System.out.println(defaultVar); // Error: different package
System.out.println(protectedVar); // OK: accessed via inheritance
System.out.println(publicVar); // OK: public
}
}
Q7. Describe the role of the 'static' keyword in Java.
The static keyword in Java is used primarily for memory management. It indicates that a particular member belongs to the class itself, rather than to instances (objects) of the class. It can be applied to variables, methods, blocks, and nested classes.
- 1. Static Variables: A static variable is shared among all objects of that class. Memory is allocated only once in the class area at the time of class loading. It is useful for representing common properties (e.g., company name for all employees).
- 2. Static Methods: A static method belongs to the class and can be invoked without creating an object of the class (using
ClassName.methodName()). A crucial rule is that static methods can only directly access other static data and static methods; they cannot access non-static (instance) data directly or usethisorsuperkeywords. - 3. Static Blocks: Used to initialize static variables. It is executed automatically exactly once when the class is loaded into memory, even before the
main()method executes.
Code Example:
class Student {
int rollNo; // Instance variable
String name; // Instance variable
static String college = "MIT"; // Static variable shared by all
// Static Block
static {
System.out.println("Static Block Executed");
// college = "Stanford"; // Can modify static variables here
}
public Student(int r, String n) {
rollNo = r;
name = n;
}
// Static Method
public static void changeCollege(String newCollege) {
college = newCollege;
// name = "John"; // ERROR: Cannot make a static reference to non-static field
}
public void display() {
System.out.println(rollNo + " " + name + " " + college);
}
}
public class Main {
public static void main(String[] args) {
Student.changeCollege("Harvard"); // Calling static method without object
Student s1 = new Student(101, "Alice");
Student s2 = new Student(102, "Bob");
s1.display(); // 101 Alice Harvard
s2.display(); // 102 Bob Harvard
}
}
Q8. What is the significance of the 'final' keyword in Java?
The final keyword in Java is a non-access modifier used to restrict the user. It can be applied in three different contexts: variables, methods, and classes. Once applied, it signifies that the entity is complete and cannot be altered.
- 1. Final Variable (Constant): When a variable is declared as final, its value cannot be changed once initialized. It effectively becomes a constant. A blank final variable (uninitialized at declaration) can only be initialized inside a constructor.
- 2. Final Method (Prevents Overriding): When a method is declared as final, it cannot be overridden by any subclasses. This is useful for locking down the implementation of a critical method to prevent unexpected behavior in child classes.
- 3. Final Class (Prevents Inheritance): When a class is declared as final, it cannot be extended (inherited) by any other class. For security and immutability reasons, many core Java classes like
String,Integer, andMathare declared as final.
Code Example:
// 1. Final Class
final class Vehicle {
public void drive() { System.out.println("Driving a vehicle"); }
}
// class Car extends Vehicle { } // ERROR: Cannot inherit from final Vehicle
class Parent {
// 2. Final Method
public final void show() {
System.out.println("This is a final method");
}
}
class Child extends Parent {
// public void show() { } // ERROR: Cannot override final method
public void testVariable() {
// 3. Final Variable
final int MAX_AGE = 100;
// MAX_AGE = 101; // ERROR: Cannot assign a value to final variable
System.out.println("Max age is: " + MAX_AGE);
}
}
Q9. Describe Exception Handling in Java using try, catch, and finally blocks.
An exception is an unwanted or unexpected event occurring during the execution of a program (at runtime) that disrupts the normal flow of instructions. Java provides a robust mechanism to handle these exceptions so that the program can terminate gracefully or recover.
The Core Keywords:
- try: The
tryblock encloses the code that might throw an exception. It must be followed by either acatchor afinallyblock. - catch: The
catchblock is used to handle the exception thrown by the precedingtryblock. You can have multiple catch blocks to handle different types of exceptions specifically. - finally: The
finallyblock contains crucial code that must execute whether an exception occurs or not, and whether it is caught or not. It is primarily used for cleaning up resources (closing files, database connections, network sockets).
Code Example:
import java.util.Scanner;
public class ExceptionDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("Enter a number to divide 100 by: ");
int divisor = scanner.nextInt();
// Risky code that might throw an ArithmeticException
int result = 100 / divisor;
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
// Handled if divisor is 0
System.out.println("Error: Cannot divide by zero!");
} catch (Exception e) {
// Generic catch-all for any other exception (e.g., InputMismatchException)
System.out.println("An unexpected error occurred: " + e.getMessage());
} finally {
// This block ALWAYS executes
System.out.println("Executing finally block: Closing resources.");
scanner.close();
}
System.out.println("Program continues normally after try-catch-finally.");
}
}
Q10. Differentiate between Checked and Unchecked Exceptions in Java.
In Java, the java.lang.Throwable class is the root class of all exceptions and errors. The exception hierarchy is divided into two primary categories: Checked and Unchecked exceptions.
| Feature | Checked Exceptions | Unchecked Exceptions |
|---|---|---|
| Verification Time | Checked at Compile-time by the compiler. | Occur at Runtime. The compiler does not check them. |
| Handling Requirement | Must be explicitly handled using a try-catch block or declared in the method signature using the throws keyword. Otherwise, the code will not compile. | No mandatory requirement to handle or declare them, though it is good programming practice to do so if they are predictable. |
| Hierarchy | Classes that extend Throwable or Exception (except RuntimeException and its subclasses). | Classes that extend RuntimeException (and Error, though errors are typically unrecoverable system failures). |
| Typical Use Case | Represents conditions outside the immediate control of the program (e.g., a missing file, a broken network connection). The programmer is forced to plan for these. | Represents programming logic errors (e.g., dividing by zero, accessing a null reference, going out of array bounds). These should be fixed by writing better code. |
| Examples | IOException, SQLException, ClassNotFoundException, InterruptedException. | NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException, IllegalArgumentException. |
Custom Exception:
Thread Life Cycle: New -> Runnable -> Running -> Blocked/Waiting -> Terminated.
Thread vs Runnable: Implementing Runnable is better as Java doesn't support multiple inheritance, so you can extend another class if needed.
Thread Synchronization: Prevents thread interference using synchronized keyword on methods or blocks.
Inter-Thread Communication: Threads communicate via wait(), notify(), and notifyAll() inside synchronized context.
Deadlock: Two or more threads are blocked forever, waiting for each other. Avoid by acquiring locks in a consistent order.
String Immutability: Strings cannot be changed once created for security, thread-safety, and performance (String Pool).
Autoboxing and Unboxing: Automatic conversion between primitive types and their corresponding wrapper classes.
ArrayList vs Vector: ArrayList is unsynchronized and fast. Vector is synchronized and slow.
HashMap Working: Uses hashing. Stores key-value pairs in buckets. Handles collisions using Linked Lists or Trees.
Java Generics and Wildcards: Generics provide compile-time type safety. Wildcards (?) allow flexibility in generic types.
Byte vs Character Streams: Byte streams (InputStream/OutputStream) read/write 1 byte at a time. Character streams (Reader/Writer) read/write 2 bytes (Unicode).
Serialization: Process of converting an object into a byte stream. Deserialization is the reverse.
Lambda Expressions: Anonymous functions in Java 8 that simplify code by treating functionality as a method argument.
Functional Interfaces: Interfaces with exactly one abstract method. Annotated with @FunctionalInterface.
Stream API:
Singleton Pattern: Ensures only one instance exists. Lazy initializes on first use; Eager initializes at class loading.
Factory Pattern: Creates objects without exposing the instantiation logic to the client.
SOLID Principles: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion.
UML Relationships: Association (uses-a), Aggregation (has-a, weak), Composition (part-of, strong).
Use Case Diagram: Visual representation of system interactions by actors and use cases.
Friend Functions: Functions in C++ that can access private/protected members of a class.
Operator Overloading: Giving special meaning to an existing operator in C++ for user-defined types.
Deep vs Shallow Copy: Shallow copy copies references. Deep copy creates new copies of the referenced objects.
Diamond Problem: Ambiguity in multiple inheritance. Solved in C++ using virtual inheritance.
Generic Stack:
Reflection API: Allows inspection and modification of classes, interfaces, fields, and methods at runtime.
Try-with-Resources: Automatically closes resources like files or sockets at the end of the statement.
Word Frequency:
MVC Architecture: Separates application into Model (data), View (UI), and Controller (logic).