Programming in Java
Unit 12: I/O Fundamentals & Generics
From byte streams to type-safe collections โ master Java's I/O architecture, serialization, and generics to build robust, production-ready applications.
โฑ๏ธ 6 hrs theory + 5 hrs lab | ๐ฐ โน5Kโโน15K/month | ๐ 30 MCQs (Bloom's Mapped)
๐ผ Jobs this unlocks: Java Backend Developer (โน5โ10 LPA) | Software Engineer (โน6โ14 LPA) | File Automation Freelancer (โน5Kโ15K/mo)
Opening Hook โ Every Aadhaar Record Travels Through Java I/O
๐ข How 1.4 Billion Aadhaar Records Move Securely Across India
When you walk into an Aadhaar enrolment centre in Lucknow, the operator captures your fingerprints, iris scan, photo, and demographics. That citizen object โ a Java object containing your name, DOB, address, and biometric hashes โ must travel from a local machine in UP to UIDAI's central servers in Bengaluru.
How? Java Serialization. The citizen object is serialized (converted to a byte stream) using ObjectOutputStream, encrypted, written to a file, transmitted over the network, and deserialized at the UIDAI server using ObjectInputStream. The biometric data is marked transient โ it gets separate, more secure handling.
Every Aadhaar record โ all 1.4 billion of them โ has been read, written, serialized, and deserialized using Java I/O. Meanwhile, the APIs serving Aadhaar verification to banks and telecom companies use Generics to ensure type-safe data containers, so a Response<AadhaarData> can never accidentally hold a PANCard object.
What if YOU could build this? What if you could serialize any Java object to disk, read CSV files line-by-line at blazing speed, and build type-safe data structures that catch errors at compile time instead of runtime? That's exactly what this unit teaches you.
Learning Outcomes โ Bloom's Taxonomy Mapped (12 Outcomes)
| Bloom's Level | Learning Outcome |
|---|---|
| ๐ต Remember | List the core classes in Java's byte stream and character stream hierarchies |
| ๐ต Remember | Define serialization, deserialization, and the role of the transient keyword |
| ๐ต Understand | Explain how BufferedReader improves performance over raw FileReader using internal buffering |
| ๐ต Understand | Describe why generics provide compile-time type safety and eliminate the need for explicit casting |
| ๐ข Apply | Write a Java program that reads a CSV file using BufferedReader and writes parsed output using BufferedWriter |
| ๐ข Apply | Implement a generic class GenericStack<T> with push, pop, and peek operations |
| ๐ข Analyze | Compare Scanner vs BufferedReader for reading user input โ performance, parsing, and use cases |
| ๐ข Analyze | Differentiate between <? extends T> (upper bound) and <? super T> (lower bound) using the PECS rule |
| ๐ Evaluate | Assess when to use byte streams vs character streams for different file types (binary vs text) |
| ๐ Evaluate | Judge the trade-offs of making a field transient vs serializing sensitive data with encryption |
| ๐ Create | Design a GenericPair<K,V> class and use it in a real-world key-value store scenario |
| ๐ Create | Build a complete Student Record Serializer that writes/reads student objects to/from binary files |
Concept Explanation โ I/O Fundamentals & Generics
Part A โ Java I/O (Input / Output)
1. Byte Streams โ InputStream & OutputStream Hierarchy
Java I/O is built on the concept of streams โ an ordered sequence of data. Think of a stream like a water pipe: data flows through it in one direction, byte by byte (or character by character). Byte streams handle raw binary data โ images, audio, serialized objects, PDFs.
๐ Byte Stream Class Hierarchy
InputStream (abstract) โ reads bytes
FileInputStreamโ reads bytes from a fileBufferedInputStreamโ adds buffering for performanceObjectInputStreamโ deserializes objectsByteArrayInputStreamโ reads from byte array in memoryDataInputStreamโ reads primitive types (int, float, etc.)
OutputStream (abstract) โ writes bytes
FileOutputStreamโ writes bytes to a fileBufferedOutputStreamโ adds buffering for performanceObjectOutputStreamโ serializes objectsByteArrayOutputStreamโ writes to byte array in memoryDataOutputStreamโ writes primitive types
Java // Copy a file byte-by-byte using FileInputStream / FileOutputStream import java.io.*; public class ByteCopyDemo { public static void main(String[] args) { try ( FileInputStream fis = new FileInputStream("input.jpg"); FileOutputStream fos = new FileOutputStream("output.jpg") ) { int byteData; while ((byteData = fis.read()) != -1) { fos.write(byteData); } System.out.println("File copied successfully!"); } catch (IOException e) { e.printStackTrace(); } } }
close() when the block exits.
2. Character Streams โ Reader & Writer Hierarchy
Byte streams handle raw bytes. But text files use characters (Unicode). Reading a Hindi text file with FileInputStream can corrupt characters because it doesn't understand encoding. That's why Java provides character streams โ they handle encoding/decoding automatically.
๐ Character Stream Class Hierarchy
Reader (abstract) โ reads characters
FileReaderโ reads characters from a fileBufferedReaderโ reads lines efficiently with bufferingInputStreamReaderโ bridge from byte stream to character streamStringReaderโ reads from a String
Writer (abstract) โ writes characters
FileWriterโ writes characters to a fileBufferedWriterโ writes efficiently with bufferingOutputStreamWriterโ bridge from character stream to byte streamPrintWriterโ convenient print/println methods
Java // Reading and writing text files using FileReader / FileWriter import java.io.*; public class CharStreamDemo { public static void main(String[] args) { // Writing to a file try (FileWriter fw = new FileWriter("greeting.txt")) { fw.write("เคจเคฎเคธเฅเคคเฅ! Welcome to Java I/O.\n"); fw.write("This handles Unicode perfectly.\n"); System.out.println("File written successfully."); } catch (IOException e) { e.printStackTrace(); } // Reading from a file try (FileReader fr = new FileReader("greeting.txt")) { int ch; while ((ch = fr.read()) != -1) { System.out.print((char) ch); } } catch (IOException e) { e.printStackTrace(); } } }
3. Buffered Streams โ BufferedReader & BufferedWriter
Analogy: Imagine filling a water tank. You can carry water one glass at a time (unbuffered โ 1000 trips), or carry a full bucket each time (buffered โ 50 trips). Buffered streams read/write large chunks internally, dramatically reducing disk I/O calls.
BufferedReader wraps a FileReader and provides the essential readLine() method โ the most commonly used I/O method in real Java applications.
Java // Reading a file line-by-line with BufferedReader import java.io.*; public class BufferedDemo { public static void main(String[] args) { // Writing with BufferedWriter try (BufferedWriter bw = new BufferedWriter( new FileWriter("students.csv"))) { bw.write("Name,Roll,CGPA"); bw.newLine(); bw.write("Arjun Patel,101,8.5"); bw.newLine(); bw.write("Sneha Iyer,102,9.2"); bw.newLine(); bw.write("Rahul Verma,103,7.8"); bw.newLine(); } catch (IOException e) { e.printStackTrace(); } // Reading with BufferedReader try (BufferedReader br = new BufferedReader( new FileReader("students.csv"))) { String line; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } } }
The File Class โ Inspect Before You Read
The java.io.File class doesn't read or write โ it represents a file path and provides metadata operations:
Java import java.io.File; public class FileOps { public static void main(String[] args) { File f = new File("students.csv"); System.out.println("Exists: " + f.exists()); // true System.out.println("Name: " + f.getName()); // students.csv System.out.println("Size: " + f.length() + " bytes"); System.out.println("Readable: " + f.canRead()); // true System.out.println("Writable: " + f.canWrite()); // true System.out.println("Path: " + f.getAbsolutePath()); // Create a directory File dir = new File("backup"); if (dir.mkdir()) System.out.println("Directory created!"); // List files File cwd = new File("."); for (String name : cwd.list()) { System.out.println(" โ " + name); } } }
Scanner vs BufferedReader โ Which to Use?
| Feature | Scanner | BufferedReader |
|---|---|---|
| Package | java.util | java.io |
| Primary Use | Parsing tokens (int, double, etc.) | Reading lines of text fast |
| Speed | Slower (regex-based parsing) | Faster (buffered I/O) |
| Buffer Size | 1 KB default | 8 KB default |
| Thread-safe? | No | Yes (synchronized) |
| Key Method | nextInt(), nextLine() | readLine() |
| Best For | Console input, small files | Large files, production code |
Scanner for quick console input in coding contests and labs. Use BufferedReader for production file processing โ it's 3โ5ร faster for large files. Amazon and Flipkart interview questions often ask this comparison.
4. Serialization & Deserialization
Serialization = converting a Java object into a byte stream (so it can be saved to a file or sent over a network). Deserialization = reverse โ byte stream back to a Java object.
Serializable interface is like the packing label โ without it, India Post (JVM) refuses to accept the parcel.
Full Code: StudentSerializer
Java import java.io.*; // Step 1: Implement Serializable class Student implements Serializable { private static final long serialVersionUID = 1L; String name; int rollNo; double cgpa; transient String password; // Will NOT be serialized Student(String name, int rollNo, double cgpa, String password) { this.name = name; this.rollNo = rollNo; this.cgpa = cgpa; this.password = password; } public String toString() { return "Student{" + name + ", Roll:" + rollNo + ", CGPA:" + cgpa + ", Pass:" + password + "}"; } } public class StudentSerializer { public static void main(String[] args) { Student s1 = new Student("Arjun Patel", 101, 8.5, "secret123"); Student s2 = new Student("Sneha Iyer", 102, 9.2, "pass456"); // SERIALIZE โ write objects to file try (ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("students.ser"))) { oos.writeObject(s1); oos.writeObject(s2); System.out.println("โ Students serialized to students.ser"); } catch (IOException e) { e.printStackTrace(); } // DESERIALIZE โ read objects back try (ObjectInputStream ois = new ObjectInputStream( new FileInputStream("students.ser"))) { Student d1 = (Student) ois.readObject(); Student d2 = (Student) ois.readObject(); System.out.println("๐ Deserialized:"); System.out.println(d1); System.out.println(d2); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
password is null after deserialization! The transient keyword told Java to skip that field during serialization. This is how sensitive data (passwords, OTPs, session tokens) is protected. In Aadhaar's system, biometric hashes are marked transient โ they travel through a separate, encrypted channel.
serialVersionUID! Without it, Java auto-generates one based on class structure. If you add a field later, the UID changes, and deserialization of old files throws InvalidClassException. Set it explicitly: private static final long serialVersionUID = 1L;
Full Code: CSVFileReader
Java import java.io.*; public class CSVFileReader { public static void main(String[] args) { String csvFile = "students.csv"; String delimiter = ","; System.out.println("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ"); System.out.println("โ STUDENT RECORDS FROM CSV โ"); System.out.println("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ"); try (BufferedReader br = new BufferedReader( new FileReader(csvFile))) { String header = br.readLine(); // skip header System.out.printf("%-20s %-10s %-8s%n", "NAME", "ROLL", "CGPA"); System.out.println("โ".repeat(38)); String line; int count = 0; while ((line = br.readLine()) != null) { String[] parts = line.split(delimiter); if (parts.length == 3) { System.out.printf("%-20s %-10s %-8s%n", parts[0], parts[1], parts[2]); count++; } } System.out.println("โ".repeat(38)); System.out.println("Total records: " + count); } catch (IOException e) { System.err.println("Error reading file: " + e.getMessage()); } } }
Part B โ Java Generics
5. Why Generics? โ Type Safety at Compile Time
Before Java 5, collections stored Object types. You could put a String and an Integer in the same ArrayList, and it would only crash at runtime with a ClassCastException. Generics fix this โ they catch type errors at compile time.
Syringe<Covishield>), you can't accidentally fill it with insulin โ the label (compiler) prevents the mistake at preparation time, not after injection.
Java // WITHOUT generics (Java 4 style) โ DANGEROUS ArrayList list = new ArrayList(); list.add("Hello"); list.add(42); // No compile error! String s = (String) list.get(1); // ๐ฅ ClassCastException at RUNTIME! // WITH generics (Java 5+) โ SAFE ArrayList<String> safeList = new ArrayList<>(); safeList.add("Hello"); // safeList.add(42); โ Compile error! Caught early. String s2 = safeList.get(0); // No cast needed โ
Generic Class: Box<T>
Java class Box<T> { private T item; public void put(T item) { this.item = item; } public T get() { return item; } public String toString() { return "Box[" + item + "]"; } } // Usage: Box<String> nameBox = new Box<>(); nameBox.put("Arjun"); String name = nameBox.get(); // No cast needed! Box<Integer> ageBox = new Box<>(); ageBox.put(22); int age = ageBox.get(); // Auto-unboxing
Generic Method: <T> T getFirst(T[] arr)
Java public class GenericMethodDemo { // Generic method โ works with any array type public static <T> T getFirst(T[] arr) { if (arr == null || arr.length == 0) return null; return arr[0]; } public static void main(String[] args) { String[] names = {"Arjun", "Sneha", "Rahul"}; Integer[] marks = {85, 92, 78}; System.out.println(getFirst(names)); // Arjun System.out.println(getFirst(marks)); // 85 } }
Full Code: GenericStack<T>
Java import java.util.ArrayList; class GenericStack<T> { private ArrayList<T> stack = new ArrayList<>(); public void push(T item) { stack.add(item); System.out.println("Pushed: " + item); } public T pop() { if (stack.isEmpty()) throw new RuntimeException("Stack empty!"); return stack.remove(stack.size() - 1); } public T peek() { if (stack.isEmpty()) throw new RuntimeException("Stack empty!"); return stack.get(stack.size() - 1); } public boolean isEmpty() { return stack.isEmpty(); } public int size() { return stack.size(); } public String toString() { return "Stack" + stack.toString(); } } public class StackDemo { public static void main(String[] args) { GenericStack<String> bookStack = new GenericStack<>(); bookStack.push("Java: The Complete Reference"); bookStack.push("Head First Java"); bookStack.push("Effective Java"); System.out.println("Top: " + bookStack.peek()); System.out.println("Popped: " + bookStack.pop()); System.out.println(bookStack); GenericStack<Integer> markStack = new GenericStack<>(); markStack.push(85); markStack.push(92); System.out.println("Marks stack: " + markStack); } }
Full Code: GenericPair<K,V>
Java class GenericPair<K, V> { private K key; private V value; public GenericPair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } public String toString() { return "(" + key + " โ " + value + ")"; } } public class PairDemo { public static void main(String[] args) { // Roll number โ Student name GenericPair<Integer, String> p1 = new GenericPair<>(101, "Arjun Patel"); // Subject โ Marks GenericPair<String, Double> p2 = new GenericPair<>("Java", 92.5); // Aadhaar โ Phone GenericPair<String, String> p3 = new GenericPair<>("XXXX-XXXX-1234", "+91-98765-43210"); System.out.println(p1); System.out.println(p2); System.out.println(p3); } }
6. Bounded Types & Wildcards
Bounded Type: <T extends Comparable<T>>
Sometimes you need to restrict what types can be used. <T extends Comparable<T>> means "T must implement Comparable" โ this lets you compare and sort.
Full Code: BoundedSorter
Java import java.util.Arrays; class BoundedSorter { // Only types that implement Comparable can use this method public static <T extends Comparable<T>> T findMax(T[] arr) { T max = arr[0]; for (T item : arr) { if (item.compareTo(max) > 0) { max = item; } } return max; } public static <T extends Comparable<T>> void bubbleSort(T[] arr) { int n = arr.length; for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - i - 1; j++) { if (arr[j].compareTo(arr[j+1]) > 0) { T temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } public static void main(String[] args) { Integer[] marks = {78, 92, 85, 64, 97}; System.out.println("Max mark: " + findMax(marks)); bubbleSort(marks); System.out.println("Sorted: " + Arrays.toString(marks)); String[] cities = {"Mumbai", "Ahmedabad", "Chennai", "Delhi"}; System.out.println("Max city: " + findMax(cities)); bubbleSort(cities); System.out.println("Sorted: " + Arrays.toString(cities)); } }
Wildcards: ?, ? extends T, ? super T (PECS Rule)
๐ Wildcard Types Explained
<?> โ Unbounded Wildcard: Accepts any type. Use when you only need to read and don't care about the type. Example: List<?> can be List<String>, List<Integer>, etc.
<? extends T> โ Upper Bounded (Producer): Accepts T or any subclass of T. You can read from it (it "produces" T values). Example: List<? extends Number> accepts List<Integer>, List<Double>.
<? super T> โ Lower Bounded (Consumer): Accepts T or any superclass of T. You can write to it (it "consumes" T values). Example: List<? super Integer> accepts List<Number>, List<Object>.
๐ง PECS Rule: Producer Extends, Consumer Super
- If you read from a collection (it produces values) โ use
extends - If you write to a collection (it consumes values) โ use
super - If you both read and write โ don't use wildcards, use exact type
Java import java.util.*; public class WildcardDemo { // Producer Extends โ reads from the list public static double sumOfList(List<? extends Number> list) { double sum = 0; for (Number n : list) { sum += n.doubleValue(); } return sum; } // Consumer Super โ writes to the list public static void addIntegers(List<? super Integer> list) { list.add(10); list.add(20); list.add(30); } public static void main(String[] args) { List<Integer> intList = Arrays.asList(10, 20, 30); List<Double> dblList = Arrays.asList(1.5, 2.5, 3.5); System.out.println("Int sum: " + sumOfList(intList)); // 60.0 System.out.println("Dbl sum: " + sumOfList(dblList)); // 7.5 List<Number> numList = new ArrayList<>(); addIntegers(numList); // Works โ Number is super of Integer System.out.println("Numbers: " + numList); // [10, 20, 30] } }
Diamond Operator <> โ Type Inference
Since Java 7, you don't need to repeat the type on the right side:
Java // Before Java 7: Map<String, List<Integer>> map = new HashMap<String, List<Integer>>(); // Java 7+ with diamond operator: Map<String, List<Integer>> map = new HashMap<>(); // Compiler infers types โ
ArrayList<String> and ArrayList<Integer> become the same ArrayList class at runtime. This is why you can't do new T() or instanceof List<String>.
Learn by Doing โ 3-Tier Lab: Student Record File Serializer
๐ข Tier 1 โ GUIDED TASK: Serialize & Deserialize Student Records
Step 1: Create the Student class
Create a class Student that implements Serializable. Fields: name (String), rollNo (int), cgpa (double), password (String, transient).
Step 2: Serialize to file
In main(), create 3 Student objects. Use ObjectOutputStream wrapped in FileOutputStream to write them to "student_records.ser".
Step 3: Deserialize from file
Use ObjectInputStream wrapped in FileInputStream to read the objects back. Print each. Verify that password is null.
Step 4: Add CSV export
After deserialization, write the student data to "student_export.csv" using BufferedWriter. Include a header row: Name,Roll,CGPA.
Step 5: Read & display CSV
Use BufferedReader to read "student_export.csv" and print it in a formatted table.
๐ Congratulations! You've built a complete serialize โ export โ read pipeline โ the same pattern used in IRCTC ticket data processing.
๐ก Tier 2 โ SEMI-GUIDED: Generic Collection Manager
Your Mission:
Build a GenericCollectionManager<T> that can store, search, sort, and serialize a list of any Comparable type.
Hints:
- Class Signature:
class GenericCollectionManager<T extends Comparable<T> & Serializable> - Methods to implement:
add(T),remove(T),sort(),search(T),saveToFile(String),loadFromFile(String) - Sorting: Use your
bubbleSortorCollections.sort() - Serialization: Serialize the entire
ArrayList<T> - Test with:
GenericCollectionManager<String>for city names andGenericCollectionManager<Integer>for marks
filter(Predicate<T>) method using Java 8 lambdas that returns filtered results.
๐ด Tier 3 โ OPEN CHALLENGE: Student Management System with File Persistence
The Brief:
Build a console-based Student Management System with these features:
- Menu-driven: Add student, View all, Search by roll, Delete by roll, Sort by CGPA, Export to CSV, Import from CSV, Save (serialize), Load (deserialize), Exit
- Use Generics: Store students in
GenericStack<Student>orArrayList<Student> - File I/O: CSV import/export + binary serialization
- Error handling: File not found, invalid input, empty list
- Bonus: Add an undo feature using your
GenericStack
Problem Set โ Syntax, Programming, Industry & Interview
Syntax Questions (5)
- Write the syntax to create a
FileReaderobject that reads from"data.txt". - Write the syntax for declaring a generic class
Containerwith type parameterT. - Write the statement to serialize an object
empusingObjectOutputStream oos. - Write the syntax for a bounded generic method that accepts only
Comparabletypes. - Write a try-with-resources statement that creates a
BufferedReaderwrapping aFileReader.
Programming Problems (8)
- File Word Counter: Write a program that reads a text file using
BufferedReaderand counts the total number of words, lines, and characters. - File Copy Utility: Write a program that copies a text file from source to destination using
BufferedReaderandBufferedWriter. Display byte count after copy. - Student Serializer: Create a
Studentclass with 5 fields (one transient). Serialize an array of 5 students to a file, then deserialize and display them. - CSV Parser: Read a CSV file containing employee records (Name, Dept, Salary). Parse each line, calculate total salary, and write a summary report to a new file.
- Generic Stack: Implement a
GenericStack<T>with push, pop, peek, isEmpty, size, and display. Test with String and Integer types. - Generic Pair: Implement a
GenericPair<K,V>class. Write a method that accepts an array of pairs and returns the pair with the maximum value (V must extend Comparable). - Bounded Sorter: Write a generic method
sort(T[] arr)whereT extends Comparable<T>. Use insertion sort. Test with Integer[] and String[]. - File-Based Phone Book: Build a phone book that saves contacts to a file using serialization. Support: add, search, delete, display all, save, load.
Industry-Level Problems (3)
- Log File Analyzer: Given a server log file (timestamp, level, message), read it using
BufferedReader, count ERROR vs WARN vs INFO entries, and write a summary report. UseGenericPair<String, Integer>to store level-count pairs. - Config File Reader: Build a generic config reader that reads key=value pairs from a
.propertiesfile and stores them in aMap<String, String>. Support type-safe getters:getInt(key),getString(key),getBoolean(key). - Data Pipeline: Read student records from CSV โ validate (remove invalid rows) โ serialize valid records โ deserialize and generate a marks report โ write report to a text file. Use generics for the validation step.
Interview Questions (3)
- TCS/Infosys: "What is the difference between
SerializableandExternalizable? When would you use each?" Write a comparison with code examples. - Amazon/Flipkart: "Explain the PECS rule with a real-world example. Why can't you add elements to a
List<? extends Number>?" Provide code that demonstrates the compiler error. - Wipro/HCL: "What is type erasure in Java generics? What are its limitations? Can you create a generic array? Why or why not?"
MCQ Assessment Bank โ 30 Questions (Bloom's Mapped)
Remember / Identify (Q1โQ6)
Which class is used to read bytes from a file in Java?
- FileReader
- FileInputStream
- BufferedReader
- Scanner
Which interface must a class implement to be serializable?
- Cloneable
- Comparable
- Serializable
- Iterable
What does the transient keyword do?
- Makes a field constant
- Prevents a field from being serialized
- Makes a field thread-safe
- Makes a field accessible across packages
Which method of BufferedReader reads a complete line?
- read()
- readLine()
- nextLine()
- getLine()
In Java generics, what does <T> represent?
- A specific class called T
- A type parameter placeholder
- The Thread class
- A wildcard type
Which class is used to serialize objects in Java?
- ObjectWriter
- FileWriter
- ObjectOutputStream
- DataOutputStream
Understand / Explain (Q7โQ12)
Why is BufferedReader faster than FileReader for reading large files?
- It uses multithreading
- It reads data in large chunks from disk, reducing I/O calls
- It compresses the file first
- It skips whitespace automatically
What problem do generics solve in Java?
- Memory management
- Runtime type safety by catching type errors at compile time
- Faster execution speed
- Multi-threading synchronization
Why can't primitive types (int, double) be used as generic type parameters?
- Primitives are too large
- Generics work with Object references; primitives are not objects
- Primitives don't have methods
- It's a bug in Java
What happens if you serialize an object with a transient field and then deserialize it?
- The program throws an exception
- The transient field retains its original value
- The transient field gets its default value (null/0/false)
- The entire object becomes null
What is the purpose of serialVersionUID?
- To uniquely identify each serialized object
- To ensure compatibility between serialized data and the class version
- To encrypt the serialized data
- To count how many times an object has been serialized
What does "type erasure" mean in Java generics?
- Generic types are removed from memory after use
- The compiler replaces generic types with Object (or bounds) and removes type info from bytecode
- Types are encrypted during compilation
- Generic classes run slower than non-generic classes
Apply / Implement (Q13โQ18)
Which code correctly reads a file line by line?
FileReader fr = new FileReader("f.txt"); fr.readLine();BufferedReader br = new BufferedReader(new FileReader("f.txt")); br.readLine();Scanner sc = new Scanner("f.txt"); sc.readLine();InputStream is = new InputStream("f.txt"); is.readLine();
What is the output of this code?Box<String> b = new Box<>(); b.put("Java"); System.out.println(b.get().length());
- 4
- Java
- Compile error
- Runtime error
Which statement correctly serializes an object?
oos.serialize(obj);oos.writeObject(obj);oos.write(obj);oos.save(obj);
What will this code print?GenericPair<String, Integer> p = new GenericPair<>("Java", 12); System.out.println(p.getKey() + p.getValue());
- Java12
- 12Java
- Compile error
- Java 12
Which declaration creates a FileWriter that appends to an existing file?
new FileWriter("f.txt")new FileWriter("f.txt", true)new FileWriter("f.txt", "append")new FileWriter("f.txt").append()
What does this method signature mean?public static <T extends Comparable<T>> T findMax(T[] arr)
- T must be a subclass of Comparable
- T can be any type
- T must implement the Comparable interface
- Both A and C
Analyze / Compare (Q19โQ24)
When should you use byte streams instead of character streams?
- When reading text files in Hindi
- When processing binary files (images, audio, serialized objects)
- When reading CSV files
- When using Scanner
What is the key difference between List<?> and List<Object>?
- They are identical
List<?>accepts List<String>, List<Integer>, etc.;List<Object>only accepts List<Object>List<Object>is fasterList<?>can only hold null
Why is Scanner slower than BufferedReader for large file processing?
- Scanner doesn't buffer data
- Scanner uses regex-based parsing internally, adding overhead
- Scanner can't read files
- Scanner compresses data before reading
According to the PECS rule, which wildcard should be used for a method that READS items from a list?
<? super T><? extends T><?>- No wildcard needed
What happens if two classes have the same serialVersionUID but different field structures?
- Deserialization succeeds silently
- Deserialization may produce corrupt data or throw exceptions for incompatible fields
- The JVM automatically migrates the data
- The program won't compile
Why can't you do new T() inside a generic class?
- T is not a real class
- Type erasure removes T at runtime, so JVM doesn't know which constructor to call
- Generics don't support constructors
- T must be abstract
Evaluate / Create (Q25โQ30)
A banking application needs to log transaction details. Which approach is better?
- Use
System.out.println() - Use
BufferedWriterwithFileWriterin append mode - Store in a local variable
- Use
Scannerto write
You need to store employee data where some fields contain sensitive information (Aadhaar number). Best approach?
- Don't serialize the class at all
- Mark sensitive fields as
transientand handle them separately with encryption - Store everything in plain text
- Use
staticfields for sensitive data
Which design is better for a reusable cache system?
class Cache { Object[] data; }class Cache<K, V> { Map<K, V> data; }class Cache { String[] keys; String[] values; }class Cache { int[] data; }
You're designing an API response wrapper for an Indian fintech app. Which approach?
class Response { Object data; int statusCode; }class Response<T> { T data; int statusCode; String message; }class Response { String jsonData; }class Response extends HashMap
Design a method that can print any List regardless of its element type. Which signature is correct?
void printList(List<Object> list)void printList(List<?> list)void printList(List list)void printList(List<String> list)
You need to build a configuration loader that reads properties from files and returns typed values. Best class design?
- One method per type:
getInt(),getString(), etc. - A generic method:
<T> T get(String key, Class<T> type) - Return everything as
Stringand let caller cast - Use
Objectreturn type everywhere
Short Answer Questions (8)
Q1. Differentiate between byte streams and character streams in Java.
Byte streams (InputStream/OutputStream) handle raw binary data โ images, audio, serialized objects. They read/write one byte (8 bits) at a time. Best for non-text files.
Character streams (Reader/Writer) handle text data with proper Unicode encoding. They read/write one character (16 bits) at a time. Essential for text files, especially those in Hindi, Tamil, or other non-ASCII scripts.
Key difference: Character streams automatically handle encoding/decoding (UTF-8, UTF-16), while byte streams treat all data as raw bytes.
Q2. What is serialization? Why is it needed?
Serialization is the process of converting a Java object into a byte stream so it can be saved to a file, transmitted over a network, or stored in a database. Deserialization is the reverse โ reconstructing the object from bytes.
Why needed: (1) Persisting object state between program runs. (2) Sending objects between JVMs across a network (RMI, socket programming). (3) Caching objects. (4) Deep copying objects.
Requirement: The class must implement java.io.Serializable.
Q3. Explain the role of the transient keyword with an example.
The transient keyword marks a field to be excluded from serialization. When the object is serialized, transient fields are skipped. After deserialization, they contain default values (null for objects, 0 for numbers, false for boolean).
Use case: Passwords, OTPs, session tokens, cached computed values, and database connections should never be serialized for security and safety reasons.
Example: transient String password; โ after deserialization, password will be null.
Q4. Why are generics preferred over using Object type?
Type safety: Generics catch type errors at compile time, not runtime. With Object, a ClassCastException only shows up when the program runs.
No casting: With ArrayList<String>, you get String directly. With ArrayList (raw), you must cast: (String) list.get(0).
Code reusability: Write one class (Box<T>) that works for any type, instead of BoxString, BoxInteger, etc.
Readability: List<Student> clearly communicates intent; List doesn't.
Q5. What is the PECS rule? Explain with an example.
PECS = Producer Extends, Consumer Super.
Producer Extends: If a method reads from a collection (it "produces" values), use <? extends T>. Example: double sum(List<? extends Number> list) โ can accept List<Integer>, List<Double>, etc.
Consumer Super: If a method writes to a collection (it "consumes" values), use <? super T>. Example: void addInts(List<? super Integer> list) โ can accept List<Number>, List<Object>.
Analogy: A vending machine (producer) extends โ it gives you specific snacks. A trash can (consumer) is super โ it accepts anything.
Q6. Compare Scanner and BufferedReader for file reading.
Scanner (java.util): Good for parsing typed input (nextInt, nextDouble). Uses regex internally โ slower. 1KB buffer. Not thread-safe. Best for console input and small files.
BufferedReader (java.io): Reads raw text lines fast. 8KB buffer. Thread-safe (synchronized). No built-in parsing โ you must use Integer.parseInt() etc. Best for large files and production code.
Bottom line: Scanner for convenience, BufferedReader for performance.
Q7. What is a bounded type parameter? Give syntax and use case.
A bounded type parameter restricts the types that can be used as a generic argument.
Syntax: <T extends UpperBound> โ T must be UpperBound or a subtype of it.
Example: <T extends Comparable<T>> ensures T can be compared, enabling sorting and finding max/min.
Multiple bounds: <T extends Comparable<T> & Serializable> โ T must implement both interfaces.
Use case: A generic sort method that only works with types that have a natural ordering.
Q8. Explain try-with-resources for I/O operations.
Try-with-resources (Java 7+) automatically closes resources (streams, connections) when the try block finishes, even if an exception occurs.
Syntax: try (BufferedReader br = new BufferedReader(new FileReader("f.txt"))) { ... }
Requirements: The resource must implement AutoCloseable (or Closeable). All Java I/O streams implement this.
Benefit: Eliminates resource leaks. No need for explicit finally { br.close(); } blocks. Cleaner, safer code.
Long Answer Questions (3)
Q1. Explain the Java I/O stream hierarchy with a complete diagram. Discuss byte streams vs character streams with code examples for reading and writing files. (15 marks)
Answer Structure:
1. Stream Hierarchy Overview
Java I/O is built on four abstract base classes:
| Category | Input | Output |
|---|---|---|
| Byte Streams | InputStream | OutputStream |
| Character Streams | Reader | Writer |
2. Byte Streams (Binary Data)
Handle raw binary data โ 8 bits at a time. Used for images, audio, serialized objects, PDFs.
Key classes: FileInputStream, FileOutputStream, BufferedInputStream, BufferedOutputStream, ObjectInputStream, ObjectOutputStream, DataInputStream, DataOutputStream.
Java // Byte stream example: copying an image file try (FileInputStream fis = new FileInputStream("photo.jpg"); FileOutputStream fos = new FileOutputStream("backup.jpg")) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead); } }
3. Character Streams (Text Data)
Handle Unicode text โ 16 bits at a time. Automatically handle encoding (UTF-8, UTF-16).
Key classes: FileReader, FileWriter, BufferedReader, BufferedWriter, PrintWriter, InputStreamReader, OutputStreamWriter.
Java // Character stream example: reading a text file try (BufferedReader br = new BufferedReader(new FileReader("notes.txt"))) { String line; while ((line = br.readLine()) != null) { System.out.println(line); } }
4. Buffered Streams โ Performance Optimization
BufferedReader/BufferedWriter use an internal buffer (8KB default), reducing the number of physical disk I/O operations. A file read with FileReader makes one disk call per character; BufferedReader makes one disk call per 8192 characters.
5. When to Use Which
| Scenario | Use |
|---|---|
| Read/write images, audio | Byte streams (FileInputStream/FileOutputStream) |
| Read/write text files | Character streams (BufferedReader/BufferedWriter) |
| Serialize Java objects | ObjectOutputStream/ObjectInputStream |
| Read typed console input | Scanner |
| High-performance text reading | BufferedReader |
Q2. Explain Java serialization and deserialization. Discuss the Serializable interface, transient keyword, and serialVersionUID with a complete working program. (15 marks)
Answer Structure:
1. What is Serialization?
Converting a Java object's state to a byte stream for storage or transmission. The byte stream can be saved to a file, sent over a network (sockets, RMI), or stored in a database as a BLOB.
2. Serializable Interface
A marker interface (no methods) in java.io. A class must implement it to be serializable. If a non-serializable object is passed to writeObject(), a NotSerializableException is thrown.
3. transient Keyword
Fields marked transient are excluded from serialization. After deserialization, they hold default values. Use for: passwords, cached values, logger references, non-serializable fields.
4. serialVersionUID
A version identifier for the class. If the class changes (fields added/removed) and the UID doesn't match, deserialization throws InvalidClassException. Best practice: always declare it explicitly.
5. Complete Program
Java import java.io.*; class Employee implements Serializable { private static final long serialVersionUID = 2L; String name; int empId; double salary; transient String sessionToken; Employee(String name, int empId, double salary, String token) { this.name = name; this.empId = empId; this.salary = salary; this.sessionToken = token; } public String toString() { return name + " | ID:" + empId + " | โน" + salary + " | Token:" + sessionToken; } } public class SerializationDemo { public static void main(String[] args) throws Exception { Employee e = new Employee("Priya", 1001, 85000, "abc-xyz-123"); // Serialize ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("emp.ser")); oos.writeObject(e); oos.close(); // Deserialize ObjectInputStream ois = new ObjectInputStream( new FileInputStream("emp.ser")); Employee d = (Employee) ois.readObject(); ois.close(); System.out.println(d); // Output: Priya | ID:1001 | โน85000.0 | Token:null } }
Key observations: sessionToken is null after deserialization because it was marked transient. All other fields are correctly restored.
Q3. Explain Java generics in detail โ generic classes, generic methods, bounded types, and wildcards. Provide code for a generic data structure. (15 marks)
Answer Structure:
1. What Are Generics?
Generics allow you to write classes, interfaces, and methods that work with any type while providing compile-time type safety. Introduced in Java 5 (2004).
2. Generic Class
Java class Container<T> { private T data; public void set(T data) { this.data = data; } public T get() { return data; } }
3. Generic Method
Java public static <T> void printArray(T[] arr) { for (T item : arr) System.out.print(item + " "); }
4. Bounded Types
<T extends Number> restricts T to Number or its subclasses. Multiple bounds: <T extends Comparable<T> & Serializable>.
5. Wildcards (PECS Rule)
<?> = any type, <? extends T> = read-only (producer), <? super T> = write-only (consumer).
6. Complete Generic Data Structure โ Sorted List
Java import java.util.*; class SortedList<T extends Comparable<T>> { private List<T> items = new ArrayList<>(); public void add(T item) { items.add(item); Collections.sort(items); } public T getMin() { return items.get(0); } public T getMax() { return items.get(items.size()-1); } public String toString() { return items.toString(); } } // Works with Integer, String, or any Comparable type! SortedList<Integer> marks = new SortedList<>(); marks.add(78); marks.add(92); marks.add(65); System.out.println(marks); // [65, 78, 92]
7. Type Erasure
The compiler removes all generic type information from bytecode for backward compatibility. Box<String> and Box<Integer> become the same Box class at runtime. This is why you can't use instanceof with generics or create generic arrays.
Lab Program 10 โ Java I/O: Console & File Operations
๐ฌ Lab 10: Read/Write Using Console (Scanner/BufferedReader) AND File (FileReader/FileWriter/BufferedWriter)
Aim:
To demonstrate Java I/O operations for both console-based and file-based input/output using Scanner, BufferedReader, FileReader, FileWriter, and BufferedWriter.
Full Code:
Java import java.io.*; import java.util.Scanner; public class Lab10_JavaIO { // โโ PART 1: Console Input with Scanner โโ public static void consoleWithScanner() { Scanner sc = new Scanner(System.in); System.out.println("โโโ PART 1: Console Input (Scanner) โโโ"); System.out.print("Enter your name: "); String name = sc.nextLine(); System.out.print("Enter your age: "); int age = sc.nextInt(); System.out.print("Enter your CGPA: "); double cgpa = sc.nextDouble(); System.out.println("\n๐ Scanner Output:"); System.out.println("Name: " + name); System.out.println("Age: " + age); System.out.println("CGPA: " + cgpa); sc.close(); } // โโ PART 2: Console Input with BufferedReader โโ public static void consoleWithBufferedReader() throws IOException { BufferedReader br = new BufferedReader( new InputStreamReader(System.in)); System.out.println("\nโโโ PART 2: Console Input (BufferedReader) โโโ"); System.out.print("Enter city name: "); String city = br.readLine(); System.out.print("Enter pincode: "); int pin = Integer.parseInt(br.readLine()); System.out.println("\n๐ BufferedReader Output:"); System.out.println("City: " + city + ", PIN: " + pin); } // โโ PART 3: File Write with FileWriter โโ public static void writeWithFileWriter() throws IOException { System.out.println("\nโโโ PART 3: Writing with FileWriter โโโ"); FileWriter fw = new FileWriter("lab10_output.txt"); fw.write("Line 1: Welcome to Java I/O Lab.\n"); fw.write("Line 2: FileWriter writes character by character.\n"); fw.write("Line 3: เคจเคฎเคธเฅเคคเฅ โ Unicode works!\n"); fw.close(); System.out.println("โ Written to lab10_output.txt using FileWriter."); } // โโ PART 4: File Read with FileReader โโ public static void readWithFileReader() throws IOException { System.out.println("\nโโโ PART 4: Reading with FileReader โโโ"); FileReader fr = new FileReader("lab10_output.txt"); int ch; while ((ch = fr.read()) != -1) { System.out.print((char) ch); } fr.close(); } // โโ PART 5: File Write with BufferedWriter โโ public static void writeWithBufferedWriter() throws IOException { System.out.println("\nโโโ PART 5: Writing with BufferedWriter โโโ"); try (BufferedWriter bw = new BufferedWriter( new FileWriter("lab10_students.csv"))) { bw.write("Name,Roll,CGPA,City"); bw.newLine(); bw.write("Arjun Patel,101,8.5,Ahmedabad"); bw.newLine(); bw.write("Sneha Iyer,102,9.2,Chennai"); bw.newLine(); bw.write("Rahul Verma,103,7.8,Lucknow"); bw.newLine(); bw.write("Priya Sharma,104,8.9,Delhi"); bw.newLine(); bw.write("Kiran Das,105,7.2,Kolkata"); bw.newLine(); } System.out.println("โ Written to lab10_students.csv using BufferedWriter."); } // โโ PART 6: File Read with BufferedReader โโ public static void readWithBufferedReader() throws IOException { System.out.println("\nโโโ PART 6: Reading CSV with BufferedReader โโโ"); try (BufferedReader br = new BufferedReader( new FileReader("lab10_students.csv"))) { String header = br.readLine(); System.out.printf("%-18s %-8s %-8s %-12s%n", "NAME", "ROLL", "CGPA", "CITY"); System.out.println("โ".repeat(46)); String line; int count = 0; double totalCgpa = 0; while ((line = br.readLine()) != null) { String[] parts = line.split(","); if (parts.length == 4) { System.out.printf("%-18s %-8s %-8s %-12s%n", parts[0], parts[1], parts[2], parts[3]); totalCgpa += Double.parseDouble(parts[2]); count++; } } System.out.println("โ".repeat(46)); System.out.printf("Total: %d students | Avg CGPA: %.2f%n", count, totalCgpa / count); } } // โโ MAIN METHOD โโ public static void main(String[] args) throws IOException { System.out.println("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ"); System.out.println("โ LAB 10 โ JAVA I/O OPERATIONS โ"); System.out.println("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n"); // Uncomment to test console input: // consoleWithScanner(); // consoleWithBufferedReader(); // File operations (no console input needed): writeWithFileWriter(); readWithFileReader(); writeWithBufferedWriter(); readWithBufferedReader(); System.out.println("\n๐ Lab 10 complete!"); } }
๐ค Viva Questions (5)
- Q: What is the difference between FileReader and BufferedReader?
A: FileReader reads one character at a time from disk. BufferedReader wraps FileReader and reads large chunks into an internal buffer, then serves reads from memory. BufferedReader also provides readLine(). - Q: Why do we use try-with-resources instead of manually closing streams?
A: try-with-resources guarantees the stream is closed even if an exception occurs, preventing resource leaks. Manual close() in finally blocks is verbose and error-prone. - Q: What happens if you write to a file using FileWriter without specifying append mode?
A: The file is overwritten. Previous contents are lost. To append, usenew FileWriter("file.txt", true). - Q: How does BufferedWriter's newLine() differ from writing "\n"?
A: newLine() writes the platform-specific line separator (\r\n on Windows, \n on Linux). Using "\n" directly may cause issues on Windows systems. - Q: Can BufferedReader read binary files (e.g., .jpg)?
A: No, BufferedReader is a character stream designed for text. For binary files, use BufferedInputStream/FileInputStream. Using character streams on binary data can corrupt it.
๐ Extension Challenge
Modify the lab program to:
- Accept student records from console using Scanner
- Write them to CSV using BufferedWriter
- Read the CSV back and find the student with highest CGPA
- Write a "topper report" to a separate text file
- Add error handling for invalid CGPA values (must be 0.0โ10.0)
Industry Spotlight โ A Day in the Life
๐จโ๐ป Rahul Deshpande, 28 โ Backend Developer at Amazon India, Hyderabad
Background: B.Tech (CSE) from COEP, Pune. First learned Java I/O during his 3rd semester. Interned at a Pune-based startup where he built file-processing microservices. Joined Amazon India through off-campus hiring (LeetCode + system design).
A Typical Day:
9:30 AM โ Morning standup with the Seller Central team. Current sprint: optimise bulk product upload pipeline โ sellers upload 50,000+ products via CSV files.
10:00 AM โ Code review a colleague's PR. The new CSV parser uses BufferedReader with a 32KB buffer for faster reads. Rahul spots a missing try-with-resources and flags it.
11:00 AM โ Write a generic BatchProcessor<T extends Serializable> class that serializes failed upload batches for retry. Uses ObjectOutputStream to save failed records to S3.
1:00 PM โ Lunch at Amazon's cafeteria. Quick chat about generics vs raw types with a junior dev who's getting ClassCastExceptions.
2:00 PM โ Debug a production issue: a seller's CSV file has BOM (Byte Order Mark) characters. Solution: use InputStreamReader with explicit UTF-8 encoding instead of plain FileReader.
4:00 PM โ Design review for a new feature: Response<T> wrapper for all Seller API responses. Uses generics to ensure type-safe JSON deserialization.
5:30 PM โ Mentor a junior developer on Java serialization best practices โ serialVersionUID, transient fields, custom readObject/writeObject.
| Detail | Info |
|---|---|
| Tools Used Daily | Java 17, IntelliJ IDEA, BufferedReader/Writer, ObjectStreams, Generics, AWS S3, DynamoDB |
| Entry Salary (2024) | โน15โ22 LPA (Amazon SDE-1) |
| Mid-Level (3โ5 yrs) | โน25โ40 LPA (SDE-2) |
| Senior (7+ yrs) | โน45โ70 LPA (SDE-3) |
| Companies Using Java I/O + Generics | Amazon, Flipkart, Razorpay, PhonePe, Paytm, TCS, Infosys, Wipro, UIDAI, IRCTC |
Earn With It โ File Automation Scripts โน5Kโโน15K/month
๐ฐ Your Earning Path After This Unit
Portfolio Piece: A Java-based Student Record Management System with CSV import/export, serialization, and generic data structures โ hosted on GitHub.
Freelance Gig Ideas:
โข CSV/Excel file parser for small businesses (inventory, attendance) โ โน3,000โโน8,000/project
โข Bulk file renamer/organiser script (photos, invoices, receipts) โ โน2,000โโน5,000
โข Automated report generator: read data from files โ generate formatted text/CSV reports โ โน5,000โโน12,000
โข Log file analyzer for local IT companies โ โน4,000โโน10,000
โข Data migration scripts (convert old file formats to new CSV/JSON) โ โน5,000โโน15,000
| Platform | Best For | Typical Rate |
|---|---|---|
| Internshala | Indian student freelance projects | โน3,000โโน10,000/project |
| Fiverr | Quick file processing gigs | $15โ$60/gig |
| Upwork | Java automation projects | $20โ$50/hour |
| Direct outreach to IT companies | โน5,000โโน15,000/project | |
| Local Businesses | Shops, schools, clinics needing file automation | โน2,000โโน8,000/project |
โฑ๏ธ Time to First Earning: 2โ4 weeks (complete the lab programs + build one real project + create Fiverr/Internshala profile)
Chapter Summary
๐ง Key Takeaways โ Unit 12
- Byte Streams (InputStream/OutputStream) handle binary data; Character Streams (Reader/Writer) handle text with Unicode support.
- BufferedReader/BufferedWriter dramatically improve I/O performance through internal buffering (8KB default).
- The File class provides file metadata operations โ exists, size, create directory, list files.
- Scanner is for convenience (parsing tokens); BufferedReader is for performance (reading large files fast).
- Serialization converts objects to bytes (ObjectOutputStream); Deserialization reverses it (ObjectInputStream). Class must implement
Serializable. - transient excludes fields from serialization; serialVersionUID ensures class version compatibility.
- Generics provide compile-time type safety, eliminating ClassCastException and manual casting.
- Generic classes (
Box<T>), generic methods (<T> T getFirst()), and bounded types (<T extends Comparable<T>>) enable reusable, type-safe code. - Wildcards:
<?>= any type,<? extends T>= read (Producer Extends),<? super T>= write (Consumer Super). - Diamond operator
<>enables type inference in Java 7+. - Type erasure removes generic type info from bytecode for backward compatibility.
๐ฆ Code Tweet โ Unit 12 in 280 Characters
Java I/O: InputStreamโbytes, Readerโchars. BufferedReader=fast. Serializable+ObjectOutputStream=save objects. transient=skip secrets. Generics: Box<T>=type-safe. <?extends T>=read, <?super T>=write. PECS rule. Diamond<>=less typing. ๐ #JavaIO #Generics
Checkpoint โ Self-Assessment
| Topic | Tool / Concept | Output / Deliverable | Earning Ready? |
|---|---|---|---|
| Byte Streams | FileInputStream / FileOutputStream | File copy program | โ Yes โ binary file processing |
| Character Streams | FileReader / FileWriter | Text file read/write | โ Yes โ text processing scripts |
| Buffered I/O | BufferedReader / BufferedWriter | CSV parser with formatted output | โ Yes โ CSV automation gigs |
| File Class | File operations | File metadata inspector | โ Yes โ file management tools |
| Serialization | ObjectOutputStream / ObjectInputStream | Student serializer program | โ Yes โ data persistence |
| transient & serialVersionUID | Conceptual + Code | Secure serialization demo | โ Yes โ interview ready |
| Generic Classes | Box<T>, GenericStack<T>, GenericPair<K,V> | Reusable data structures | โ Yes โ library design |
| Bounded Types | <T extends Comparable<T>> | BoundedSorter program | โ Yes โ algorithm design |
| Wildcards & PECS | <?>, <? extends T>, <? super T> | Wildcard demo programs | โ Yes โ API design |
| Lab 10 | Scanner, BufferedReader, FileWriter, BufferedWriter | Complete I/O lab program | โ Yes โ lab exam ready |
โ Unit 12 complete. MCQs: 30. Lab 10 covered. Ready for Unit 13!
[QR: Link to EduArtha video tutorial โ Java I/O Fundamentals & Generics]