Topics to learn Today
- Method Overloading, Static & Instance Methods
- Classes and Objects
- Oops Concepts
- Java Collections – The Basics
- java.time
- File I/O and Streams
- Exception Classes
- System & Runtime
- Problem
Method Overloading, Static & Instance Methods
Method Overloading
Overloading means you can have multiple methods with the same name but different parameters.
Java uses the number and type of parameters to decide which version to run.
public static int square(int x) {
return x * x;
}
public static double square(double x) {
return x * x;
}Usage:
System.out.println(square(5)); // 25 (int version)
System.out.println(square(3.5)); // 12.25 (double version)✅ Same name
✅ Different parameter type or count
❌ Cannot differ by return type only
Static Methods
- Belong to the class itself
- Can be called without creating an object
- Common for utility/helper methods
public class MathUtil {
public static int square(int x) {
return x * x;
}
}Usage:
int result = MathUtil.square(4); // No object neededThe
main()method is also static because the JVM calls it without creating an object.
Instance Methods
- Belong to an object
- You need to create an instance to call them
public class Person {
String name;
public void greet() {
System.out.println("Hello, my name is " + name);
}
}Usage:
Person p = new Person();
p.name = "Alice";
p.greet(); // Hello, my name is AliceInstance methods can access non-static fields and other instance methods.
Quick Comparison
| Feature | Static Method | Instance Method |
|---|---|---|
| Belongs to | Class | Object |
| Accessed by | ClassName.method() | object.method() |
Can use this | ❌ | ✅ |
| Common use case | Utility, main() | Behavior tied to object state |
Classes & Objects – The Basics
A class is like a blueprint.
An object is a real-world thing built from that blueprint.
🔧 What is a Class?
A class defines the structure and behavior of an object — like a blueprint or template.
public class Person {
String name;
int age;
void sayHello() {
System.out.println("Hi, I’m " + name + " and I’m " + age + " years old.");
}
}nameandageare fields (aka instance variables)sayHello()is a method
🧍 What is an Object?
An object is a real instance of a class, created in memory.
Person p = new Person(); // Create an object
p.name = "Alice";
p.age = 25;
p.sayHello(); // Hi, I’m Alice and I’m 25 years old.-
new Person()creates a new object from thePersonclass -
pis a reference to that object
🛠 Constructors
A constructor is a special method used to initialize an object when it’s created.
public class Person {
String name;
int age;
// Constructor
public Person(String n, int a) {
name = n;
age = a;
}
void sayHello() {
System.out.println("Hi, I’m " + name);
}
}Usage:
Person p = new Person("Bob", 30);
p.sayHello(); // Hi, I’m BobIf you don’t define a constructor, Java provides a default constructor.
🎯 The this Keyword
Used to refer to the current object — often used in constructors or methods.
public Person(String name, int age) {
this.name = name; // refers to the object's field
this.age = age;
}✅ TL;DR Summary Table
| Concept | Example | Notes |
|---|---|---|
| Class | class Car { ... } | Blueprint for objects |
| Object | Car c = new Car(); | Real instance of a class |
| Field | String name; | Variables inside the class |
| Method | void drive() { ... } | Behavior of the class |
| Constructor | public Car(...) | Initializes an object |
this | this.name = name; | Refers to current object’s field |
OOPs Concepts – The Core of Java
The Idea is that Java is not just a procedural language — it’s object-oriented. Everything revolves around classes, objects, and the principles of OOP.
OOP = Object-Oriented Programming
It helps organise code using real-world thinking: objects, properties, behaviors, and relationships.
1. Encapsulation – Hiding Data
Bind data (fields) and methods in one unit, and restrict direct access.
-
Keeps data safe from outside interference
-
Achieved using private fields + public getters/setters
public class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount > 0) balance += amount;
}
public double getBalance() {
return balance;
}
}-
balanceis encapsulated — cannot be accessed directly -
Only accessible through methods
2. Inheritance – Reuse Behaviour
One class inherits properties and methods from another.
-
Promotes code reuse
-
Use
extendskeyword
public class Animal {
void eat() {
System.out.println("Eating...");
}
}
public class Dog extends Animal {
void bark() {
System.out.println("Barking...");
}
}Usage:
Dog d = new Dog();
d.eat(); // inherited
d.bark(); // own method3. Polymorphism – Many Forms
One interface, many implementations.
a) Compile-Time Polymorphism (Method Overloading)
public class MathUtils {
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
}b) Runtime Polymorphism (Method Overriding)
class Animal {
void speak() { System.out.println("Animal sound"); }
}
class Cat extends Animal {
@Override
void speak() { System.out.println("Meow"); }
}Animal a = new Cat();
a.speak(); // Meow (runtime decision)4. Abstraction – Hiding Implementation Details
Show only essential features, hide the rest.
- Use abstract classes or interfaces
- Forces subclasses to implement required behaviour
Abstract Class Example
abstract class Shape {
abstract void draw(); // no body
}
class Circle extends Shape {
void draw() {
System.out.println("Drawing circle");
}
}Interface Example
interface Vehicle {
void drive(); // implicitly public and abstract
}
class Car implements Vehicle {
public void drive() {
System.out.println("Driving car");
}
}✅ TL;DR OOPs Summary Table
| OOP Concept | Meaning | Java Keyword/Feature |
|---|---|---|
| Encapsulation | Hiding data inside a class | private, getters/setters |
| Inheritance | One class inherits from another | extends |
| Polymorphism | One name, many behaviors | Overloading, Overriding |
| Abstraction | Hiding complex logic from the user | abstract, interface |
Java Collections – The Basics
Java Collections are a framework of interfaces and classes for storing and working with groups of objects. Instead of manually building arrays or linked lists, Java provides built-in, optimized structures.
Collections are part of the java.util package.
1. ArrayList – Resizable Array
-
Fast random access
-
Slow for frequent inserts/removals in the middle
import java.util.ArrayList;
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add(1, "C");
list.remove("B");
System.out.println(list.get(0)); // ACommon methods: add(), get(), remove(), set(), contains(), size()
2. LinkedList – Doubly Linked List
-
Good for fast insert/delete at start or end
-
Implements both
ListandDeque
import java.util.LinkedList;
LinkedList<String> list = new LinkedList<>();
list.add("X");
list.addFirst("A");
list.addLast("B");
list.removeFirst();
System.out.println(list.getLast()); // BCommon methods: addFirst(), addLast(), removeFirst(), peek(), poll(), offer()
3. HashMap – Key-Value Storage
-
Stores data in key-value pairs
-
Keys must be unique
-
No order guaranteed
import java.util.HashMap;
HashMap<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
System.out.println(map.get("a")); // 1
map.remove("a");Common methods: put(), get(), remove(), containsKey(), keySet(), entrySet()
4. HashSet – Unique Elements
- No duplicates allowed
- Fast lookup and insertion
- Unordered
import java.util.HashSet;
HashSet<String> set = new HashSet<>();
set.add("one");
set.add("two");
set.add("one"); // Ignored
System.out.println(set.contains("two")); // trueCommon methods: add(), remove(), contains(), size(), isEmpty()
5. TreeMap – Sorted Key-Value Map
-
Keys are sorted
-
Backed by a Red-Black Tree
-
Slower than
HashMap, but ordered
import java.util.TreeMap;
TreeMap<Integer, String> treeMap = new TreeMap<>();
treeMap.put(2, "B");
treeMap.put(1, "A");
System.out.println(treeMap.firstKey()); // 1Common methods: put(), get(), firstKey(), lastKey(), subMap()
6. TreeSet – Sorted Set
-
No duplicates
-
Elements are sorted
-
Slower than
HashSet
import java.util.TreeSet;
TreeSet<String> set = new TreeSet<>();
set.add("B");
set.add("A");
System.out.println(set.first()); // ACommon methods: add(), remove(), first(), last(), higher(), lower()
7. Queue and Deque – FIFO and Double-Ended
Queue (First-In-First-Out)
import java.util.Queue;
import java.util.ArrayDeque;
Queue<String> queue = new ArrayDeque<>();
queue.offer("A");
queue.offer("B");
System.out.println(queue.poll()); // ADeque (Double-Ended Queue)
import java.util.Deque;
import java.util.ArrayDeque;
Deque<String> deque = new ArrayDeque<>();
deque.addFirst("X");
deque.addLast("Y");
System.out.println(deque.pollFirst()); // XCommon methods:
- Queue:
offer(),poll(),peek() - Deque:
addFirst(),addLast(),pollFirst(),pollLast()
8. Collections Utility Class
A helper class with static methods to work with lists, sets, etc.
import java.util.Collections;
import java.util.ArrayList;
ArrayList<Integer> nums = new ArrayList<>();
Collections.addAll(nums, 3, 1, 2);
Collections.sort(nums); // [1, 2, 3]
Collections.reverse(nums); // [3, 2, 1]
Collections.shuffle(nums); // Random orderUseful methods:
sort(),reverse(),shuffle()max(),min(),frequency()binarySearch()synchronizedList()for thread safety
Summary Table
| Collection Type | Ordered? | Allows Duplicates? | Unique Features |
|---|---|---|---|
ArrayList | ✅ | ✅ | Fast access |
LinkedList | ✅ | ✅ | Fast insertion/removal |
HashMap | ❌ | Keys ❌ | Key-value pairs |
HashSet | ❌ | ❌ | Unique, unordered elements |
TreeMap | ✅ | Keys ❌ | Sorted key-value |
TreeSet | ✅ | ❌ | Sorted unique elements |
Queue/Deque | Partially | ✅ | FIFO / double-ended operations |
Collections | N/A | N/A | Utility methods |
java.time – Working with Dates and Time in Java
The Idea is to learn how to work with dates, times, durations, and formatting in Java using the modern java.time package (introduced in Java 8).
Old date APIs like
DateandCalendarwere clunky and error-prone.
java.timeis immutable, thread-safe, and much more readable.
🧱 Core Classes in java.time
1. LocalDate – Date only (no time)
import java.time.LocalDate;
LocalDate date = LocalDate.now(); // Today
LocalDate dob = LocalDate.of(1990, 5, 15); // 1990-05-15Common methods:
date.getYear();
date.getMonth(); // MAY
date.getDayOfWeek(); // MONDAY
date.plusDays(5); // add days
date.minusYears(1); // subtract years2. LocalTime – Time only (no date)
import java.time.LocalTime;
LocalTime time = LocalTime.now(); // Current time
LocalTime fixed = LocalTime.of(14, 30); // 14:30Common methods:
time.getHour();
time.plusMinutes(15);3. LocalDateTime – Date + Time (no time zone)
import java.time.LocalDateTime;
LocalDateTime dt = LocalDateTime.now();
LocalDateTime meeting = LocalDateTime.of(2025, 5, 15, 10, 0);Combine date and time:
LocalDate date = LocalDate.of(2025, 5, 15);
LocalTime time = LocalTime.of(9, 30);
LocalDateTime combined = LocalDateTime.of(date, time);4. Instant – Timestamp (machine time)
-
Represents a specific moment in UTC
-
Often used for logging, timestamps, APIs
import java.time.Instant;
Instant now = Instant.now(); // e.g., 2025-05-15T10:45:30Z🔄 Operations with Dates
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
LocalDate lastWeek = today.minusWeeks(1);
boolean isBefore = today.isBefore(tomorrow); // true⏳ Durations and Periods
Period – Date-based (years, months, days)
import java.time.Period;
Period age = Period.between(LocalDate.of(2000, 1, 1), LocalDate.now());
System.out.println(age.getYears()); // e.g., 25Duration – Time-based (hours, minutes, seconds)
import java.time.Duration;
Duration d = Duration.between(LocalTime.of(10, 0), LocalTime.of(12, 30));
System.out.println(d.toMinutes()); // 150🧾 Formatting & Parsing
import java.time.format.DateTimeFormatter;
import java.time.LocalDate;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
LocalDate date = LocalDate.now();
String formatted = date.format(formatter); // "15-05-2025"Parsing:
LocalDate parsedDate = LocalDate.parse("31-12-2025", formatter);🌍 Time Zones with ZonedDateTime
import java.time.ZonedDateTime;
import java.time.ZoneId;
ZonedDateTime nowInParis = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
ZonedDateTime nowInIST = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));Convert between zones:
ZonedDateTime inUTC = nowInParis.withZoneSameInstant(ZoneId.of("UTC"));✅ TL;DR Summary Table
| Class | Description | Example |
|---|---|---|
LocalDate | Date only | LocalDate.of(2025, 5, 15) |
LocalTime | Time only | LocalTime.of(14, 30) |
LocalDateTime | Date + time | LocalDateTime.now() |
Instant | Timestamp in UTC | Instant.now() |
Period | Difference in dates | Period.between(d1, d2) |
Duration | Difference in time | Duration.between(t1, t2) |
ZonedDateTime | Date-time with time zone info | ZonedDateTime.now(ZoneId) |
DateTimeFormatter | Format/parse dates | format(), parse() |
Let me know if you’d like this expanded with time arithmetic problems, ISO format quirks, or legacy date conversion examples.
Here’s your Java: File I/O and Streams topic — clean, structured, and in the same note style you’ve been using for foundational learning.
📂 Java File I/O & Streams – Read, Write, Process Files
The Idea is to learn how Java handles file reading, writing, and data streams, using the java.io and java.nio.file packages.
You’ve written data in variables. Now, let’s persist it — save to disk, read from files, and process content line-by-line or byte-by-byte.
📄 1. File Class – Represent a File or Directory
import java.io.File;
File file = new File("example.txt");
System.out.println(file.exists()); // true or false
System.out.println(file.getAbsolutePath());You can check, create, or delete files/directories:
file.createNewFile(); // Creates new file if it doesn’t exist
file.delete(); // Deletes the file📝 2. Writing to a File (FileWriter, BufferedWriter)
import java.io.FileWriter;
import java.io.BufferedWriter;
BufferedWriter writer = new BufferedWriter(new FileWriter("data.txt"));
writer.write("Hello, file!");
writer.newLine();
writer.write("Another line");
writer.close(); // Always close!📖 3. Reading from a File (FileReader, BufferedReader)
import java.io.FileReader;
import java.io.BufferedReader;
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
BufferedReaderis preferred over rawFileReaderbecause it’s more efficient (reads in chunks, not character-by-character).
🧹 4. Try-with-Resources (Auto Close)
Since Java 7, you can auto-close files safely:
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}⚡ 5. Files Utility (from java.nio.file)
import java.nio.file.Files;
import java.nio.file.Paths;
List<String> lines = Files.readAllLines(Paths.get("data.txt"));
Files.write(Paths.get("output.txt"), lines);Very concise, useful for small files.
🔁 6. InputStream / OutputStream – For Binary Data
Used to read/write binary data (e.g. images, PDFs, etc.)
Write Bytes
import java.io.FileOutputStream;
FileOutputStream out = new FileOutputStream("binary.dat");
out.write(65); // writes ASCII 'A'
out.close();Read Bytes
import java.io.FileInputStream;
FileInputStream in = new FileInputStream("binary.dat");
int data = in.read();
System.out.println(data); // 65
in.close();Use
BufferedInputStream/BufferedOutputStreamfor performance
✅ TL;DR Summary Table
| Task | Class / Tool | Notes |
|---|---|---|
| File reference | File | Check, create, delete files |
| Write text | FileWriter, BufferedWriter | For writing strings to file |
| Read text | FileReader, BufferedReader | Efficient line-by-line reading |
| Short file I/O | Files.readAllLines(), Files.write() | Java NIO utility (since Java 7) |
| Binary data | InputStream, OutputStream | For images, audio, etc. |
| Safe closing | try-with-resources | Automatically closes streams |
🚨 Java Exception Classes – Handle Errors Gracefully
The Idea is to understand how Java handles errors using exceptions — instead of crashing the program, you can catch and recover from problems.
Exceptions are objects that represent runtime problems — and Java gives you a full hierarchy of exception classes to work with.
⚙️ What is an Exception?
An exception is an event that disrupts normal flow of the program.
-
It can be caught and handled
-
It is represented as an object of a subclass of
Throwable
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero");
}🧬 Exception Class Hierarchy
Throwable
├── Exception
│ ├── IOException
│ ├── SQLException
│ └── ...
├── RuntimeException
│ ├── NullPointerException
│ ├── IndexOutOfBoundsException
│ ├── ArithmeticException
│ └── ...
└── Error (don't handle this)
🧾 Checked vs Unchecked Exceptions
| Type | Inherits From | Must Handle? | Examples |
|---|---|---|---|
| Checked | Exception | ✅ Yes | IOException, SQLException |
| Unchecked | RuntimeException | ❌ No | NullPointerException, IndexOutOfBoundsException |
-
Checked exceptions: Compiler forces you to handle them (
try-catchorthrows) -
Unchecked exceptions: Optional to handle, but can still crash program
📦 Common Exception Classes
✅ Unchecked (Runtime)
| Class | Cause |
|---|---|
NullPointerException | Accessing method/field on null |
ArrayIndexOutOfBoundsException | Invalid array index |
ArithmeticException | Division by zero |
IllegalArgumentException | Bad arguments to a method |
✅ Checked
| Class | Cause |
|---|---|
IOException | File or I/O errors |
SQLException | Database access issues |
FileNotFoundException | File not found |
ParseException | String parsing failed |
🧰 How to Handle Exceptions
try {
int[] arr = {1, 2};
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Index out of bounds!");
} finally {
System.out.println("Always runs");
}-
try: Code that might fail -
catch: Handle specific exception -
finally: Always runs, for cleanup
🎯 Throwing Exceptions Yourself
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age can't be negative");
}
this.age = age;
}Use throw to create and send an exception
🧱 Creating Custom Exception
public class InvalidInputException extends Exception {
public InvalidInputException(String message) {
super(message);
}
}throw new InvalidInputException("Invalid value provided");🕵️ Catching Unknown Exceptions
Yes — you can encounter unknown exceptions. These are runtime issues you didn’t explicitly handle, like:
-
Unexpected
nullvalues -
Invalid input
-
Logic bugs
-
Library exceptions
✅ Catching All Exceptions
try {
// risky code
} catch (Exception e) {
System.out.println("Something went wrong: " + e.getMessage());
e.printStackTrace();
}-
Catches any checked or unchecked exception
-
Good for fallback logging or debugging
⚠️ Catching Everything (Including Error) — Not Recommended
try {
// risky code
} catch (Throwable t) {
System.out.println("Unexpected fatal error: " + t.getClass());
}-
Throwableis the superclass of everything (Exception+Error) -
Only use this for logging or diagnostics — don’t try to recover from
OutOfMemoryError, etc.
👍 Best Practice
try {
riskyOperation();
} catch (IOException e) {
// handle known issue
} catch (Exception e) {
// catch any unknown issues
logError(e);
}-
Always catch specific exceptions first
-
Add a general catch block if needed for unknowns or unexpected failures
✅ TL;DR Summary Table
| Concept | Keyword / Class | Notes |
|---|---|---|
| Throw exception | throw new Exception() | Create and send error |
| Catch exception | catch (Exception e) | Handle gracefully |
| Always run block | finally | Cleanup or final actions |
| Custom exception | class MyException extends Exception | Your own logic |
| Must handle? | Checked: ✅ Yes, Unchecked: ❌ No | Compiler enforces for checked |
| Unknown exceptions | catch (Exception e) or catch (Throwable t) | For unexpected issues |
System & Runtime – Interacting with the JVM and OS
🖥️ 1. System Class – Static Environment Tools
-
Contains only static methods and fields
-
No need to create an object
-
Utility class to deal with:
-
I/O streams
-
Time
-
Environment variables
-
Memory info
-
Exiting JVM
-
🔹 Common System Uses
✅ Standard Output and Error
System.out.println("Normal output");
System.err.println("This is an error");✅ Input via System.in
Scanner sc = new Scanner(System.in); // uses System.in internally✅ Current Time
long time = System.currentTimeMillis(); // milliseconds since epoch
long nano = System.nanoTime(); // high-res time for benchmarking✅ Environment and Properties
System.getenv("PATH"); // OS environment variable
System.getProperty("os.name"); // JVM system property
System.getProperties(); // All system properties✅ Exit Program
System.exit(0); // Exit normally
System.exit(1); // Exit with error code✅ Array Copying (Faster than loops)
int[] src = {1, 2, 3};
int[] dest = new int[3];
System.arraycopy(src, 0, dest, 0, 3);🚀 2. Runtime Class – Managing the Running JVM
-
Runtimeis a singleton (only one per JVM) -
Use
Runtime.getRuntime()to access it -
Lets you:
-
Monitor memory
-
Run external processes
-
Add shutdown hooks
-
Suggest garbage collection
-
🔹 Common Runtime Uses
✅ Memory Information
Runtime r = Runtime.getRuntime();
long max = r.maxMemory();
long used = r.totalMemory() - r.freeMemory();
System.out.println("Used memory: " + used);✅ Garbage Collection (Suggestion)
System.gc(); // Static (System calls Runtime under the hood)
Runtime.getRuntime().gc(); // Same effectNote: This suggests, not forces, garbage collection.
✅ Run External Commands
Runtime.getRuntime().exec("notepad"); // On WindowsYou can launch external programs (be cautious, OS-dependent)
✅ Add Shutdown Hook
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Program is shutting down...");
}));🧠 System vs Runtime
| Feature | System | Runtime |
|---|---|---|
| Type | Static utility class | Singleton object per JVM |
| Access | Direct (System.out, etc.) | Via Runtime.getRuntime() |
| Controls JVM? | Indirect | Yes (memory, shutdown, exec) |
| Good for | I/O, env, time, exit | Memory control, external processes |
| Thread-safe? | Yes | Mostly, but manage with care |
✅ TL;DR Summary Table
| Task | Use |
|---|---|
| Print output | System.out.println() |
| Print error | System.err.println() |
| Read input | System.in (via Scanner) |
| Exit program | System.exit(code) |
| Get time | System.currentTimeMillis() |
| Get environment var | System.getenv("KEY") |
| Run external process | Runtime.getRuntime().exec("cmd") |
| Get memory info | Runtime.getRuntime().totalMemory() |
| Suggest GC | System.gc() or Runtime.gc() |
| Shutdown hook | Runtime.addShutdownHook() |
Today’s Problem
Multi-File Analyser with Benchmarking and Report Generation
Objective:
Build a Java application that:
-
Analyzes multiple text files
-
Extracts detailed statistics (word count, frequency, unique words)
-
Benchmarks performance (time + memory)
-
Generates a structured report as output
-
Uses clean OOP design, exception handling, collections, file I/O, and a middleware for benchmarking
✅ Requirements
📥 Input
-
A directory path provided by the user (e.g.
"./texts/") -
The directory contains multiple
.txtfiles (assume UTF-8 text) -
Your program must read all
.txtfiles
⚙️ For Each File
-
Read the file
-
Count:
-
Number of lines
-
Number of words
-
Number of unique words
-
-
Identify top 5 most frequent words
-
Ignore punctuation and case
📤 Output Report (as file report.txt)
For each file:
File: file1.txt
Lines: 120
Words: 945
Unique Words: 348
Top 5 Words:
1. the – 52
2. java – 41
3. and – 35
4. you – 33
5. code – 31
At the end of the report:
=== Benchmark: Analysis Completed ===
Time: 1,238,912 ns
Memory Used: 1,045,672 bytes
Files Processed: 5
🧱 Technical Constraints
You must use:
-
File,BufferedReader,Files.walk()orFiles.list() -
HashMap,TreeMap,PriorityQueue(for top words) -
OOP: at least two classes (
FileAnalyzer,ReportGenerator, etc.) -
Exception handling (invalid paths, read errors, empty files)
-
Your own
BenchmarkUtilmiddleware to time and profile the entire operation -
StringandPatternorsplit()for tokenizing words -
Optional: Use concurrency (
ExecutorService) to process files in parallel
🔁 Bonus Features (Optional)
-
Use
ExecutorServiceto analyze files concurrently -
Use
java.timeto timestamp start/end time in the report -
Compress the report as a
.zipfile after generation -
Track words across all files and print the global top 10