1. Program Demonstrating try–catch–finally, Multiple Catch Blocks, and throw/throws

PROGRAM:

import java.io.*;

class ExceptionDemo

{

    // Method using throws keyword

    static void checkNumber(int num) throws IOException

    {

        if (num < 0)

        {

            throw new IOException("Number is negative");

        }

        else

        {

            System.out.println("Number is positive");

        }

    }

    public static void main(String[] args)

    {

        try

        {

            int a = 10;

            int b = 0;

            // Arithmetic Exception

            int result = a / b;

            System.out.println("Result: " + result);

            // Calling method that throws exception

            checkNumber(-5);

        }

        // Multiple Catch Blocks

        catch (ArithmeticException e)

        {

            System.out.println("ArithmeticException caught: Division by zero");

        }

        catch (IOException e)

        {

            System.out.println("IOException caught: " + e.getMessage());

        }

        catch (Exception e)

        {

            System.out.println("General Exception caught");

        }

        // Finally Block

        finally

        {

            System.out.println("Finally block always executes");

        }

        System.out.println("Program continues after exception handling");

    }

}


OUTPUT:

ArithmeticException caught: Division by zero

Finally block always executes

Program continues after exception handling


FLOW OF CONTROL EXPLANATION:
1. Execution of try Block:
The program enters the try block. The statement int result = a / b; attempts to divide 10 by 0, which causes an ArithmeticException.

2. Transfer of Control to Catch Block:
As soon as the exception occurs, the remaining statements inside the try block are skipped. The control immediately moves to the matching catch (ArithmeticException e) block.

3. Multiple Catch Mechanism:
Java checks catch blocks from top to bottom. Since the exception type matches ArithmeticException, that specific catch block executes. Other catch blocks (IOException, Exception) are ignored.

4. Execution of Finally Block:
After executing the catch block, the finally block runs. The finally block always executes whether an exception occurs or not. It is generally used for resource cleanup.

5. Execution After try–catch–finally:
After the finally block execution, the program continues normally with the next statement outside the try–catch structure.

6. Working of throw Keyword:
Inside the method checkNumber(int num), the throw statement is used to explicitly generate an exception if the number is negative.

7. Working of throws Keyword:
The method checkNumber() declares throws IOException, which indicates that the method may pass the exception to the calling method instead of handling it directly.

8. Control Flow in Case of checkNumber():
If division by zero did not occur, the method checkNumber(-5) would execute and explicitly throw an IOException. Then the corresponding catch(IOException e) block would handle it.

TECHNICAL POINTS:
  • try block contains code that may generate exceptions.
  • Multiple catch blocks handle different exception types separately.
  • finally block executes regardless of exception occurrence.
  • throw is used to explicitly create an exception.
  • throws is used in method declaration to propagate exception handling responsibility.
  • Exception handling prevents abnormal program termination and ensures smooth execution.
2. Java Packages – Detailed Explanation (16 Marks)

PACKAGE DECLARATION
A package in Java is a namespace that groups related classes and interfaces together to avoid name conflicts and provide better organization.

A package is declared using the package keyword as the first statement in a Java source file.

Syntax:

package packagename;

Example:

package mypack;

public class Test

{

    public void display()

    {

        System.out.println("Inside mypack");

    }

}

The package statement must be written at the top of the source file before any import statements.

DIRECTORY STRUCTURE OF PACKAGE
When a package is declared, Java creates a folder structure that matches the package name.

Example:

package mypack;

Directory structure will be:

project_folder

   └── mypack

         └── Test.java

For sub-packages:

package com.company.project;

Directory structure:

project_folder

   └── com

        └── company

              └── project

                    └── ClassName.java

The directory hierarchy must match the package name.

TYPES OF PACKAGES
1. Built-in Packages
Built-in packages are pre-defined packages provided by Java API.

Examples:
  • java.lang – Contains fundamental classes like String, Math, System.
  • java.util – Contains utility classes like Scanner, ArrayList.
  • java.io – Contains classes for input/output operations.
  • java.sql – Contains classes for database connectivity.
Example usage:

import java.util.Scanner;


2. User-defined Packages
User-defined packages are created by programmers to organize their own classes.

They improve modularity and reusability of code.

ACCESS CONTROL ACROSS PACKAGES
Modifier Same Class Same Package Subclass (Other Package) Other Package
private Yes No No No
default Yes Yes No No
protected Yes Yes Yes No
public Yes Yes Yes Yes

Example 1: Same Package Access
File: mypack/A.java

package mypack;

public class A

{

    protected int x = 10;

}

File: mypack/B.java

package mypack;

class B

{

    public static void main(String[] args)

    {

        A obj = new A();

        System.out.println(obj.x);

    }

}

Here, protected member is accessible within the same package.

Example 2: Different Package Access
File: pack1/A.java

package pack1;

public class A

{

    protected int x = 20;

}

File: pack2/B.java

package pack2;

import pack1.A;

class B extends A

{

    public static void main(String[] args)

    {

        B obj = new B();

        System.out.println(obj.x);

    }

}

Here, protected member is accessible in subclass even though it is in a different package.

CREATION OF USER-DEFINED PACKAGE
Step 1: Create Package Folder Automatically Using Command
Create a file named MyClass.java

package mypack;

public class MyClass

{

    public void show()

    {

        System.out.println("User Defined Package Example");

    }

}


COMPILATION COMMAND
To compile and automatically generate directory structure:

javac -d . MyClass.java

-d . tells the compiler to create the directory structure in the current folder.

After compilation:

mypack

   └── MyClass.class


USING USER-DEFINED PACKAGE
Create another file Test.java in same root folder:

import mypack.MyClass;

class Test

{

    public static void main(String[] args)

    {

        MyClass obj = new MyClass();

        obj.show();

    }

}


COMPILATION AND EXECUTION
Compile:

javac Test.java

Run:

java Test


OUTPUT

User Defined Package Example


ADVANTAGES OF PACKAGES
  1. Avoids class name conflicts.
  2. Provides access protection.
  3. Improves code organization.
  4. Supports modular programming.
  5. Makes large projects manageable.

IMPORTANT TECHNICAL POINTS
  • Package statement must be first line in source file.
  • Directory structure must match package name.
  • Built-in packages are part of Java API.
  • User-defined packages are created using package keyword.
  • import statement is used to access classes of another package.
  • -d option in javac creates proper directory structure.
  • Access modifiers control visibility across packages.
  • Protected members are accessible in subclasses of different packages.
3. Java Program Demonstrating Synchronization, Thread Safety and Deadlock (16 Marks)

PROGRAM 1: Synchronization and Thread Safety

class Counter

{

    int count = 0;

    // Synchronized method

    synchronized void increment()

    {

        count++;

    }

}

class MyThread extends Thread

{

    Counter c;

    MyThread(Counter c)

    {

        this.c = c;

    }

    public void run()

    {

        for(int i = 0; i < 1000; i++)

        {

            c.increment();

        }

    }

}

public class SyncDemo

{

    public static void main(String[] args) throws InterruptedException

    {

        Counter obj = new Counter();

        MyThread t1 = new MyThread(obj);

        MyThread t2 = new MyThread(obj);

        t1.start();

        t2.start();

        t1.join();

        t2.join();

        System.out.println("Final Count: " + obj.count);

    }

}


OUTPUT

Final Count: 2000


EXPLANATION – SYNCHRONIZATION AND THREAD SAFETY
1. Race Condition

A race condition occurs when multiple threads access and modify shared data simultaneously, leading to inconsistent results.

If the increment() method is not synchronized, both threads may update count at the same time, producing incorrect output (less than 2000).
2. Role of synchronized Keyword

The synchronized keyword allows only one thread to access the method at a time.

It locks the object (monitor lock).

Other threads must wait until the lock is released.

Thus, synchronization ensures mutual exclusion, preventing race conditions and making the program thread-safe.
3. Thread Safety

A program is thread-safe when shared resources are protected from concurrent modification. Here, synchronization ensures correct incrementing of the shared variable.

PROGRAM 2: Demonstration of Deadlock

class Resource

{

}

public class DeadlockDemo

{

    public static void main(String[] args)

    {

        final Resource r1 = new Resource();

        final Resource r2 = new Resource();

        Thread t1 = new Thread()

        {

            public void run()

            {

                synchronized(r1)

                {

                    System.out.println("Thread 1 locked Resource 1");

                    try

                    {

                        Thread.sleep(100);

                    }

                    catch (Exception e)

                    {

                    }

                    synchronized(r2)

                    {

                        System.out.println("Thread 1 locked Resource 2");

                    }

                }

            }

        };

        Thread t2 = new Thread()

        {

            public void run()

            {

                synchronized(r2)

                {

                    System.out.println("Thread 2 locked Resource 2");

                    try

                    {

                        Thread.sleep(100);

                    }

                    catch (Exception e)

                    {

                    }

                    synchronized(r1)

                    {

                        System.out.println("Thread 2 locked Resource 1");

                    }

                }

            }

        };

        t1.start();

        t2.start();

    }

}


DEADLOCK EXPLANATION
1. What is Deadlock?

A deadlock occurs when two or more threads wait indefinitely for each other to release resources.

2. Deadlock Situation in Program

Thread 1 locks r1 and waits for r2.

Thread 2 locks r2 and waits for r1.

Both threads wait forever → Program hangs.
CONDITIONS FOR DEADLOCK (Necessary Conditions)
  1. Mutual Exclusion
  2. Hold and Wait
  3. No Preemption
  4. Circular Wait
If all four occur simultaneously, deadlock happens.
HOW SYNCHRONIZATION PREVENTS RACE CONDITIONS
  • It ensures only one thread accesses critical section.
  • It provides atomic execution.
  • It prevents inconsistent data updates.
  • It ensures predictable output.

HOW TO AVOID DEADLOCK
1. Lock Ordering

Always acquire locks in the same order.

Example fix:

Both threads should lock r1 first, then r2.
2. Using tryLock() (ReentrantLock)

Allows timeout-based locking.
3. Avoid Nested Locks

Reduce dependency between shared resources.
4. Deadlock Detection Algorithms

Used in advanced systems.
TECHNICAL SUMMARY POINTS
  • Synchronization ensures mutual exclusion.
  • It prevents race conditions.
  • Thread safety means safe concurrent access.
  • Deadlock occurs due to circular waiting.
  • Deadlock can be prevented using proper lock ordering.
  • The synchronized keyword locks object monitor.
  • join() ensures thread completion before printing result.
  • sleep() is used to simulate delay and expose deadlock.
4. Detailed Note on Input/Output Streams Hierarchy in Java (16 Marks)

I/O STREAMS IN JAVA
Java provides the java.io package to perform input and output operations. A stream is a sequence of data. Java uses streams to read and write data from various sources such as files, keyboard, memory, and network.

There are two main categories of streams:
  1. Byte Streams
  2. Character Streams

I/O STREAM HIERARCHY
1. Byte Stream Hierarchy
Byte streams handle raw binary data (8-bit).

InputStream (abstract)

                        |

        -----------------------------------

        |                |                |

 FileInputStream  BufferedInputStream  ObjectInputStream

                 OutputStream (abstract)

                        |

        -----------------------------------

        |                |                |

 FileOutputStream BufferedOutputStream ObjectOutputStream

InputStream and OutputStream are abstract base classes.

Used for images, audio, video, and binary files.

2. Character Stream Hierarchy
Character streams handle text data (16-bit Unicode).

Reader (abstract)

                        |

        -----------------------------------

        |                |

   FileReader      BufferedReader

                    Writer (abstract)

                        |

        -----------------------------------

        |                |

   FileWriter      BufferedWriter

Reader and Writer are abstract classes.

Used for text files.

DIFFERENCE BETWEEN BYTE AND CHARACTER STREAMS
Byte Streams Character Streams
Handle binary data Handle text data
Use InputStream/OutputStream Use Reader/Writer
Example: FileInputStream Example: FileReader

PROGRAM 1: File Streams Example
This program demonstrates reading and writing using file streams.

import java.io.*;

class FileStreamDemo

{

    public static void main(String[] args) throws IOException

    {

        FileOutputStream fos = new FileOutputStream("data.txt");

        String str = "Hello Java IO";

        fos.write(str.getBytes());

        fos.close();

        FileInputStream fis = new FileInputStream("data.txt");

        int i;

        while((i = fis.read()) != -1)

        {

            System.out.print((char)i);

        }

        fis.close();

    }

}

Explanation:

FileOutputStream writes bytes into file.

write() method writes data.

FileInputStream reads byte-by-byte.

read() returns -1 at end of file.

PROGRAM 2: Buffered Streams Example
Buffered streams improve performance by reducing disk access.

import java.io.*;

class BufferedDemo

{

    public static void main(String[] args) throws IOException

    {

        FileWriter fw = new FileWriter("buffer.txt");

        BufferedWriter bw = new BufferedWriter(fw);

        bw.write("Buffered Stream Example");

        bw.newLine();

        bw.write("Improves Performance");

        bw.close();

        FileReader fr = new FileReader("buffer.txt");

        BufferedReader br = new BufferedReader(fr);

        String line;

        while((line = br.readLine()) != null)

        {

            System.out.println(line);

        }

        br.close();

    }

}

Explanation:

BufferedWriter buffers data before writing to file.

BufferedReader reads entire line using readLine().

Reduces number of I/O operations.

Improves speed and efficiency.

PROGRAM 3: Object Streams Example (Serialization)
Object streams are used to store objects into files.

import java.io.*;

class Student implements Serializable

{

    int id;

    String name;

    Student(int id, String name)

    {

        this.id = id;

        this.name = name;

    }

}

class ObjectStreamDemo

{

    public static void main(String[] args) throws Exception

    {

        Student s1 = new Student(1, "Varshan");

        FileOutputStream fos = new FileOutputStream("student.txt");

        ObjectOutputStream oos = new ObjectOutputStream(fos);

        oos.writeObject(s1);

        oos.close();

        FileInputStream fis = new FileInputStream("student.txt");

        ObjectInputStream ois = new ObjectInputStream(fis);

        Student s2 = (Student) ois.readObject();

        ois.close();

        System.out.println(s2.id + " " + s2.name);

    }

}


Explanation of Object Streams
Serializable interface enables object serialization.

ObjectOutputStream converts object into byte stream.

ObjectInputStream reconstructs object from file.

Used in file storage, networking, and distributed systems.

COMBINED CONCEPT UNDERSTANDING
Stream Type Purpose Example
File Stream Direct file reading/writing FileInputStream
Buffered Stream Improves performance BufferedReader
Object Stream Stores objects ObjectOutputStream

ADVANTAGES OF USING BUFFERED AND OBJECT STREAMS
  1. Buffered streams reduce disk access.
  2. Improves program performance.
  3. Object streams allow complete object storage.
  4. Supports persistent data storage.
  5. Suitable for large applications.

IMPORTANT TECHNICAL POINTS
  • InputStream and OutputStream are base classes for byte streams.
  • Reader and Writer are base classes for character streams.
  • Buffered streams wrap file streams.
  • Object streams are built on file streams.
  • Serialization requires implementing Serializable.
  • close() method releases system resources.
  • IOException must be handled or declared.
5. Exception Hierarchy in Java with Checked and Unchecked Exceptions (16 Marks)

EXCEPTION HIERARCHY IN JAVA
All exceptions in Java are derived from the root class Throwable. The hierarchy is divided into two major branches:
  1. Error
  2. Exception

NEAT DIAGRAM OF EXCEPTION HIERARCHY

Object

                        |

                    Throwable

                   /          \

               Error        Exception

                               |

                 ----------------------------

                 |                          |

        Checked Exceptions        Unchecked Exceptions

                                        |

                                  RuntimeException


1. Throwable Class
Root class for all errors and exceptions.

Contains important methods like:
  • getMessage()
  • printStackTrace()
  • toString()

2. Error Class
Represents serious system-level problems.

Caused by JVM.

Cannot be handled normally.

Examples:
  • OutOfMemoryError
  • StackOverflowError
  • VirtualMachineError
Errors are not meant to be caught in normal applications.

3. Exception Class
Represents conditions that applications can handle.

Divided into:
  • Checked Exceptions
  • Unchecked Exceptions (Runtime Exceptions)

CHECKED EXCEPTIONS
Definition:

Checked exceptions are checked at compile time. The compiler ensures they are either handled using try–catch or declared using throws.

Examples:
  • IOException
  • FileNotFoundException
  • SQLException
  • ClassNotFoundException
Example Program (Checked Exception)

import java.io.*;

class CheckedDemo

{

    public static void main(String[] args)

    {

        try

        {

            FileReader fr = new FileReader("abc.txt");

        }

        catch(FileNotFoundException e)

        {

            System.out.println("File not found");

        }

    }

}

Explanation:

If file does not exist, FileNotFoundException occurs.

Compiler forces handling of this exception.

Use Cases of Checked Exceptions:
  1. File handling operations.
  2. Database connectivity.
  3. Network programming.
  4. Situations where recovery is possible.

UNCHECKED EXCEPTIONS (Runtime Exceptions)
Definition:

Unchecked exceptions occur at runtime and are not checked by compiler.

They are subclasses of RuntimeException.

Examples:
  • ArithmeticException
  • NullPointerException
  • ArrayIndexOutOfBoundsException
  • NumberFormatException
Example Program (Unchecked Exception)

class UncheckedDemo

{

    public static void main(String[] args)

    {

        int a = 10;

        int b = 0;

        int c = a / b;

        System.out.println(c);

    }

}

Explanation:

Division by zero causes ArithmeticException.

Compiler does not force handling.

Occurs during execution.

Use Cases of Unchecked Exceptions:
  1. Programming errors.
  2. Logical mistakes.
  3. Invalid operations.
  4. Improper data handling.

DIFFERENCE BETWEEN CHECKED AND UNCHECKED EXCEPTIONS
Checked Exception Unchecked Exception
Checked at compile time Occur at runtime
Must be handled Not mandatory to handle
Subclass of Exception Subclass of RuntimeException
Example: IOException Example: ArithmeticException

FLOW OF EXCEPTION HANDLING
  1. Exception occurs in try block.
  2. JVM creates exception object.
  3. Control transfers to matching catch block.
  4. If no catch matches → program terminates.
  5. Finally block executes (if present).

CUSTOM EXCEPTION EXAMPLE

class InvalidAgeException extends Exception

{

    InvalidAgeException(String msg)

    {

        super(msg);

    }

}

class Test

{

    static void validate(int age) throws InvalidAgeException

    {

        if(age < 18)

        {

            throw new InvalidAgeException("Not eligible");

        }

        else

        {

            System.out.println("Eligible");

        }

    }

    public static void main(String[] args)

    {

        try

        {

            validate(15);

        }

        catch(Exception e)

        {

            System.out.println(e.getMessage());

        }

    }

}


PRACTICAL SCENARIOS
  • Checked Exception: File reading failure in banking software.
  • Unchecked Exception: Dividing by zero due to programming mistake.
  • Error: JVM memory crash.

IMPORTANT TECHNICAL POINTS
  • Throwable is root of exception hierarchy.
  • Error represents serious system failures.
  • Exception represents application-level problems.
  • Checked exceptions must be handled or declared.
  • Unchecked exceptions are subclasses of RuntimeException.
  • Custom exceptions can be created by extending Exception.
  • Proper exception handling improves robustness.
6. Complete Example Demonstrating Packages with Subpackages (16 Marks)

DIRECTORY STRUCTURE
Suppose we create a main package college and a subpackage college.students

project_folder

   └── college

         ├── Faculty.java

         └── students

               ├── Student.java

               └── Helper.java


STEP 1: PUBLIC CLASS IN MAIN PACKAGE
File: Faculty.java

package college;

public class Faculty

{

    public void showFaculty()

    {

        System.out.println("Faculty Information");

    }

}

Faculty is a public class.

It can be accessed from other packages.

STEP 2: PUBLIC CLASS IN SUBPACKAGE
File: Student.java

package college.students;

public class Student

{

    public void showStudent()

    {

        System.out.println("Student Information");

    }

}

Student is public.

It can be imported and used outside the package.

STEP 3: PACKAGE-PRIVATE CLASS
File: Helper.java

package college.students;

class Helper

{

    void assist()

    {

        System.out.println("Helper Class");

    }

}

Helper has no access modifier.

It is package-private (default).

It can be accessed only inside college.students package.

STEP 4: MAIN CLASS USING IMPORT STATEMENT
File: TestImport.java

import college.Faculty;

import college.students.Student;

class TestImport

{

    public static void main(String[] args)

    {

        Faculty f = new Faculty();

        f.showFaculty();

        Student s = new Student();

        s.showStudent();

    }

}


OUTPUT

Faculty Information

Student Information


STEP 5: USING FULLY QUALIFIED NAME
Instead of using import statement, we can directly use the full package name.
File: TestFQN.java

class TestFQN

{

    public static void main(String[] args)

    {

        college.Faculty f = new college.Faculty();

        f.showFaculty();

        college.students.Student s =

                new college.students.Student();

        s.showStudent();

    }

}


OUTPUT

Faculty Information

Student Information


COMPILATION STEPS
Compile package classes first:

javac -d . Faculty.java

javac -d . Student.java

javac -d . Helper.java

Then compile main class:

javac TestImport.java

Run:

java TestImport


ACCESS CONTROL BEHAVIOR
  • Faculty → Accessible everywhere (public)
  • Student → Accessible everywhere (public)
  • Helper → Accessible only inside college.students
Helper cannot be accessed from TestImport class

Example (Not Allowed):

college.students.Helper h = new college.students.Helper();

This gives compile-time error because Helper is package-private.

USE OF IMPORT STATEMENT
The import statement allows usage of classes without writing full package path.

Example:

import college.Faculty;

Advantages:
  • Cleaner code
  • Improves readability
  • Avoids repetition

WHEN FULLY QUALIFIED NAMES ARE PREFERRED
Fully Qualified Names (FQN) are preferred in the following situations:
1. Name Conflict Situation
If two packages contain same class name:

Example:

java.util.Date

java.sql.Date

Using FQN avoids ambiguity:

java.util.Date d1;

java.sql.Date d2;


2. Avoiding Multiple Imports
In small programs where class is used only once:

new college.Faculty().showFaculty();

No need to import.

3. Improving Code Clarity in Large Systems
Explicit package naming avoids confusion in enterprise-level applications.

IMPORTANT TECHNICAL POINTS
  • Package declaration must be first line in file.
  • Subpackages are separate packages.
  • Public classes are accessible everywhere.
  • Default classes are accessible only inside same package.
  • Import statement improves readability.
  • Fully Qualified Name removes ambiguity.
  • Directory structure must match package name.
  • -d option in javac creates proper folder hierarchy.

This answer includes directory structure, programs, compilation commands, access control explanation, import usage, FQN usage, and exam-oriented explanation suitable for a 16-mark university answer.
7. Thread Life Cycle in Java (16 Marks)

THREAD LIFE CYCLE
A thread life cycle represents the various states a thread goes through during its execution. Java provides built-in support to manage thread states through the Thread class.

NEAT DIAGRAM OF THREAD LIFE CYCLE

+----------------+

|      NEW       |

+----------------+

        |

     start()

        |

        v

+----------------+

|   RUNNABLE     |

+----------------+

   |        |

scheduler   |

selects     |

   |        |

   v        |

+----------------+

|    RUNNING     |

+----------------+

   |   |     |

sleep() |  wait()/join()

   |   |     

   v   v

+----------------+

|  BLOCKED /     |

|  WAITING /     |

|  TIMED_WAITING |

+----------------+

        |

 notify()/time over

        |

        v

+----------------+

|   RUNNABLE     |

+----------------+

        |

   run() ends

        |

        v

+----------------+

|  TERMINATED    |

+----------------+


STATES OF THREAD
1. NEW

Thread is created but not started.
Created using new Thread().

2. RUNNABLE

After calling start().
Thread is ready for execution.
Waiting for CPU scheduling.

3. RUNNING

Thread is executing run() method.
CPU is allocated.

4. BLOCKED / WAITING / TIMED_WAITING

Thread temporarily inactive.
Caused by:

sleep()
wait()
join()
Waiting for lock

5. TERMINATED (Dead)

Thread finishes execution.
Cannot be restarted.

PROGRAM TO SHOW THREAD STATE TRANSITIONS

class LifeCycleDemo extends Thread

{

    public void run()

    {

        try

        {

            System.out.println("Thread State inside run(): " + Thread.currentThread().getState());

            Thread.sleep(1000);

        }

        catch (InterruptedException e)

        {

            System.out.println(e);

        }

    }

    public static void main(String[] args) throws Exception

    {

        LifeCycleDemo t = new LifeCycleDemo();

        // NEW State

        System.out.println("State after creation: " + t.getState());

        // Runnable State

        t.start();

        System.out.println("State after start(): " + t.getState());

        // Waiting for thread to finish

        t.join();

        // Terminated State

        System.out.println("State after completion: " + t.getState());

    }

}


OUTPUT (Sample)

State after creation: NEW

State after start(): RUNNABLE

Thread State inside run(): RUNNABLE

State after completion: TERMINATED


EXPLANATION OF STATE TRANSITIONS
  1. When thread object is created → NEW state.
  2. When start() method is called → moves to RUNNABLE state.
  3. When CPU scheduler selects thread → enters RUNNING state.
  4. When sleep(1000) is called → moves to TIMED_WAITING state.
  5. After sleep time completes → returns to RUNNABLE state.
  6. When run() method finishes → moves to TERMINATED state.
  7. join() ensures main thread waits until child thread completes.

IMPORTANT METHODS USED
  • start() → Moves thread to Runnable state.
  • run() → Contains execution code.
  • sleep() → Moves thread to Timed Waiting.
  • join() → Waits for thread to finish.
  • getState() → Returns current thread state.

PRACTICAL SIGNIFICANCE
  • Helps in multithreading control.
  • Avoids deadlock and starvation.
  • Used in concurrent applications.
  • Essential in real-time systems.

TECHNICAL SUMMARY
  • Thread life cycle contains five main states.
  • start() must be called to begin execution.
  • sleep() and wait() cause temporary blocking.
  • join() ensures synchronization between threads.
  • A terminated thread cannot be restarted.
  • JVM thread scheduler manages execution.

This structured explanation with neat diagram, program, output, state transition explanation, and technical details is suitable for a 16-mark university examination answer pattern.
8. Serialization and Deserialization in Java (16 Marks)

SERIALIZATION
Serialization is the process of converting a Java object into a byte stream so that it can be stored in a file or transmitted over a network.

It is implemented using the Serializable interface.

It is available in the java.io package.

The object’s state (data members) is saved.

DESERIALIZATION
Deserialization is the reverse process of serialization. It converts the byte stream back into a copy of the original Java object.

NEED FOR SERIALIZATION
  1. Saving object state permanently in a file.
  2. Sending objects through network.
  3. Used in distributed systems.
  4. Used in caching and session management.

STREAM CLASSES USED
  • ObjectOutputStream → Used for serialization.
  • ObjectInputStream → Used for deserialization.
  • FileOutputStream and FileInputStream → Underlying file streams.

PROGRAM: SERIALIZATION AND DESERIALIZATION

import java.io.*;

// Step 1: Implement Serializable interface

class Student implements Serializable

{

    int id;

    String name;

    Student(int id, String name)

    {

        this.id = id;

        this.name = name;

    }

}

public class SerializationDemo

{

    public static void main(String[] args) throws Exception

    {

        // Creating object

        Student s1 = new Student(101, "Varshan");

        // ---------------- SERIALIZATION ----------------

        FileOutputStream fos = new FileOutputStream("student.dat");

        ObjectOutputStream oos = new ObjectOutputStream(fos);

        oos.writeObject(s1);

        oos.close();

        fos.close();

        System.out.println("Object Serialized Successfully");

        // ---------------- DESERIALIZATION ----------------

        FileInputStream fis = new FileInputStream("student.dat");

        ObjectInputStream ois = new ObjectInputStream(fis);

        Student s2 = (Student) ois.readObject();

        ois.close();

        fis.close();

        System.out.println("Object Deserialized Successfully");

        System.out.println("ID: " + s2.id);

        System.out.println("Name: " + s2.name);

    }

}


OUTPUT

Object Serialized Successfully

Object Deserialized Successfully

ID: 101

Name: Varshan


EXPLANATION OF PROGRAM
Step 1: Implement Serializable

The class must implement Serializable interface. It is a marker interface (contains no methods).

Step 2: Serialization Process

Create FileOutputStream.

Wrap it with ObjectOutputStream.

Use writeObject() method to convert object into byte stream.

Close streams.

Step 3: Deserialization Process

Create FileInputStream.

Wrap it with ObjectInputStream.

Use readObject() method.

Typecast to original class type.

Close streams.


TRANSIENT KEYWORD
If a variable should not be serialized, declare it as transient.

Example:

transient int password;

Such variables are ignored during serialization.

serialVersionUID
It is a unique identifier for serialized class.

Example:

private static final long serialVersionUID = 1L;

Used to verify compatibility during deserialization.

ADVANTAGES OF SERIALIZATION
  1. Easy object persistence.
  2. Simplifies network communication.
  3. Supports distributed computing.
  4. Saves complete object state.

LIMITATIONS
  1. Not suitable for very large objects frequently.
  2. Security risks if untrusted data is deserialized.
  3. Performance overhead compared to text formats.

REAL-TIME USE CASES
  • Banking transaction storage.
  • Sending objects in client-server architecture.
  • Saving game state in gaming applications.
  • Session handling in web applications.

TECHNICAL SUMMARY
  • Serialization converts object → byte stream.
  • Deserialization converts byte stream → object.
  • Serializable is mandatory.
  • ObjectOutputStream and ObjectInputStream are used.
  • transient prevents serialization of variables.
  • Used in file storage and networking.