BRAINBIT INFOTECH · RANCHI
COMPLETE CORE JAVA COURSE · PART 5 OF 5

Advanced Java & Capstone Projects

Files and dates, multithreading, databases with JDBC, modern Java features, and complete projects.

4 modules~26 hours40 practice examplesNeeds Parts 1-4

By the end of this part you can build

  • An attendance register saved to CSV
  • A multi-threaded ticket booking system
  • A student database with JDBC
  • Five capstone projects for your portfolio

Every code example has a ▶ Run button. Click ✎ Edit, or press L, to open the Code Lab.

ROADMAP FOR THIS PART

Module 18Files and Date-Time4.5 h
Module 19Multithreading and Concurrency4.5 h
Module 20JDBC Databases and Modern Java5 h
Module 21Capstone Projects12 h
{ } Java java Main.java main()
JavaBefore we begin

How to Use This Part

Suggested teaching hours per module (Part 5)05104.5 hM184.5 hM195 hM2012 hM21

Every module has

  1. 1
    A "Where is it used?" diagram of real projects
  2. 2
    Concept in short + code + terminal: press ▶ Run to watch the output appear
  3. 3
    Step-through animations recorded from the real JVM: variables, method calls, objects
  4. 4
    A Code Lab: edit, copy or download the code and run it with java Main.java
  5. 5
    10 practice examples: 5 Easy, 3 Medium, 2 Hard, with solutions
  6. 6
    An animated recap + quiz
Controls→ next, ← back, M menu, L Code Lab, F fullscreen.
Running JavaEvery output on the slides was produced by compiling and running the code with a real JDK 21, so the slides work offline. On your computer (JDK 11+), run a single file with java Main.java in the folder D:\java-practice.
MODULE 18

Files and Date-Time

⏱ 4.5 hours10 practice examplesStep-through animationsCode Lab

You will learn

  • Why files: saving data after the program ends
  • Writing and reading text files with java.nio.file.Files
  • CSV files, appending and BufferedReader
  • LocalDate, LocalTime, LocalDateTime
  • Period, Duration, ChronoUnit and DateTimeFormatter

You will build

  • A marks file importer and report writer
  • An attendance log
  • Age, due-date and EMI schedule calculators
FilesFiles.readAllLines(p)LocalDate.of(2026, 1, 26)
Module 18Real-world use

Where Is This Used in Real Projects?

files and datesdata that lasts💾Save datanotes, settings📄ReportsCSV / text exports📝Logsapp activity📅Due datesfees, EMIs, library🎂Ageseligibility checks⏰Timersdurations, delays

Real projects that use this

  • Variables vanish when the program ends; files keep data on the disk
  • Banks export statements as CSV; schools import mark lists from Excel/CSV
  • Every server writes log files to track what happened and when
  • Fee due dates, EMI schedules, ages and delivery estimates all use java.time
Module 18The idea

Files Keep Data When the Program Stops

Java programmemory (lost on exit)marks.csvon disk (kept)write: Files.writeStringread: Files.readAllLines
TaskOne line (java.nio.file)
write textFiles.writeString(p, text)
read all textFiles.readString(p)
read linesFiles.readAllLines(p)
appendFiles.writeString(p, t, APPEND)
exists?Files.exists(p)
💡 Concept in shortA Path names a file: Path p = Path.of("marks.csv");. The Files class reads and writes it. File operations can fail (missing file, no permission), so they throw IOException, a checked exception.
RememberRelative paths like "marks.csv" are relative to the folder where you run java.
Module 18Write and read

Writing and Reading a Text File

Main.java  JAVA
1import java.nio.file.*;
2import java.io.IOException;
3import java.util.List;
4
5public class Main {
6 public static void main(String[] args) throws IOException {
7 Path p = Path.of("notes.txt");
8 Files.writeString(p, "Buy notebooks\nPay school fee\n"); // create / overwrite
9 Files.writeString(p, "Call Riya\n", StandardOpenOption.APPEND); // add at end
10
11 List<String> lines = Files.readAllLines(p);
12 System.out.println(lines.size() + " lines:");
13 for (int i = 0; i < lines.size(); i++) {
14 System.out.println((i + 1) + ". " + lines.get(i));
15 }
16 System.out.println("Exists? " + Files.exists(p) + ", size " + Files.size(p) + " bytes");
17 }
18}
Terminal
D:\java-practice> java Main.java
3 lines:
1. Buy notebooks
2. Pay school fee
3. Call Riya
Exists? true, size 39 bytes
📄 notes.txt (file on disk)TXT
1Buy notebooks
2Pay school fee
3Call Riya
💡 Concept in shortwriteString replaces the whole file unless you pass APPEND. readAllLines returns a List<String>, one item per line. throws IOException on main passes file errors up (fine for practice; real apps catch them).
Module 18CSV

Reading a CSV File into Objects

Main.java  JAVA
1import java.nio.file.*;
2import java.io.IOException;
3import java.util.*;
4
5record Student(String name, int marks) { }
6
7public class Main {
8 public static void main(String[] args) throws IOException {
9 List<Student> list = new ArrayList<>();
10 List<String> lines = Files.readAllLines(Path.of("marks.csv"));
11 for (String line : lines.subList(1, lines.size())) { // skip header
12 String[] f = line.split(",");
13 list.add(new Student(f[0].trim(), Integer.parseInt(f[1].trim())));
14 }
15 list.sort(Comparator.comparingInt(Student::marks).reversed());
16 StringBuilder out = new StringBuilder("Rank,Name,Marks\n");
17 for (int i = 0; i < list.size(); i++) {
18 out.append(i + 1).append(",").append(list.get(i).name()).append(",").append(list.get(i).marks()).append("\n");
19 }
20 Files.writeString(Path.of("ranked.csv"), out);
21 System.out.println("Read " + list.size() + " students, wrote ranked.csv");
22 }
23}
Terminal
D:\java-practice> java Main.java
Read 4 students, wrote ranked.csv
📄 ranked.csv (file on disk)CSV
1Rank,Name,Marks
21,Aditi,91
32,Sana,88
43,Rohan,76
54,Imran,64
💡 Concept in shortA CSV file is text with comma-separated columns; Excel opens and saves it. Read lines, skip the header, split(","), convert with parseInt, and build objects. Writing is the reverse.
📄 marks.csv (input) (file on disk)TXT
1name,marks
2Aditi,91
3Rohan,76
4Sana,88
5Imran,64
Module 18Buffered

BufferedReader and BufferedWriter for Big Files

Main.java  JAVA
1import java.io.*;
2import java.nio.file.*;
3
4public class Main {
5 public static void main(String[] args) {
6 Path log = Path.of("app.log");
7 try (BufferedWriter w = Files.newBufferedWriter(log)) {
8 for (int i = 1; i <= 5; i++) {
9 w.write("2026-01-26 10:0" + i + " INFO user" + i + " logged in");
10 w.newLine();
11 }
12 w.write("2026-01-26 10:07 ERROR payment timeout");
13 w.newLine();
14 } catch (IOException e) {
15 System.out.println("Could not write: " + e.getMessage());
16 }
17
18 int errors = 0, total = 0;
19 try (BufferedReader r = Files.newBufferedReader(log)) {
20 String line;
21 while ((line = r.readLine()) != null) { // one line at a time
22 total++;
23 if (line.contains("ERROR")) { errors++; System.out.println("Found: " + line); }
24 }
25 } catch (IOException e) {
26 System.out.println("Could not read: " + e.getMessage());
27 }
28 System.out.println(total + " lines, " + errors + " error(s)");
29 }
30}
Terminal
D:\java-practice> java Main.java
Found: 2026-01-26 10:07 ERROR payment timeout
6 lines, 1 error(s)
💡 Concept in shortFor large files (logs of millions of lines), read one line at a time with BufferedReader.readLine() (returns null at the end) instead of loading everything. try-with-resources closes the file.
Module 18Missing file

Handling a Missing File Gracefully

Main.java  JAVA
1import java.nio.file.*;
2import java.io.IOException;
3
4public class Main {
5 public static void main(String[] args) {
6 Path p = Path.of("settings.txt");
7 String theme;
8 try {
9 theme = Files.readString(p).trim();
10 } catch (NoSuchFileException e) {
11 System.out.println("No settings yet, creating defaults");
12 theme = "light";
13 try {
14 Files.writeString(p, theme);
15 } catch (IOException ex) {
16 System.out.println("Cannot save: " + ex.getMessage());
17 }
18 } catch (IOException e) {
19 theme = "light";
20 }
21 System.out.println("Theme: " + theme);
22 }
23}
Terminal
D:\java-practice> java Main.java
No settings yet, creating defaults
Theme: light
📄 settings.txt (file on disk)TXT
1light
💡 Concept in shortA file might not exist on first run. Catch NoSuchFileException (a kind of IOException) and create defaults. This is how apps create their settings file the first time.
Module 18Date-Time

java.time: Dates and Times Done Right

jshell (Java interactive shell)
// jshell: type Java and see the answer at once. Press Next line.
0 / 9
💡 Concept in shortLocalDate (date), LocalTime (time) and LocalDateTime (both) are immutable: plusDays returns a new date. They know month lengths and leap years for you.
ClassExample
LocalDate2026-01-26
LocalTime09:30
LocalDateTime2026-01-26T08:00
DayOfWeekMONDAY
MonthJANUARY
RememberLocalDate.now() gives today's date. Examples here use fixed dates so the output never changes.
Module 18Differences

Period, Duration and ChronoUnit: Measuring Gaps

Main.java  JAVA
1import java.time.*;
2import java.time.temporal.ChronoUnit;
3
4public class Main {
5 public static void main(String[] args) {
6 LocalDate born = LocalDate.of(2011, 8, 15);
7 LocalDate today = LocalDate.of(2026, 10, 26);
8 Period age = Period.between(born, today);
9 System.out.println("Age: " + age.getYears() + " years " + age.getMonths() + " months " + age.getDays() + " days");
10
11 LocalDate exam = LocalDate.of(2027, 2, 17);
12 System.out.println("Days to board exam: " + ChronoUnit.DAYS.between(today, exam));
13 System.out.println("Weeks to exam: " + ChronoUnit.WEEKS.between(today, exam));
14
15 LocalTime dep = LocalTime.of(6, 5), arr = LocalTime.of(14, 40);
16 Duration trip = Duration.between(dep, arr);
17 System.out.println("Ranchi to Patna: " + trip.toHours() + " h " + trip.toMinutesPart() + " min");
18 }
19}
Terminal
D:\java-practice> java Main.java
Age: 15 years 2 months 11 days
Days to board exam: 114
Weeks to exam: 16
Ranchi to Patna: 8 h 35 min
💡 Concept in shortPeriod = years/months/days between dates. Duration = hours/minutes/seconds between times. ChronoUnit.DAYS.between(a, b) = a single number of units.
Module 18Formatting

DateTimeFormatter: Show and Read Dates Indian Style

Main.java  JAVA
1import java.time.*;
2import java.time.format.DateTimeFormatter;
3
4public class Main {
5 public static void main(String[] args) {
6 LocalDateTime t = LocalDateTime.of(2026, 1, 26, 8, 30);
7 DateTimeFormatter indian = DateTimeFormatter.ofPattern("dd/MM/yyyy");
8 DateTimeFormatter nice = DateTimeFormatter.ofPattern("EEEE, d MMMM yyyy, hh:mm a");
9 System.out.println(t.format(indian));
10 System.out.println(t.format(nice));
11
12 LocalDate typed = LocalDate.parse("15/08/2026", indian); // text -> date
13 System.out.println("Parsed: " + typed + " is a " + typed.getDayOfWeek());
14 System.out.println("ISO default: " + LocalDate.parse("2026-10-02"));
15 }
16}
Terminal
D:\java-practice> java Main.java
26/01/2026
Monday, 26 January 2026, 08:30 AM
Parsed: 2026-08-15 is a SATURDAY
ISO default: 2026-10-02
💡 Concept in shortofPattern letters: dd day, MM month number, MMMM month name, yyyy year, EEEE weekday, hh:mm a 12-hour time. parse converts typed text back into a date.
PatternResult
dd/MM/yyyy26/01/2026
d MMM yy26 Jan 26
HH:mm08:30
hh:mm a08:30 am
Module 18Hands-on

Code Lab: Try It Yourself

Main.java
Output of the starter code
D:\java-practice> java Main.java
Issued : 01 Oct 2026 (Thu)
Due : 15 Oct 2026 (Thu)
Returned: 22 Oct 2026 (Thu)
Late by 7 days, fine Rs 14
Change the code, then run it on your computer: ⬇ Download → open a terminal in that folder → java Main.java. No JDK yet? Open an online Java compiler and paste the code (Copy).
Your tasks
  1. If the due date falls on a Sunday, move it to Monday
  2. Write the receipt to a file receipt.txt
  3. Read the issue date as text "01/10/2026"
  4. Cap the fine at Rs 50
PracticePractice 1 of 10 · Easy

Save a Shopping List

EasyScenario. A family saves its shopping list to a file and reads it back.
💡 Concept used: Files.write with a List, then readAllLines.

Your tasks

  1. Write 4 items with Files.write
  2. Read them back and number them
  3. Print the file size
🏢 Used in real projects: Notes apps and simple tools store lists in files.
ShoppingFile.java  JAVA
1import java.nio.file.*;
2import java.io.IOException;
3import java.util.List;
4
5public class ShoppingFile {
6 public static void main(String[] args) throws IOException {
7 Path p = Path.of("shopping.txt");
8 Files.write(p, List.of("Atta 10kg", "Mustard oil", "Tomatoes 2kg", "Tea pack"));
9 List<String> items = Files.readAllLines(p);
10 for (int i = 0; i < items.size(); i++) System.out.println((i + 1) + ") " + items.get(i));
11 System.out.println("File size: " + Files.size(p) + " bytes");
12 }
13}
PracticePractice 2 of 10 · Easy

Days Until an Event

EasyScenario. A school notice board shows days left until events.
💡 Concept used: ChronoUnit.DAYS.between with fixed dates; getDayOfWeek.

Your tasks

  1. Fixed today = 26 Oct 2026
  2. Events: Chhath, Christmas, Republic Day
  3. Print days left and weekday
🏢 Used in real projects: Countdown widgets and reminder apps.
Countdown.java  JAVA
1import java.time.*;
2import java.time.temporal.ChronoUnit;
3
4public class Countdown {
5 public static void main(String[] args) {
6 LocalDate today = LocalDate.of(2026, 10, 26);
7 String[] names = {"Chhath Puja", "Christmas", "Republic Day"};
8 LocalDate[] dates = {LocalDate.of(2026, 11, 15), LocalDate.of(2026, 12, 25), LocalDate.of(2027, 1, 26)};
9 for (int i = 0; i < names.length; i++) {
10 System.out.printf("%-13s %s (%s) in %d days%n", names[i], dates[i], dates[i].getDayOfWeek(), ChronoUnit.DAYS.between(today, dates[i]));
11 }
12 }
13}
PracticePractice 3 of 10 · Easy

Age Calculator

EasyScenario. A form checks whether an applicant is old enough (18+) on a given date.
💡 Concept used: Period.between gives years, months and days.

Your tasks

  1. Three birth dates
  2. Age on 26 Oct 2026
  3. Eligible if 18 or more
🏢 Used in real projects: Government and bank forms check age eligibility exactly this way.
AgeCheck.java  JAVA
1import java.time.*;
2
3public class AgeCheck {
4 public static void main(String[] args) {
5 LocalDate on = LocalDate.of(2026, 10, 26);
6 String[] n = {"Priya", "Kunal", "Meera"};
7 LocalDate[] born = {LocalDate.of(2008, 10, 26), LocalDate.of(2008, 10, 27), LocalDate.of(1995, 2, 14)};
8 for (int i = 0; i < n.length; i++) {
9 Period p = Period.between(born[i], on);
10 System.out.printf("%-6s %2d y %2d m %2d d -> %s%n", n[i], p.getYears(), p.getMonths(), p.getDays(), p.getYears() >= 18 ? "eligible" : "not eligible");
11 }
12 }
13}
PracticePractice 4 of 10 · Easy

Attendance Log File

EasyScenario. A coaching centre appends attendance lines every day and counts presence per student.
💡 Concept used: APPEND mode to add lines; read and count with a Map.

Your tasks

  1. Append 3 days of attendance lines
  2. Read all lines
  3. Count days present per student
🏢 Used in real projects: Log files are append-only records read later for reports.
AttendanceLog.java  JAVA
1import java.nio.file.*;
2import java.io.IOException;
3import java.util.*;
4
5public class AttendanceLog {
6 public static void main(String[] args) throws IOException {
7 Path p = Path.of("attendance.txt");
8 Files.deleteIfExists(p);
9 String[][] days = {{"2026-10-20", "Aman,Riya,Sana"}, {"2026-10-21", "Aman,Sana"}, {"2026-10-22", "Aman,Riya,Imran"}};
10 for (String[] d : days) {
11 Files.writeString(p, d[0] + ":" + d[1] + "\n", StandardOpenOption.CREATE, StandardOpenOption.APPEND);
12 }
13 Map<String, Integer> present = new TreeMap<>();
14 for (String line : Files.readAllLines(p)) {
15 for (String name : line.split(":")[1].split(",")) present.merge(name, 1, Integer::sum);
16 }
17 System.out.println(present);
18 }
19}
PracticePractice 5 of 10 · Easy

Fee Due Date Calculator

EasyScenario. Monthly fees are due on the 10th; if the 10th is a Sunday, the due date moves to Monday. Late fee Rs 50.
💡 Concept used: withDayOfMonth, getDayOfWeek, plusMonths, isAfter.

Your tasks

  1. Due dates for Nov 2026 - Feb 2027
  2. Shift Sundays to Monday
  3. Check a payment date for late fee
🏢 Used in real projects: School ERP and billing systems calculate due dates and penalties this way.
FeeDue.java  JAVA
1import java.time.*;
2import java.time.format.DateTimeFormatter;
3
4public class FeeDue {
5 static LocalDate due(LocalDate month) {
6 LocalDate d = month.withDayOfMonth(10);
7 return d.getDayOfWeek() == DayOfWeek.SUNDAY ? d.plusDays(1) : d;
8 }
9 public static void main(String[] args) {
10 DateTimeFormatter f = DateTimeFormatter.ofPattern("dd MMM yyyy EEE");
11 LocalDate m = LocalDate.of(2026, 11, 1);
12 LocalDate[] paid = {LocalDate.of(2026, 11, 9), LocalDate.of(2026, 12, 12), LocalDate.of(2027, 1, 11), LocalDate.of(2027, 2, 10)};
13 for (int i = 0; i < 4; i++) {
14 LocalDate d = due(m.plusMonths(i));
15 boolean late = paid[i].isAfter(d);
16 System.out.println("Due " + d.format(f) + " | paid " + paid[i].format(f) + (late ? " | late fee Rs 50" : " | on time"));
17 }
18 }
19}
PracticePractice 6 of 10 · Medium

Marks Report Writer

MediumScenario. Read marks.csv, compute total and grade, and write a neat report.txt.
💡 Concept used: Read CSV -> process -> write formatted text; files param provides the input.

Your tasks

  1. Read marks.csv (name + 3 subjects)
  2. Total, percent and grade
  3. Write aligned report.txt
  4. Print summary to console
📄 marks.csv (file on disk)CSV
1name,maths,science,english
2Aditi,95,91,88
3Rohan,72,68,80
4Sana,88,94,90
5Imran,55,61,70
🏢 Used in real projects: Report generation from spreadsheets is a daily task in schools and offices.
ReportWriter.java  JAVA
1import java.nio.file.*;
2import java.io.IOException;
3import java.util.*;
4
5public class ReportWriter {
6 public static void main(String[] args) throws IOException {
7 List<String> lines = Files.readAllLines(Path.of("marks.csv"));
8 StringBuilder out = new StringBuilder(String.format("%-8s %5s %6s %s%n", "Name", "Total", "Pct", "Grade"));
9 int count = 0;
10 for (String line : lines.subList(1, lines.size())) {
11 String[] f = line.split(",");
12 int t = 0;
13 for (int i = 1; i < f.length; i++) t += Integer.parseInt(f[i].trim());
14 double pct = t / 3.0;
15 String g = pct >= 90 ? "A+" : pct >= 75 ? "A" : pct >= 60 ? "B" : "C";
16 out.append(String.format("%-8s %5d %5.1f%% %s%n", f[0], t, pct, g));
17 count++;
18 }
19 Files.writeString(Path.of("report.txt"), out);
20 System.out.println("Report written for " + count + " students");
21 }
22}
PracticePractice 7 of 10 · Medium

EMI Schedule with Dates

MediumScenario. A bike loan of Rs 85,000 at 10.5% for 6 months: print each EMI date (5th of every month) and remaining balance.
💡 Concept used: plusMonths for schedules, the EMI formula, and formatted dates.

Your tasks

  1. EMI formula
  2. First EMI on 5 Nov 2026
  3. Interest and principal split each month
  4. Write the schedule to emi.csv
🏢 Used in real projects: Loan apps show and download EMI schedules like this.
EmiSchedule.java  JAVA
1import java.nio.file.*;
2import java.io.IOException;
3import java.time.*;
4import java.time.format.DateTimeFormatter;
5
6public class EmiSchedule {
7 public static void main(String[] args) throws IOException {
8 double p = 85000, r = 10.5 / 12 / 100; int n = 6;
9 double emi = p * r * Math.pow(1 + r, n) / (Math.pow(1 + r, n) - 1);
10 DateTimeFormatter f = DateTimeFormatter.ofPattern("dd-MM-yyyy");
11 StringBuilder csv = new StringBuilder("date,emi,interest,principal,balance\n");
12 LocalDate d = LocalDate.of(2026, 11, 5);
13 double bal = p;
14 for (int i = 0; i < n; i++, d = d.plusMonths(1)) {
15 double in = bal * r, pr = emi - in;
16 bal -= pr;
17 System.out.printf("%s EMI %.0f int %6.0f prin %7.0f bal %7.0f%n", d.format(f), emi, in, pr, Math.abs(bal));
18 csv.append(String.format("%s,%.2f,%.2f,%.2f,%.2f%n", d.format(f), emi, in, pr, Math.abs(bal)));
19 }
20 Files.writeString(Path.of("emi.csv"), csv);
21 }
22}
PracticePractice 8 of 10 · Medium

Log File Analyser

MediumScenario. Analyse a server log: count levels, find the busiest hour, and list errors.
💡 Concept used: BufferedReader line by line, parsing timestamps with LocalDateTime.parse, maps for counts.

Your tasks

  1. Read server.log with BufferedReader
  2. Parse "yyyy-MM-dd HH:mm" timestamps
  3. Count INFO/WARN/ERROR
  4. Busiest hour and error list
🏢 Used in real projects: DevOps teams analyse logs to find errors and peak load times.
LogAnalyser.java  JAVA
1import java.io.*;
2import java.nio.file.*;
3import java.time.LocalDateTime;
4import java.time.format.DateTimeFormatter;
5import java.util.*;
6
7public class LogAnalyser {
8 public static void main(String[] args) throws IOException {
9 DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
10 Map<String, Integer> levels = new TreeMap<>();
11 Map<Integer, Integer> perHour = new TreeMap<>();
12 List<String> errors = new ArrayList<>();
13 try (BufferedReader r = Files.newBufferedReader(Path.of("server.log"))) {
14 String line;
15 while ((line = r.readLine()) != null) {
16 LocalDateTime t = LocalDateTime.parse(line.substring(0, 16), f);
17 String level = line.split(" ")[2];
18 levels.merge(level, 1, Integer::sum);
19 perHour.merge(t.getHour(), 1, Integer::sum);
20 if (level.equals("ERROR")) errors.add(t.toLocalTime() + " " + line.substring(23));
21 }
22 }
23 System.out.println("Levels: " + levels);
24 int busy = Collections.max(perHour.entrySet(), Map.Entry.comparingByValue()).getKey();
25 System.out.println("Busiest hour: " + busy + ":00 (" + perHour.get(busy) + " lines)");
26 errors.forEach(e -> System.out.println("ERROR at " + e));
27 }
28}
PracticePractice 9 of 10 · Hard

Library Due Tracker with File Storage

HardScenario. A library saves issued books in a CSV; on each run it loads the file, finds overdue books on a given date, calculates fines, and saves an updated file.
💡 Concept used: A full load-process-save cycle: parse dates from CSV, ChronoUnit for overdue days, records, and rewriting the file.

Your tasks

  1. Load issued.csv (member,book,issueDate dd-MM-yyyy)
  2. Due = issue + 14 days; today = 26-10-2026
  3. Fine Rs 2/day (max Rs 60)
  4. Write overdue.csv and print a summary
🏢 Used in real projects: Library and rental systems run exactly this daily job (in real life on a database).
DueTracker.java  JAVA
1import java.nio.file.*;
2import java.io.IOException;
3import java.time.*;
4import java.time.format.DateTimeFormatter;
5import java.time.temporal.ChronoUnit;
6import java.util.*;
7
8record Issue(String member, String book, LocalDate issued) {
9 LocalDate due() { return issued.plusDays(14); }
10}
11
12public class DueTracker {
13 public static void main(String[] args) throws IOException {
14 DateTimeFormatter f = DateTimeFormatter.ofPattern("dd-MM-yyyy");
15 LocalDate today = LocalDate.of(2026, 10, 26);
16 List<Issue> issues = new ArrayList<>();
17 for (String l : Files.readAllLines(Path.of("issued.csv"))) {
18 String[] p = l.split(",");
19 issues.add(new Issue(p[0], p[1], LocalDate.parse(p[2], f)));
20 }
21 StringBuilder out = new StringBuilder("member,book,due,daysLate,fine\n");
22 int totalFine = 0;
23 for (Issue i : issues) {
24 long late = ChronoUnit.DAYS.between(i.due(), today);
25 if (late > 0) {
26 int fine = (int) Math.min(late * 2, 60);
27 totalFine += fine;
28 out.append(String.join(",", i.member(), i.book(), i.due().format(f), String.valueOf(late), String.valueOf(fine))).append("\n");
29 System.out.printf("%-6s %-16s due %s, %2d days late, fine Rs %d%n", i.member(), i.book(), i.due().format(f), late, fine);
30 }
31 }
32 Files.writeString(Path.of("overdue.csv"), out);
33 System.out.println("Total fines: Rs " + totalFine);
34 }
35}
PracticePractice 10 of 10 · Hard

Shop Opening Hours Checker

HardScenario. A delivery app checks whether a shop is open at a given date-time, handling a lunch break, weekly off-day and holidays from a file.
💡 Concept used: LocalTime ranges with isBefore/isAfter, DayOfWeek, a holiday file parsed into a Set<LocalDate>.

Your tasks

  1. Hours 09:00-13:00 and 14:00-20:30, closed Tuesday
  2. holidays.txt with dates
  3. isOpen(LocalDateTime)
  4. Check 6 order times
🏢 Used in real projects: Delivery and booking apps check business hours and holidays before accepting orders.
OpenHours.java  JAVA
1import java.nio.file.*;
2import java.io.IOException;
3import java.time.*;
4import java.util.*;
5
6public class OpenHours {
7 static Set<LocalDate> holidays = new HashSet<>();
8 static boolean between(LocalTime t, String a, String b) { return !t.isBefore(LocalTime.parse(a)) && t.isBefore(LocalTime.parse(b)); }
9 static String status(LocalDateTime dt) {
10 if (holidays.contains(dt.toLocalDate())) return "closed (holiday)";
11 if (dt.getDayOfWeek() == DayOfWeek.TUESDAY) return "closed (weekly off)";
12 LocalTime t = dt.toLocalTime();
13 if (between(t, "09:00", "13:00") || between(t, "14:00", "20:30")) return "OPEN";
14 return between(t, "13:00", "14:00") ? "lunch break" : "closed (outside hours)";
15 }
16 public static void main(String[] args) throws IOException {
17 for (String s : Files.readAllLines(Path.of("holidays.txt"))) holidays.add(LocalDate.parse(s.trim()));
18 String[] orders = {"2026-10-26T10:15", "2026-10-26T13:20", "2026-10-27T11:00", "2026-11-08T12:00", "2026-10-29T20:45", "2026-10-30T19:59"};
19 for (String o : orders) {
20 LocalDateTime dt = LocalDateTime.parse(o);
21 System.out.printf("%s %-9s -> %s%n", o.replace('T', ' '), dt.getDayOfWeek(), status(dt));
22 }
23 }
24}
Module 18Recap

Module 18 Recap & Quiz

What you learned

  • 1Path.of("f.txt") + Files.writeString / readString / readAllLines / write
  • 2APPEND option adds to the end; file methods throw IOException
  • 3CSV: read lines, skip header, split(","), parse; write with StringBuilder
  • 4BufferedReader.readLine() for big files; try-with-resources
  • 5LocalDate / LocalTime / LocalDateTime are immutable: plusDays returns a new date
  • 6Period, Duration, ChronoUnit.DAYS.between; DateTimeFormatter.ofPattern("dd/MM/yyyy")

Quick quiz

Files.writeString(p, "x") on an existing file...

Use APPEND to add.

Gap in days between two dates?

ChronoUnit gives a number of days.

date.plusDays(5)...

java.time objects are immutable.
MODULE 19

Multithreading and Concurrency

⏱ 4.5 hours10 practice examplesStep-through animationsCode Lab

You will learn

  • Threads: doing several things at once
  • Creating threads: Runnable, lambdas, start(), join()
  • Race conditions and synchronized
  • ExecutorService thread pools and Future results
  • Thread-safe tools: AtomicInteger, ConcurrentHashMap, CompletableFuture

You will build

  • Parallel file downloads
  • A ticket booking counter without double booking
  • A parallel price checker
Threadnew Thread(task).start()synchronized
Module 19Real-world use

Where Is This Used in Real Projects?

threadswork in parallel⬇Downloadsmany files at once🎮Gamesgraphics + sound + input🌐Web serversone thread per request📱AndroidUI vs background work🎫Bookingmany users, same seat⏳Schedulersrun every hour

Real projects that use this

  • A web server like Tomcat serves thousands of users at once using thread pools
  • Android apps download data on a background thread so the screen never freezes
  • Ticket sites must stop two people booking the same seat at the same moment
  • Modern CPUs have many cores; threads use them all
Module 19The idea

A Thread Is an Independent Path of Execution

time →Single threaddownloadresizeuploadThread 1download AThread 2download BThread 3download C3 downloads together finish in 1/3 of the time
💡 Concept in shortEvery Java program starts with one thread: main. You can start more threads that run at the same time (on different CPU cores, or by quickly taking turns). Waiting tasks (downloads, file reads) gain the most.
WordMeaning
threadone path of execution
concurrenttasks overlap in time
paralleltruly at the same instant, on many cores
race conditionthreads clash on shared data
Module 19Create threads

Creating and Starting Threads

Main.java  JAVA
1public class Main {
2 public static void main(String[] args) throws InterruptedException {
3 Runnable download = () -> {
4 String name = Thread.currentThread().getName();
5 for (int part = 1; part <= 3; part++) {
6 try { Thread.sleep(100); } catch (InterruptedException e) { return; }
7 }
8 System.out.println(name + " finished");
9 };
10 long start = System.currentTimeMillis();
11 Thread t1 = new Thread(download, "video.mp4");
12 Thread t2 = new Thread(download, "notes.pdf");
13 Thread t3 = new Thread(download, "song.mp3");
14 t1.start(); t2.start(); t3.start(); // run at the same time
15 t1.join(); t2.join(); t3.join(); // wait for all to finish
16 long ms = System.currentTimeMillis() - start;
17 System.out.println("All done in about " + (ms / 100 * 100) + " ms (not 900 ms)");
18 }
19}
Terminal
D:\java-practice> java Main.java
video.mp4 finished
notes.pdf finished
song.mp3 finished
All done in about 300 ms (not 900 ms)
💡 Concept in shortPut the work in a Runnable (a lambda), wrap it in a Thread, and call start(). join() makes main wait until that thread ends. Thread.sleep(ms) pauses the current thread.
Common mistakeCall start(), not run(). run() executes on the current thread, with no parallelism.
RememberThe finishing order of threads can change each run: that is normal.
Module 19Race condition

The Danger: Race Conditions on Shared Data

Main.java  JAVA
1public class Main {
2 static int unsafeCount = 0;
3 static int safeCount = 0;
4
5 static synchronized void safeIncrement() { safeCount++; }
6
7 public static void main(String[] args) throws InterruptedException {
8 Runnable work = () -> {
9 for (int i = 0; i < 1_000_000; i++) {
10 unsafeCount++; // read, add, write: can be interrupted!
11 safeIncrement(); // one thread at a time
12 }
13 };
14 Thread a = new Thread(work), b = new Thread(work);
15 a.start(); b.start();
16 a.join(); b.join();
17 System.out.println("Expected : 2000000");
18 System.out.println("Unsafe : " + unsafeCount + " (lost updates! changes every run)");
19 System.out.println("Synchronized: " + safeCount);
20 }
21}
Terminal
D:\java-practice> java Main.java
Expected : 2000000
Unsafe : 1990356 (lost updates! changes every run)
Synchronized: 2000000
💡 Concept in shortcount++ is really three steps (read, add, write). Two threads can read the same old value and one update is lost. synchronized lets only one thread at a time run that method, so no update is lost.
Common mistakeRace conditions are the classic multithreading bug: rare, random and hard to reproduce.
Module 19Animation

How an Update Gets Lost

count = 5. Thread A reads count: sees 5.
Thread B also reads count before A writes: sees 5.
Thread A adds 1 and writes 6.
Thread B adds 1 to its old copy (5) and writes 6.
Two increments happened, but count is 6, not 7. One update was lost.
With synchronized, B must wait until A finishes; B then reads 6 and writes 7.
1 / 6
💡 Concept in shortThreads share heap objects and static fields. Any data that more than one thread changes must be protected: synchronized, locks, or atomic classes.
ToolUse
synchronized method/blockone thread at a time
AtomicIntegersafe counters
ConcurrentHashMapsafe shared map
immutable objectsnothing to protect
Module 19Executor

ExecutorService: A Pool of Worker Threads

Main.java  JAVA
1import java.util.*;
2import java.util.concurrent.*;
3
4public class Main {
5 static int checkPrice(String shop) throws InterruptedException {
6 Thread.sleep(150); // pretend network call
7 return switch (shop) { case "ShopA" -> 1499; case "ShopB" -> 1399; default -> 1450; };
8 }
9
10 public static void main(String[] args) throws Exception {
11 ExecutorService pool = Executors.newFixedThreadPool(3);
12 List<String> shops = List.of("ShopA", "ShopB", "ShopC");
13 Map<String, Future<Integer>> results = new LinkedHashMap<>();
14 for (String s : shops) {
15 results.put(s, pool.submit(() -> checkPrice(s))); // returns at once
16 }
17 int best = Integer.MAX_VALUE; String where = "";
18 for (var e : results.entrySet()) {
19 int price = e.getValue().get(); // waits for that result
20 System.out.println(e.getKey() + ": Rs " + price);
21 if (price < best) { best = price; where = e.getKey(); }
22 }
23 pool.shutdown();
24 System.out.println("Cheapest: " + where + " Rs " + best);
25 }
26}
Terminal
D:\java-practice> java Main.java
ShopA: Rs 1499
ShopB: Rs 1399
ShopC: Rs 1450
Cheapest: ShopB Rs 1399
💡 Concept in shortCreating threads by hand is costly. An ExecutorService keeps a pool of reusable threads. submit(task) returns a Future; future.get() waits for and returns the result. Always shutdown() the pool.
RememberReal servers (Tomcat, Spring) handle each web request on a pool thread.
Module 19Thread-safe tools

AtomicInteger and ConcurrentHashMap

Main.java  JAVA
1import java.util.*;
2import java.util.concurrent.*;
3import java.util.concurrent.atomic.AtomicInteger;
4
5public class Main {
6 public static void main(String[] args) throws InterruptedException {
7 AtomicInteger visits = new AtomicInteger();
8 Map<String, Integer> perPage = new ConcurrentHashMap<>();
9 String[] pages = {"/home", "/results", "/home", "/admit-card", "/results", "/home"};
10
11 ExecutorService pool = Executors.newFixedThreadPool(4);
12 for (int user = 0; user < 1000; user++) {
13 String page = pages[user % pages.length];
14 pool.submit(() -> {
15 visits.incrementAndGet(); // atomic ++
16 perPage.merge(page, 1, Integer::sum); // atomic per key
17 });
18 }
19 pool.shutdown();
20 pool.awaitTermination(5, TimeUnit.SECONDS);
21 System.out.println("Total visits: " + visits.get());
22 System.out.println("Per page: " + new TreeMap<>(perPage));
23 }
24}
Terminal
D:\java-practice> java Main.java
Total visits: 1000
Per page: {/admit-card=167, /home=500, /results=333}
💡 Concept in shortAtomicInteger gives lock-free safe counters. ConcurrentHashMap lets many threads update a map safely (a normal HashMap can corrupt). awaitTermination waits for all submitted tasks.
Module 19CompletableFuture

CompletableFuture: Chain Async Steps

Main.java  JAVA
1import java.util.concurrent.*;
2
3public class Main {
4 static void pause(int ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { } }
5
6 public static void main(String[] args) {
7 CompletableFuture<Double> fare = CompletableFuture.supplyAsync(() -> { pause(200); return 2850.0; });
8 CompletableFuture<Double> meal = CompletableFuture.supplyAsync(() -> { pause(150); return 350.0; });
9
10 CompletableFuture<String> bill = fare
11 .thenCombine(meal, Double::sum) // when both are ready
12 .thenApply(total -> total * 1.05) // add 5% GST
13 .thenApply(t -> String.format("Ticket + meal: Rs %.2f", t));
14
15 System.out.println("Main thread is free while prices load...");
16 System.out.println(bill.join());
17 }
18}
Terminal
D:\java-practice> java Main.java
Main thread is free while prices load...
Ticket + meal: Rs 3360.00
💡 Concept in shortCompletableFuture runs tasks in the background and lets you chain what happens next: thenApply (transform), thenCombine (join two results), exceptionally (handle errors). join() waits for the final value.
Module 19Hands-on

Code Lab: Try It Yourself

Main.java
Output of the starter code
D:\java-practice> java Main.java
Ranchi: 38 C
Jamshedpur: 50 C
Dhanbad: 41 C
Bokaro: 38 C
Change the code, then run it on your computer: ⬇ Download → open a terminal in that folder → java Main.java. No JDK yet? Open an online Java compiler and paste the code (Copy).
Your tasks
  1. Print which pool thread ran each job (Thread.currentThread().getName())
  2. Measure total time vs 4 x 100 ms
  3. Change the pool size to 1 and compare
  4. Make one job throw an exception and handle it in get()
PracticePractice 1 of 10 · Easy

Two Threads Counting

EasyScenario. See two threads run side by side.
💡 Concept used: Thread with a lambda, start and join; thread names.

Your tasks

  1. Two threads print 1-3 with their name
  2. sleep between prints
  3. join both and print "done"
🏢 Used in real projects: Background tasks in apps run on separate threads.
TwoThreads.java  JAVA
1public class TwoThreads {
2 public static void main(String[] args) throws InterruptedException {
3 Runnable r = () -> {
4 for (int i = 1; i <= 3; i++) {
5 System.out.println(Thread.currentThread().getName() + " -> " + i);
6 try { Thread.sleep(50); } catch (InterruptedException e) { }
7 }
8 };
9 Thread a = new Thread(r, "Alpha"), b = new Thread(r, "Beta");
10 a.start(); b.start();
11 a.join(); b.join();
12 System.out.println("Both threads done");
13 }
14}
PracticePractice 2 of 10 · Easy

Countdown Timer Thread

EasyScenario. An exam app shows a countdown while the main thread keeps working.
💡 Concept used: A background thread with sleep; main continues and then joins.

Your tasks

  1. Timer thread counts 5 to 1
  2. Main prints "loading questions" meanwhile
  3. join and print "Start!"
🏢 Used in real projects: UI timers and progress indicators run on separate threads.
Countdown.java  JAVA
1public class Countdown {
2 public static void main(String[] args) throws InterruptedException {
3 Thread timer = new Thread(() -> {
4 for (int s = 5; s >= 1; s--) {
5 System.out.println(" timer: " + s);
6 try { Thread.sleep(120); } catch (InterruptedException e) { return; }
7 }
8 });
9 timer.start();
10 Thread.sleep(30);
11 System.out.println("main: loading questions...");
12 timer.join();
13 System.out.println("Start the exam!");
14 }
15}
PracticePractice 3 of 10 · Easy

Synchronized Bank Balance

EasyScenario. Two ATMs deposit into the same account at the same time.
💡 Concept used: A synchronized method prevents lost updates.

Your tasks

  1. Account with synchronized deposit
  2. Two threads deposit Rs 1, 10000 times each
  3. Balance must be exactly 20000
🏢 Used in real projects: Banking systems lock accounts during updates.
SharedAccount.java  JAVA
1class Account {
2 private int balance = 0;
3 synchronized void deposit(int amt) { balance += amt; }
4 int getBalance() { return balance; }
5}
6
7public class SharedAccount {
8 public static void main(String[] args) throws InterruptedException {
9 Account acc = new Account();
10 Runnable atm = () -> { for (int i = 0; i < 10000; i++) acc.deposit(1); };
11 Thread a = new Thread(atm), b = new Thread(atm);
12 a.start(); b.start();
13 a.join(); b.join();
14 System.out.println("Final balance: " + acc.getBalance());
15 }
16}
PracticePractice 4 of 10 · Easy

Parallel Sum with a Thread Pool

EasyScenario. Split a big sum into 4 parts computed in parallel.
💡 Concept used: Callable tasks returning results via Future.

Your tasks

  1. Sum 1..4,000,000 in 4 chunks
  2. submit each chunk to a pool of 4
  3. Add the four results
  4. Compare with the formula n(n+1)/2
🏢 Used in real projects: Big data jobs split work into chunks processed on many cores.
ParallelSum.java  JAVA
1import java.util.*;
2import java.util.concurrent.*;
3
4public class ParallelSum {
5 public static void main(String[] args) throws Exception {
6 long n = 4_000_000;
7 ExecutorService pool = Executors.newFixedThreadPool(4);
8 List<Future<Long>> parts = new ArrayList<>();
9 for (int k = 0; k < 4; k++) {
10 long from = k * n / 4 + 1, to = (k + 1) * n / 4;
11 parts.add(pool.submit(() -> { long s = 0; for (long i = from; i <= to; i++) s += i; return s; }));
12 }
13 long total = 0;
14 for (Future<Long> f : parts) total += f.get();
15 pool.shutdown();
16 System.out.println("Parallel sum: " + total);
17 System.out.println("Formula : " + n * (n + 1) / 2);
18 }
19}
PracticePractice 5 of 10 · Easy

Website Hit Counter

EasyScenario. 1000 visitors hit pages at once; count per page safely.
💡 Concept used: AtomicInteger and ConcurrentHashMap with an executor.

Your tasks

  1. Pool of 8 threads
  2. AtomicInteger total
  3. ConcurrentHashMap per page with merge
  4. Print sorted results
🏢 Used in real projects: Analytics counters on busy websites.
HitCounter.java  JAVA
1import java.util.*;
2import java.util.concurrent.*;
3import java.util.concurrent.atomic.AtomicInteger;
4
5public class HitCounter {
6 public static void main(String[] args) throws InterruptedException {
7 String[] pages = {"/", "/courses", "/java", "/contact"};
8 AtomicInteger total = new AtomicInteger();
9 ConcurrentHashMap<String, Integer> per = new ConcurrentHashMap<>();
10 ExecutorService pool = Executors.newFixedThreadPool(8);
11 for (int i = 0; i < 1000; i++) {
12 String p = pages[(i * 7) % 4];
13 pool.submit(() -> { total.incrementAndGet(); per.merge(p, 1, Integer::sum); });
14 }
15 pool.shutdown();
16 pool.awaitTermination(5, TimeUnit.SECONDS);
17 System.out.println("Total: " + total.get() + " " + new TreeMap<>(per));
18 }
19}
PracticePractice 6 of 10 · Medium

Ticket Booking Without Double Booking

MediumScenario. 5 users try to book the last 3 seats of a Ranchi-Delhi flight at the same time.
💡 Concept used: A synchronized method checks and updates seats as one atomic step.

Your tasks

  1. Flight with seatsLeft = 3
  2. synchronized boolean book(name)
  3. 5 threads try to book
  4. Exactly 3 succeed; print confirmed list sorted
🏢 Used in real projects: IRCTC/airline systems must make "check seat and book" one atomic step.
Booking.java  JAVA
1import java.util.*;
2
3class Flight {
4 private int seatsLeft = 3;
5 private final List<String> confirmed = new ArrayList<>();
6 synchronized boolean book(String name) {
7 if (seatsLeft == 0) return false;
8 try { Thread.sleep(10); } catch (InterruptedException e) { } // slow payment step
9 seatsLeft--;
10 confirmed.add(name);
11 return true;
12 }
13 synchronized List<String> confirmed() { return new ArrayList<>(confirmed); }
14}
15
16public class Booking {
17 public static void main(String[] args) throws InterruptedException {
18 Flight f = new Flight();
19 List<Thread> users = new ArrayList<>();
20 for (String n : List.of("Asha", "Bunty", "Chinu", "Deepa", "Elvis")) users.add(new Thread(() -> f.book(n)));
21 for (Thread t : users) t.start();
22 for (Thread t : users) t.join();
23 List<String> c = f.confirmed();
24 Collections.sort(c);
25 System.out.println("Confirmed (" + c.size() + "): " + c);
26 System.out.println("Seats never oversold: " + (c.size() == 3));
27 }
28}
PracticePractice 7 of 10 · Medium

Producer-Consumer Order Queue

MediumScenario. A restaurant: a waiter thread adds orders, a chef thread cooks them, using a BlockingQueue.
💡 Concept used: BlockingQueue put/take coordinate two threads safely; a "poison pill" ends the consumer.

Your tasks

  1. ArrayBlockingQueue of capacity 2
  2. Waiter puts 5 orders then "STOP"
  3. Chef takes and cooks until STOP
  4. Print the cooked count
🏢 Used in real projects: Message queues (Kafka, RabbitMQ) follow the producer-consumer pattern.
Kitchen.java  JAVA
1import java.util.concurrent.*;
2
3public class Kitchen {
4 public static void main(String[] args) throws InterruptedException {
5 BlockingQueue<String> q = new ArrayBlockingQueue<>(2);
6 String[] orders = {"Litti Chokha", "Dhuska", "Rugra curry", "Thekua", "Pittha"};
7 Thread waiter = new Thread(() -> {
8 try {
9 for (String o : orders) q.put(o); // waits if queue is full
10 q.put("STOP");
11 } catch (InterruptedException e) { }
12 });
13 int[] cooked = {0};
14 Thread chef = new Thread(() -> {
15 try {
16 while (true) {
17 String o = q.take(); // waits if queue is empty
18 if (o.equals("STOP")) break;
19 Thread.sleep(30);
20 cooked[0]++;
21 System.out.println("Chef cooked " + o);
22 }
23 } catch (InterruptedException e) { }
24 });
25 waiter.start(); chef.start();
26 waiter.join(); chef.join();
27 System.out.println("Orders cooked: " + cooked[0]);
28 }
29}
PracticePractice 8 of 10 · Medium

Parallel Price Comparison with CompletableFuture

MediumScenario. A travel app checks 3 bus operators in parallel, ignores one that fails, and shows the cheapest.
💡 Concept used: supplyAsync, exceptionally for errors, allOf to wait, and streams to pick the best.

Your tasks

  1. 3 async price checks (one throws)
  2. exceptionally returns -1 for failures
  3. allOf(...).join()
  4. Pick the cheapest valid fare
🏢 Used in real projects: Travel aggregators query many providers in parallel and tolerate failures.
BusFares.java  JAVA
1import java.util.*;
2import java.util.concurrent.*;
3
4public class BusFares {
5 static int fetch(String op, int ms, int fare) {
6 try { Thread.sleep(ms); } catch (InterruptedException e) { }
7 if (fare < 0) throw new IllegalStateException(op + " server down");
8 return fare;
9 }
10 public static void main(String[] args) {
11 Map<String, CompletableFuture<Integer>> calls = new LinkedHashMap<>();
12 calls.put("JharkhandTravels", CompletableFuture.supplyAsync(() -> fetch("JharkhandTravels", 120, 650)));
13 calls.put("RedLine", CompletableFuture.supplyAsync(() -> fetch("RedLine", 80, -1)));
14 calls.put("Shatabdi Bus", CompletableFuture.supplyAsync(() -> fetch("Shatabdi Bus", 150, 590)));
15 calls.replaceAll((op, f) -> f.exceptionally(ex -> { System.out.println("skip: " + ex.getCause().getMessage()); return -1; }));
16 CompletableFuture.allOf(calls.values().toArray(new CompletableFuture[0])).join();
17 String best = null; int min = Integer.MAX_VALUE;
18 for (var e : calls.entrySet()) {
19 int f = e.getValue().join();
20 System.out.println(e.getKey() + ": " + (f < 0 ? "n/a" : "Rs " + f));
21 if (f > 0 && f < min) { min = f; best = e.getKey(); }
22 }
23 System.out.println("Cheapest Ranchi-Patna: " + best + " Rs " + min);
24 }
25}
PracticePractice 9 of 10 · Hard

Multi-Threaded File Word Counter

HardScenario. Count words in several text files at once and merge the totals.
💡 Concept used: One task per file in a pool, each building a local map; merge results in main (no shared mutable state).

Your tasks

  1. 3 input files
  2. Callable per file returns Map<String, Integer>
  3. invokeAll and merge maps
  4. Print top 5 words
🏢 Used in real projects: Search indexers and log processors count across many files in parallel.
ParallelWords.java  JAVA
1import java.nio.file.*;
2import java.util.*;
3import java.util.concurrent.*;
4
5public class ParallelWords {
6 static Map<String, Integer> count(Path p) throws Exception {
7 Map<String, Integer> m = new HashMap<>();
8 for (String w : Files.readString(p).toLowerCase().split("\\W+")) if (w.length() > 2) m.merge(w, 1, Integer::sum);
9 return m;
10 }
11 public static void main(String[] args) throws Exception {
12 List<Path> files = List.of(Path.of("a.txt"), Path.of("b.txt"), Path.of("c.txt"));
13 ExecutorService pool = Executors.newFixedThreadPool(3);
14 List<Callable<Map<String, Integer>>> jobs = new ArrayList<>();
15 for (Path p : files) jobs.add(() -> count(p));
16 Map<String, Integer> total = new TreeMap<>();
17 for (Future<Map<String, Integer>> f : pool.invokeAll(jobs)) f.get().forEach((k, v) -> total.merge(k, v, Integer::sum));
18 pool.shutdown();
19 total.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey()))
20 .limit(5).forEach(e -> System.out.println(e.getKey() + " " + e.getValue()));
21 }
22}
PracticePractice 10 of 10 · Hard

Rate-Limited Downloader

HardScenario. Download 6 files with at most 2 at a time (a Semaphore), report progress safely, and measure total time.
💡 Concept used: Semaphore limits concurrency; AtomicInteger for progress; CountDownLatch to wait for all.

Your tasks

  1. Semaphore(2) permits
  2. Each download acquires, sleeps, releases
  3. AtomicInteger completed counter
  4. CountDownLatch(6) to wait
🏢 Used in real projects: APIs limit parallel requests; download managers use semaphores to respect limits.
Downloader.java  JAVA
1import java.util.concurrent.*;
2import java.util.concurrent.atomic.AtomicInteger;
3
4public class Downloader {
5 public static void main(String[] args) throws InterruptedException {
6 Semaphore slots = new Semaphore(2);
7 AtomicInteger done = new AtomicInteger();
8 AtomicInteger maxActive = new AtomicInteger(), active = new AtomicInteger();
9 CountDownLatch latch = new CountDownLatch(6);
10 ExecutorService pool = Executors.newFixedThreadPool(6);
11 long t0 = System.currentTimeMillis();
12 for (int i = 1; i <= 6; i++) {
13 int id = i;
14 pool.submit(() -> {
15 try {
16 slots.acquire();
17 maxActive.accumulateAndGet(active.incrementAndGet(), Math::max);
18 Thread.sleep(100);
19 active.decrementAndGet();
20 slots.release();
21 done.incrementAndGet();
22 } catch (InterruptedException e) { }
23 latch.countDown();
24 });
25 }
26 latch.await();
27 pool.shutdown();
28 long ms = (System.currentTimeMillis() - t0) / 100 * 100;
29 System.out.println("Downloaded " + done.get() + " files, never more than " + maxActive.get() + " at once");
30 System.out.println("Time about " + ms + " ms (6 files / 2 slots x 100 ms)");
31 }
32}
Module 19Recap

Module 19 Recap & Quiz

What you learned

  • 1Thread = independent path of execution; main is the first thread
  • 2new Thread(runnable).start(); join() waits; never call run() directly
  • 3Shared data + threads = race conditions; protect with synchronized
  • 4ExecutorService pools: submit, Future.get, invokeAll, shutdown
  • 5AtomicInteger, ConcurrentHashMap, BlockingQueue, Semaphore, CountDownLatch
  • 6CompletableFuture: supplyAsync, thenApply, thenCombine, exceptionally

Quick quiz

Which method actually starts a new thread?

start() creates a new thread that calls run().

count++ from two threads without protection can...

It is read-add-write, not atomic.

future.get()...

It blocks until the task finishes.
MODULE 20

JDBC Databases and Modern Java

⏱ 5 hours10 practice examplesStep-through animationsCode Lab

You will learn

  • JDBC: connecting Java to a database
  • CRUD with PreparedStatement and ResultSet
  • Transactions and the DAO pattern
  • Modern Java: var, text blocks, switch expressions, sealed types, pattern matching
  • Build tools and testing: Maven, Gradle and JUnit (overview)

You will build

  • A student records database
  • A bank transfer with a transaction
  • A shape and payment model with sealed types
SQLPreparedStatementsealed interface
Module 20Real-world use

Where Is This Used in Real Projects?

JDBCdata that scales🏫School ERPstudents table🏦Bankingaccounts, transactions🛒E-commerceorders, products🏥Hospitalpatients, visits🌱Spring BootJPA builds on JDBC📱AndroidSQLite on phone

Real projects that use this

  • Real apps keep data in databases (MySQL, PostgreSQL, Oracle, SQLite), not text files
  • Every Java database library (Hibernate, Spring Data) uses JDBC underneath
  • PreparedStatement protects against SQL injection, the most common web attack
  • Modern Java features make business code shorter and safer
Module 20The idea

JDBC: How Java Talks to a Database

Java appyour codeJDBC APIjava.sqlDriversqlite / mysql jarDatabaseSQLResultSet (rows)
  1. 1
    Add the driver jar (here sqlite-jdbc.jar; MySQL uses mysql-connector-j)
  2. 2
    Open a Connection with a URL: jdbc:sqlite:school.db
  3. 3
    Send SQL with a PreparedStatement
  4. 4
    Read rows from the ResultSet
  5. 5
    Close everything (try-with-resources)
💡 Concept in shortJDBC (Java Database Connectivity) is a standard set of interfaces in java.sql. Each database vendor supplies a driver that implements them, so the same Java code works with SQLite, MySQL or Oracle.
InterfaceRole
Connectionsession with the database
PreparedStatementSQL with ? placeholders
ResultSetrows returned by SELECT
SQLExceptionchecked database error
RememberRun with the driver on the classpath: java -cp ".;sqlite-jdbc.jar" Main.java (Windows). We use SQLite because it needs no server.
Module 20Create and insert

Create a Table and Insert Rows

Main.java  JAVA
1import java.sql.*;
2
3public class Main {
4 public static void main(String[] args) throws SQLException {
5 try (Connection con = DriverManager.getConnection("jdbc:sqlite:school.db");
6 Statement st = con.createStatement()) {
7 st.executeUpdate("DROP TABLE IF EXISTS student");
8 st.executeUpdate("CREATE TABLE student (roll INTEGER PRIMARY KEY, name TEXT, city TEXT, marks INTEGER)");
9
10 String sql = "INSERT INTO student (roll, name, city, marks) VALUES (?, ?, ?, ?)";
11 try (PreparedStatement ps = con.prepareStatement(sql)) {
12 Object[][] rows = {{1, "Aditi", "Ranchi", 91}, {2, "Rohan", "Dhanbad", 76}, {3, "Sana", "Ranchi", 88}, {4, "Imran", "Bokaro", 64}};
13 for (Object[] r : rows) {
14 ps.setInt(1, (Integer) r[0]);
15 ps.setString(2, (String) r[1]);
16 ps.setString(3, (String) r[2]);
17 ps.setInt(4, (Integer) r[3]);
18 ps.executeUpdate();
19 }
20 }
21 ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM student");
22 System.out.println("Rows in table: " + rs.getInt(1));
23 }
24 }
25}
Terminal
D:\java-practice> java Main.java
Rows in table: 4
📄 school.db (file on disk)TXT
1(SQLite database file, 8192 bytes)
💡 Concept in shortexecuteUpdate runs CREATE/INSERT/UPDATE/DELETE. A PreparedStatement has ? placeholders filled with setInt/setString (numbered from 1). The table now lives in the file school.db.
Common mistakeNever build SQL by joining strings with user input: "... WHERE name='" + input + "'" allows SQL injection. Always use ?.
Module 20Read

SELECT: Reading Rows with ResultSet

Main.java  JAVA
1import java.sql.*;
2
3public class Main {
4 public static void main(String[] args) throws Exception {
5 Setup.main(args); // creates and fills school.db
6 String sql = "SELECT roll, name, marks FROM student WHERE city = ? AND marks >= ? ORDER BY marks DESC";
7 try (Connection con = DriverManager.getConnection("jdbc:sqlite:school.db");
8 PreparedStatement ps = con.prepareStatement(sql)) {
9 ps.setString(1, "Ranchi");
10 ps.setInt(2, 60);
11 try (ResultSet rs = ps.executeQuery()) {
12 while (rs.next()) { // move to the next row
13 System.out.printf("%d %-6s %d%n", rs.getInt("roll"), rs.getString("name"), rs.getInt("marks"));
14 }
15 }
16 ResultSet avg = con.createStatement().executeQuery("SELECT city, AVG(marks) FROM student GROUP BY city");
17 while (avg.next()) System.out.printf("%-8s avg %.1f%n", avg.getString(1), avg.getDouble(2));
18 }
19 }
20}
Terminal
D:\java-practice> javac *.java && java Main
1 Aditi 91
3 Sana 88
Bokaro avg 64.0
Dhanbad avg 76.0
Ranchi avg 89.5
💡 Concept in shortexecuteQuery returns a ResultSet, a cursor over rows. rs.next() moves to the next row (false at the end); read columns with getInt("roll"), getString(2), getDouble.
RememberSetup.java (in the same folder) just creates the sample table so this example runs on its own.
Module 20Update and delete

UPDATE, DELETE and Transactions

Main.java  JAVA
1import java.sql.*;
2
3public class Main {
4 public static void main(String[] args) throws Exception {
5 try (Connection con = DriverManager.getConnection("jdbc:sqlite:bank.db")) {
6 Statement st = con.createStatement();
7 st.executeUpdate("DROP TABLE IF EXISTS account");
8 st.executeUpdate("CREATE TABLE account (id TEXT PRIMARY KEY, balance INTEGER CHECK (balance >= 0))");
9 st.executeUpdate("INSERT INTO account VALUES ('RAVI', 5000), ('MEENA', 1000)");
10 transfer(con, "RAVI", "MEENA", 3000);
11 transfer(con, "RAVI", "MEENA", 4000); // would make RAVI negative
12 ResultSet rs = st.executeQuery("SELECT * FROM account");
13 while (rs.next()) System.out.println(rs.getString(1) + ": " + rs.getInt(2));
14 }
15 }
16
17 static void transfer(Connection con, String from, String to, int amt) throws SQLException {
18 con.setAutoCommit(false); // start transaction
19 try (PreparedStatement debit = con.prepareStatement("UPDATE account SET balance = balance - ? WHERE id = ?");
20 PreparedStatement credit = con.prepareStatement("UPDATE account SET balance = balance + ? WHERE id = ?")) {
21 credit.setInt(1, amt); credit.setString(2, to); credit.executeUpdate();
22 debit.setInt(1, amt); debit.setString(2, from); debit.executeUpdate();
23 con.commit(); // both succeed together
24 System.out.println("Transferred " + amt);
25 } catch (SQLException e) {
26 con.rollback(); // undo the credit too
27 System.out.println("Transfer " + amt + " failed, rolled back");
28 } finally {
29 con.setAutoCommit(true);
30 }
31 }
32}
Terminal
D:\java-practice> java Main.java
Transferred 3000
Transfer 4000 failed, rolled back
RAVI: 2000
MEENA: 4000
💡 Concept in shortA transaction groups several statements so that all succeed (commit) or none do (rollback). In a money transfer, crediting without debiting would create money from nothing.
RememberACID transactions are why banks trust databases.
Module 20DAO pattern

The DAO Pattern: Keep SQL in One Place

📄 StudentDao.java (file on disk)TXT
1record Student(int roll, String name, String city, int marks) { }
2
3class StudentDao {
4 private final String url;
5 StudentDao(String url) { this.url = url; }
6
7 void save(Student s) throws SQLException { ... INSERT ... }
8 Optional<Student> findByRoll(int roll) throws SQLException { ... SELECT ... }
9 List<Student> findByCity(String city) throws SQLException { ... }
10 boolean delete(int roll) throws SQLException { ... DELETE ... }
11}
💡 Concept in shortA DAO (Data Access Object) is a class that hides all SQL behind simple methods like save and findByCity. The rest of the app works with records and lists, never with SQL.
Screen / API➜Service (rules)➜DAO (SQL)➜Database
RememberSpring Data JPA generates DAO methods for you: List<Student> findByCity(String city);
Module 20Modern 1

Modern Java: var, Text Blocks, switch Expressions

Main.java  JAVA
1import java.util.*;
2
3public class Main {
4 public static void main(String[] args) {
5 var marks = new ArrayList<Integer>(List.of(91, 76, 88)); // type inferred
6 var total = 0;
7 for (var m : marks) total += m;
8
9 String json = """
10 {
11 "school": "DAV Hehal, Ranchi",
12 "total": %d
13 }
14 """.formatted(total); // text block
15 System.out.print(json);
16
17 var day = "SAT";
18 String type = switch (day) { // switch expression
19 case "SAT", "SUN" -> "weekend";
20 case "MON", "TUE", "WED", "THU", "FRI" -> "weekday";
21 default -> {
22 yield "unknown";
23 }
24 };
25 System.out.println(day + " is a " + type);
26 }
27}
Terminal
D:\java-practice> java Main.java
{
"school": "DAV Hehal, Ranchi",
"total": 255
}
SAT is a weekend
💡 Concept in shortvar lets the compiler infer a local variable's type (still statically typed). Text blocks """...""" hold multi-line text (JSON, SQL, HTML) without \n. switch expressions return a value with -> and yield.
Module 20Modern 2

Sealed Types and Pattern Matching (Java 17-21)

Main.java  JAVA
1sealed interface Payment permits Upi, Card, Cash { }
2record Upi(String vpa, double amount) implements Payment { }
3record Card(String last4, double amount, boolean international) implements Payment { }
4record Cash(double amount) implements Payment { }
5
6public class Main {
7 static double fee(Payment p) {
8 return switch (p) { // compiler checks all cases
9 case Upi u -> 0;
10 case Card c when c.international() -> c.amount() * 0.035;
11 case Card c -> c.amount() * 0.02;
12 case Cash c -> 0;
13 };
14 }
15 static String describe(Object o) {
16 if (o instanceof Upi(String vpa, double amt)) return "UPI from " + vpa + " Rs " + amt; // record pattern
17 return o.toString();
18 }
19
20 public static void main(String[] args) {
21 Payment[] list = {new Upi("riya@okaxis", 499), new Card("4421", 2000, false), new Card("9001", 2000, true), new Cash(150)};
22 for (Payment p : list) System.out.printf("%-60s fee %.2f%n", p, fee(p));
23 System.out.println(describe(list[0]));
24 }
25}
Terminal
D:\java-practice> java Main.java
Upi[vpa=riya@okaxis, amount=499.0] fee 0.00
Card[last4=4421, amount=2000.0, international=false] fee 40.00
Card[last4=9001, amount=2000.0, international=true] fee 70.00
Cash[amount=150.0] fee 0.00
UPI from riya@okaxis Rs 499.0
💡 Concept in shortA sealed interface lists exactly which types may implement it. A pattern-matching switch can then handle each type, with when guards, and the compiler errors if a case is missing. Record patterns unpack fields directly.
RememberThis style (records + sealed types + switch) is how modern Java models business data.
Module 20Tools

Professional Tools: Maven/Gradle and JUnit Testing

📄 pom.xml (Maven) - dependencies (file on disk)TXT
1<dependencies>
2 <dependency>
3 <groupId>org.xerial</groupId>
4 <artifactId>sqlite-jdbc</artifactId>
5 <version>3.46.1.3</version>
6 </dependency>
7 <dependency>
8 <groupId>org.junit.jupiter</groupId>
9 <artifactId>junit-jupiter</artifactId>
10 <version>5.11.0</version>
11 <scope>test</scope>
12 </dependency>
13</dependencies>
📄 src/test/java/GstTest.java (JUnit 5) (file on disk)TXT
1import org.junit.jupiter.api.Test;
2import static org.junit.jupiter.api.Assertions.*;
3
4class GstTest {
5 @Test
6 void addsEighteenPercent() {
7 assertEquals(118.0, Gst.withGst(100, 18), 0.001);
8 }
9 @Test
10 void rejectsNegativePrice() {
11 assertThrows(IllegalArgumentException.class, () -> Gst.withGst(-5, 18));
12 }
13}
💡 Concept in shortMaven and Gradle download libraries (jars) automatically, compile, test and package your project with one command (mvn package). JUnit runs automatic tests: each @Test method checks one behaviour with assertEquals.
Folder (Maven)Holds
src/main/javayour code
src/test/javaJUnit tests
pom.xmlproject + dependencies
target/built .jar
RememberThese run on your computer with Maven installed; this course shows them as an overview. IntelliJ IDEA and VS Code create such projects in a click.
Module 20Hands-on

Code Lab: Try It Yourself

Main.java
Output of the starter code
D:\java-practice> java Main.java
2 Pen Rs 10 stock 500
1 Notebook Rs 45 stock 120
3 Geometry box Rs 150 stock 25
Change the code, then run it on your computer: ⬇ Download → open a terminal in that folder → java Main.java. No JDK yet? Open an online Java compiler and paste the code (Copy).
Your tasks
  1. Add a method sell(con, id, qty) using a PreparedStatement UPDATE
  2. Refuse a sale larger than the stock
  3. Print the total stock value with SELECT SUM(price*stock)
  4. Write a record Product and a DAO method findAll()
PracticePractice 1 of 10 · Easy

Contacts Table

EasyScenario. Save contacts in a database instead of a file.
💡 Concept used: CREATE TABLE, INSERT with PreparedStatement, SELECT all.

Your tasks

  1. Create table contact(name, phone)
  2. Insert 3 rows
  3. Select and print them

Run with the SQLite driver: java -cp ".;sqlite-jdbc.jar" File.java (use : instead of ; on Mac/Linux).

🏢 Used in real projects: Every app's data starts with CREATE TABLE and INSERT.
Contacts.java  JAVA
1import java.sql.*;
2
3public class Contacts {
4 public static void main(String[] args) throws SQLException {
5 try (Connection c = DriverManager.getConnection("jdbc:sqlite:contacts.db"); Statement s = c.createStatement()) {
6 s.executeUpdate("DROP TABLE IF EXISTS contact");
7 s.executeUpdate("CREATE TABLE contact (name TEXT, phone TEXT)");
8 try (PreparedStatement ps = c.prepareStatement("INSERT INTO contact VALUES (?, ?)")) {
9 String[][] d = {{"Mahto Electricals", "98350 12345"}, {"Gupta Sweets", "70040 22334"}, {"Lakra Tailors", "62039 44556"}};
10 for (String[] r : d) { ps.setString(1, r[0]); ps.setString(2, r[1]); ps.executeUpdate(); }
11 }
12 ResultSet rs = s.executeQuery("SELECT * FROM contact ORDER BY name");
13 while (rs.next()) System.out.println(rs.getString("name") + " : " + rs.getString("phone"));
14 }
15 }
16}
PracticePractice 2 of 10 · Easy

var and Text Blocks

EasyScenario. Print an SMS template and an HTML receipt using modern syntax.
💡 Concept used: var for locals; text blocks with formatted().

Your tasks

  1. var for name, amount
  2. Text block SMS
  3. Text block HTML with %s placeholders
🏢 Used in real projects: Text blocks hold SQL, JSON and HTML templates in real projects.
Modern.java  JAVA
1public class Modern {
2 public static void main(String[] args) {
3 var name = "Riya";
4 var amount = 1499.0;
5 var sms = """
6 Dear %s,
7 Rs %.2f received. Thank you for shopping at Kumar Store, Lalpur.
8 """.formatted(name, amount);
9 System.out.print(sms);
10 var html = """
11 <tr><td>%s</td><td>%.2f</td></tr>""".formatted(name, amount);
12 System.out.println(html);
13 }
14}
PracticePractice 3 of 10 · Easy

Switch Expression Grader

EasyScenario. Convert grade letters into remarks with a switch expression.
💡 Concept used: switch expression with multiple labels and yield.

Your tasks

  1. Loop over grades
  2. switch returns a remark
  3. Use yield in a block case
🏢 Used in real projects: Mapping codes to messages is a common use of switch expressions.
Remarks.java  JAVA
1public class Remarks {
2 public static void main(String[] args) {
3 for (String g : new String[]{"A+", "A", "B", "C", "F"}) {
4 String remark = switch (g) {
5 case "A+", "A" -> "Excellent";
6 case "B" -> "Good";
7 case "C" -> "Needs practice";
8 default -> {
9 String r = "Re-exam";
10 yield r.toUpperCase();
11 }
12 };
13 System.out.println(g + " -> " + remark);
14 }
15 }
16}
PracticePractice 4 of 10 · Easy

Search with PreparedStatement

EasyScenario. A library searches books by a word typed by the user, safely.
💡 Concept used: LIKE with a ? parameter and % wildcards; no SQL injection even with tricky input.

Your tasks

  1. Create books table
  2. search(word) with LIKE ?
  3. Try a normal word and an injection attempt

Run with the SQLite driver: java -cp ".;sqlite-jdbc.jar" File.java (use : instead of ; on Mac/Linux).

🏢 Used in real projects: Every search box backed by a database must use parameters.
BookSearch.java  JAVA
1import java.sql.*;
2
3public class BookSearch {
4 static void search(Connection c, String word) throws SQLException {
5 try (PreparedStatement ps = c.prepareStatement("SELECT title FROM book WHERE title LIKE ?")) {
6 ps.setString(1, "%" + word + "%");
7 ResultSet rs = ps.executeQuery();
8 System.out.print("Search \"" + word + "\": ");
9 while (rs.next()) System.out.print(rs.getString(1) + "; ");
10 System.out.println();
11 }
12 }
13 public static void main(String[] args) throws SQLException {
14 try (Connection c = DriverManager.getConnection("jdbc:sqlite:lib.db"); Statement s = c.createStatement()) {
15 s.executeUpdate("DROP TABLE IF EXISTS book");
16 s.executeUpdate("CREATE TABLE book (title TEXT)");
17 s.executeUpdate("INSERT INTO book VALUES ('Java for Beginners'), ('Learn Python'), ('Head First Java'), ('Jharkhand History')");
18 search(c, "Java");
19 search(c, "x' OR '1'='1"); // injection attempt finds nothing
20 }
21 }
22}
PracticePractice 5 of 10 · Easy

Sealed Shapes

EasyScenario. A drawing app supports exactly three shapes; the compiler checks every switch handles them.
💡 Concept used: sealed interface + records + pattern-matching switch.

Your tasks

  1. sealed interface Shape permits Circle, Rect, Tri
  2. area(Shape) with a switch
  3. Total area of a list
🏢 Used in real projects: Sealed hierarchies model fixed sets of cases (payment types, API responses).
Sealed.java  JAVA
1sealed interface Shape permits Circle, Rect, Tri { }
2record Circle(double r) implements Shape { }
3record Rect(double w, double h) implements Shape { }
4record Tri(double b, double h) implements Shape { }
5
6public class Sealed {
7 static double area(Shape s) {
8 return switch (s) {
9 case Circle c -> Math.PI * c.r() * c.r();
10 case Rect r -> r.w() * r.h();
11 case Tri t -> 0.5 * t.b() * t.h();
12 };
13 }
14 public static void main(String[] args) {
15 Shape[] all = {new Circle(2), new Rect(3, 4), new Tri(6, 5)};
16 double total = 0;
17 for (Shape s : all) { System.out.printf("%-22s %.2f%n", s, area(s)); total += area(s); }
18 System.out.printf("Total %.2f%n", total);
19 }
20}
PracticePractice 6 of 10 · Medium

Student DAO

MediumScenario. Wrap all SQL for students in a DAO class with records.
💡 Concept used: DAO pattern: save, findByCity, average; the main method never writes SQL.

Your tasks

  1. record Student
  2. StudentDao with save, findByCity, averageMarks
  3. Use it from main

Run with the SQLite driver: java -cp ".;sqlite-jdbc.jar" File.java (use : instead of ; on Mac/Linux).

🏢 Used in real projects: Layered applications always isolate SQL in DAO/repository classes.
DaoDemo.java  JAVA
1import java.sql.*;
2import java.util.*;
3
4record Student(int roll, String name, String city, int marks) { }
5
6class StudentDao {
7 private final Connection c;
8 StudentDao(Connection c) throws SQLException {
9 this.c = c;
10 try (Statement s = c.createStatement()) {
11 s.executeUpdate("DROP TABLE IF EXISTS student");
12 s.executeUpdate("CREATE TABLE student (roll INTEGER PRIMARY KEY, name TEXT, city TEXT, marks INTEGER)");
13 }
14 }
15 void save(Student s) throws SQLException {
16 try (PreparedStatement ps = c.prepareStatement("INSERT INTO student VALUES (?, ?, ?, ?)")) {
17 ps.setInt(1, s.roll()); ps.setString(2, s.name()); ps.setString(3, s.city()); ps.setInt(4, s.marks());
18 ps.executeUpdate();
19 }
20 }
21 List<Student> findByCity(String city) throws SQLException {
22 List<Student> out = new ArrayList<>();
23 try (PreparedStatement ps = c.prepareStatement("SELECT * FROM student WHERE city = ? ORDER BY marks DESC")) {
24 ps.setString(1, city);
25 ResultSet rs = ps.executeQuery();
26 while (rs.next()) out.add(new Student(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getInt(4)));
27 }
28 return out;
29 }
30 double averageMarks() throws SQLException {
31 try (Statement s = c.createStatement()) { return s.executeQuery("SELECT AVG(marks) FROM student").getDouble(1); }
32 }
33}
34
35public class DaoDemo {
36 public static void main(String[] args) throws SQLException {
37 try (Connection c = DriverManager.getConnection("jdbc:sqlite:dao.db")) {
38 StudentDao dao = new StudentDao(c);
39 dao.save(new Student(1, "Aditi", "Ranchi", 91));
40 dao.save(new Student(2, "Rohan", "Dhanbad", 76));
41 dao.save(new Student(3, "Sana", "Ranchi", 88));
42 System.out.println("Ranchi: " + dao.findByCity("Ranchi"));
43 System.out.printf("Average: %.2f%n", dao.averageMarks());
44 }
45 }
46}
PracticePractice 7 of 10 · Medium

Record Patterns for API Responses

MediumScenario. An app receives different API response types and handles each.
💡 Concept used: sealed + records + record deconstruction patterns with guards.

Your tasks

  1. sealed Response: Ok(data), NotFound(id), Error(code, msg)
  2. switch with record patterns
  3. Guard: server errors (code >= 500) retry
🏢 Used in real projects: Modern Java web clients model responses with sealed records.
Api.java  JAVA
1sealed interface Response permits Ok, NotFound, Failure { }
2record Ok(String data) implements Response { }
3record NotFound(String id) implements Response { }
4record Failure(int code, String msg) implements Response { }
5
6public class Api {
7 static String handle(Response r) {
8 return switch (r) {
9 case Ok(String data) -> "Show: " + data;
10 case NotFound(String id) -> "No record " + id;
11 case Failure(int code, String msg) when code >= 500 -> "Server issue (" + msg + "), retrying...";
12 case Failure(int code, String msg) -> "Error " + code + ": " + msg;
13 };
14 }
15 public static void main(String[] args) {
16 Response[] rs = {new Ok("Result: Aditi 91%"), new NotFound("ROLL-1290"), new Failure(503, "maintenance"), new Failure(401, "login required")};
17 for (Response r : rs) System.out.println(handle(r));
18 }
19}
PracticePractice 8 of 10 · Medium

Bank Transfer with Transaction

MediumScenario. Transfers between accounts must be all-or-nothing, and every transfer is logged.
💡 Concept used: setAutoCommit(false), commit, rollback, a CHECK constraint, and a log table in the same transaction.

Your tasks

  1. account(id, balance CHECK >= 0) and txn_log tables
  2. transfer() updates both accounts and inserts a log row
  3. Rollback on failure
  4. Print balances and log

Run with the SQLite driver: java -cp ".;sqlite-jdbc.jar" File.java (use : instead of ; on Mac/Linux).

🏢 Used in real projects: UPI and net banking use transactions so money is never lost or duplicated.
Transfer.java  JAVA
1import java.sql.*;
2
3public class Transfer {
4 static void transfer(Connection c, String from, String to, int amt) throws SQLException {
5 c.setAutoCommit(false);
6 try (PreparedStatement d = c.prepareStatement("UPDATE account SET balance = balance - ? WHERE id = ?");
7 PreparedStatement cr = c.prepareStatement("UPDATE account SET balance = balance + ? WHERE id = ?");
8 PreparedStatement log = c.prepareStatement("INSERT INTO txn_log (info) VALUES (?)")) {
9 cr.setInt(1, amt); cr.setString(2, to); cr.executeUpdate();
10 d.setInt(1, amt); d.setString(2, from); d.executeUpdate();
11 log.setString(1, from + "->" + to + " " + amt); log.executeUpdate();
12 c.commit();
13 System.out.println("OK " + from + " -> " + to + " Rs " + amt);
14 } catch (SQLException e) {
15 c.rollback();
16 System.out.println("FAILED " + from + " -> " + to + " Rs " + amt + " (rolled back)");
17 } finally { c.setAutoCommit(true); }
18 }
19 public static void main(String[] args) throws SQLException {
20 try (Connection c = DriverManager.getConnection("jdbc:sqlite:bank2.db"); Statement s = c.createStatement()) {
21 s.executeUpdate("DROP TABLE IF EXISTS account"); s.executeUpdate("DROP TABLE IF EXISTS txn_log");
22 s.executeUpdate("CREATE TABLE account (id TEXT PRIMARY KEY, balance INTEGER CHECK (balance >= 0))");
23 s.executeUpdate("CREATE TABLE txn_log (n INTEGER PRIMARY KEY AUTOINCREMENT, info TEXT)");
24 s.executeUpdate("INSERT INTO account VALUES ('A1', 8000), ('B2', 2000), ('C3', 500)");
25 transfer(c, "A1", "B2", 3000); transfer(c, "C3", "A1", 900); transfer(c, "B2", "C3", 4500);
26 ResultSet rs = s.executeQuery("SELECT * FROM account");
27 while (rs.next()) System.out.println(rs.getString(1) + " = " + rs.getInt(2));
28 rs = s.executeQuery("SELECT COUNT(*) FROM txn_log");
29 System.out.println("Logged transfers: " + rs.getInt(1));
30 }
31 }
32}
PracticePractice 9 of 10 · Hard

Mini Inventory System with JDBC

HardScenario. A shop inventory with CRUD, low-stock alerts and a sales report, all in SQLite.
💡 Concept used: Full CRUD through a DAO, JOIN for reports, and records for rows.

Your tasks

  1. Tables product(id, name, price, stock) and sale(product_id, qty)
  2. addProduct, sell (checks stock), restock
  3. Report: revenue per product with JOIN and GROUP BY
  4. Low-stock list

Run with the SQLite driver: java -cp ".;sqlite-jdbc.jar" File.java (use : instead of ; on Mac/Linux).

🏢 Used in real projects: Billing and inventory software (like a mini Tally/Vyapar) is built exactly like this.
Inventory.java  JAVA
1import java.sql.*;
2
3public class Inventory {
4 static Connection c;
5 static void exec(String sql) throws SQLException { try (Statement s = c.createStatement()) { s.executeUpdate(sql); } }
6 static void addProduct(String n, int price, int stock) throws SQLException {
7 try (PreparedStatement ps = c.prepareStatement("INSERT INTO product (name, price, stock) VALUES (?, ?, ?)")) {
8 ps.setString(1, n); ps.setInt(2, price); ps.setInt(3, stock); ps.executeUpdate();
9 }
10 }
11 static boolean sell(int id, int qty) throws SQLException {
12 try (PreparedStatement up = c.prepareStatement("UPDATE product SET stock = stock - ? WHERE id = ? AND stock >= ?")) {
13 up.setInt(1, qty); up.setInt(2, id); up.setInt(3, qty);
14 if (up.executeUpdate() == 0) return false;
15 }
16 try (PreparedStatement ins = c.prepareStatement("INSERT INTO sale VALUES (?, ?)")) { ins.setInt(1, id); ins.setInt(2, qty); ins.executeUpdate(); }
17 return true;
18 }
19 public static void main(String[] args) throws SQLException {
20 c = DriverManager.getConnection("jdbc:sqlite:inventory.db");
21 exec("DROP TABLE IF EXISTS product"); exec("DROP TABLE IF EXISTS sale");
22 exec("CREATE TABLE product (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, price INTEGER, stock INTEGER)");
23 exec("CREATE TABLE sale (product_id INTEGER, qty INTEGER)");
24 addProduct("Rice 5kg", 320, 40); addProduct("Mustard oil 1L", 165, 12); addProduct("Tea 250g", 140, 6);
25 int[][] sales = {{1, 10}, {2, 9}, {3, 7}, {3, 4}, {1, 25}};
26 for (int[] s : sales) System.out.println("sell #" + s[0] + " x" + s[1] + ": " + (sell(s[0], s[1]) ? "done" : "NOT ENOUGH STOCK"));
27 ResultSet rs = c.createStatement().executeQuery("SELECT p.name, SUM(s.qty), SUM(s.qty * p.price) FROM sale s JOIN product p ON p.id = s.product_id GROUP BY p.name ORDER BY 3 DESC");
28 System.out.println("-- Revenue report --");
29 while (rs.next()) System.out.printf("%-15s qty %3d Rs %,6d%n", rs.getString(1), rs.getInt(2), rs.getInt(3));
30 rs = c.createStatement().executeQuery("SELECT name, stock FROM product WHERE stock < 10");
31 while (rs.next()) System.out.println("LOW STOCK: " + rs.getString(1) + " (" + rs.getInt(2) + ")");
32 c.close();
33 }
34}
PracticePractice 10 of 10 · Hard

Modern Java Order Pipeline

HardScenario. Model online orders with sealed types and records, then compute totals and messages with pattern matching and streams.
💡 Concept used: Combining records, sealed interfaces, switch patterns with guards, var, text blocks and streams.

Your tasks

  1. sealed OrderEvent: Placed, Shipped, Delivered, Cancelled (records)
  2. message(event) with a pattern switch and guards
  3. Stream to count events per type
  4. Text-block summary
🏢 Used in real projects: Event-driven systems (order tracking, payments) are modelled this way in modern Java services.
OrderPipeline.java  JAVA
1import java.util.*;
2import java.util.stream.*;
3
4sealed interface OrderEvent permits Placed, Shipped, Delivered, Cancelled { String id(); }
5record Placed(String id, double amount) implements OrderEvent { }
6record Shipped(String id, String courier) implements OrderEvent { }
7record Delivered(String id, int days) implements OrderEvent { }
8record Cancelled(String id, String reason) implements OrderEvent { }
9
10public class OrderPipeline {
11 static String message(OrderEvent e) {
12 return switch (e) {
13 case Placed p when p.amount() >= 5000 -> p.id() + ": big order Rs " + p.amount() + ", free delivery";
14 case Placed p -> p.id() + ": placed Rs " + p.amount();
15 case Shipped s -> s.id() + ": shipped via " + s.courier();
16 case Delivered d when d.days() > 5 -> d.id() + ": delivered late (" + d.days() + " days), coupon sent";
17 case Delivered d -> d.id() + ": delivered in " + d.days() + " days";
18 case Cancelled c -> c.id() + ": cancelled (" + c.reason() + ")";
19 };
20 }
21 public static void main(String[] args) {
22 var events = List.of(new Placed("A101", 7499), new Placed("A102", 899), new Shipped("A101", "BlueDart"),
23 new Cancelled("A102", "changed mind"), new Delivered("A101", 6));
24 events.forEach(e -> System.out.println(message(e)));
25 var counts = events.stream().collect(Collectors.groupingBy(e -> e.getClass().getSimpleName(), TreeMap::new, Collectors.counting()));
26 var revenue = events.stream().filter(e -> e instanceof Placed).mapToDouble(e -> ((Placed) e).amount()).sum();
27 System.out.print("""
28 --- Summary ---
29 Events : %s
30 Revenue : Rs %.2f
31 """.formatted(counts, revenue));
32 }
33}
Module 20Recap

Module 20 Recap & Quiz

What you learned

  • 1JDBC = java.sql interfaces + a vendor driver jar
  • 2Connection -> PreparedStatement (? placeholders) -> ResultSet (rs.next())
  • 3Always use PreparedStatement: prevents SQL injection
  • 4Transactions: setAutoCommit(false), commit, rollback; DAO keeps SQL in one class
  • 5Modern Java: var, text blocks, switch expressions, records, sealed types, pattern matching
  • 6Maven/Gradle manage dependencies; JUnit tests with @Test and assertEquals

Quick quiz

Which prevents SQL injection?

Parameters are never treated as SQL.

rs.next() returns false when...

It moves the cursor; false means the end.

A sealed interface...

permits names the allowed subtypes.
MODULE 21

Capstone Projects

⏱ 12 hours10 practice examplesStep-through animationsCode Lab

You will learn

  • Plan a project: requirements, classes, layers
  • Five starter mini projects to warm up
  • Five capstone projects combining everything
  • Clean code, testing and how to grow each project
  • Your next steps: Spring Boot, Android, interviews

You will build

  • Quiz game, to-do app, converter, bill splitter, number game
  • Result management, bank, library, railway, e-commerce analytics
  • A portfolio you can show
{ }Plan -&gt; Build -&gt; TestPortfolio ready
Module 21Real-world use

Where Is This Used in Real Projects?

everything you learnedput it all together🏫School systemsresults, attendance🏦Bankingaccounts, transfers📚Librariesissue, fines🚆Railwaysseats, waitlist🛒E-commercecart, analytics🎮Gamesquiz, guessing

Real projects that use this

  • Each capstone mixes OOP, collections, exceptions, files/JDBC and streams
  • They mirror real systems used in Jharkhand schools, banks and shops
  • Put them on GitHub with a README: they become your portfolio
  • Interviewers ask you to explain exactly these designs
Module 21Plan first

How Professionals Plan a Java Project

UI / MenuScanner menu, printfService (rules)validation, calculationsDAO / Repositoryfiles or JDBCStorageCSV file or SQLiteModel (records)StudentAccountBookOrderused by every layerExceptionscustom, handledat the UI layer
💡 Concept in shortBefore coding: (1) list features (user stories), (2) find the nouns (classes/records) and verbs (methods), (3) split into layers, (4) build the smallest working version, then add features one by one.
Requirements: "A librarian can issue a book to a member"
Model: records Book, Member, Issue
Service: issue() checks limits and availability
Storage: DAO saves to CSV or SQLite
UI: a Scanner menu calls the service
Test: try normal, edge and wrong inputs
1 / 6
Module 21Roadmap

Project Roadmap: From Starter to Capstone

#ProjectMain skills
1Quiz Gamearrays, loops, Scanner
2Unit Convertermethods, switch
3Bill Splitterprintf, validation
4Number Game + High ScoreRandom, files
5To-Do ManagerArrayList, menu loop
6Student Result SystemOOP, collections, streams
7Bank Account Systeminheritance, exceptions
8Library Manager (SQLite)JDBC, DAO, dates
9Railway Reservationmaps, queues, threads
10E-commerce Analyticsrecords, sealed, streams
💡 Concept in shortProjects 1-5 are starters (1-2 hours each). Projects 6-10 are capstones (4-8 hours each). Each slide shows the requirements, the concepts used, a complete solution and its output.
RememberTry each project yourself first. Open the solution only to compare, then add the "extend it" ideas.
🌟

Portfolio tip

Create a GitHub repo per capstone with a README: features, how to run, sample output, what you learned.
PracticePractice 1 of 10 · Easy

Starter 1: Jharkhand GK Quiz Game

EasyScenario. A 5-question quiz that reads answers, scores them and shows the correct answers at the end.
💡 Concept used: Parallel arrays for questions and answers, a loop with Scanner, equalsIgnoreCase, and a score summary.

Your tasks

  1. Store questions, options and answer letters
  2. Read each answer with Scanner
  3. Score +2 correct, 0 wrong
  4. Show the final score and a review
🏢 Used in real projects: Online tests, learning apps and interview platforms.
QuizGame.java  JAVA
1import java.util.Scanner;
2
3public class QuizGame {
4 public static void main(String[] args) {
5 String[] q = {"Capital of Jharkhand?", "Hundru Falls is on which river?", "Jharkhand was formed in?", "Which city is called Steel City?", "Famous temple town of Jharkhand?"};
6 String[][] opt = {{"Ranchi", "Dhanbad", "Bokaro"}, {"Subarnarekha", "Damodar", "Koel"}, {"1998", "2000", "2005"}, {"Jamshedpur", "Hazaribagh", "Giridih"}, {"Deoghar", "Dumka", "Gumla"}};
7 char[] ans = {'a', 'a', 'b', 'a', 'a'};
8 Scanner sc = new Scanner(System.in);
9 int score = 0;
10 StringBuilder review = new StringBuilder();
11 for (int i = 0; i < q.length; i++) {
12 System.out.println("Q" + (i + 1) + ". " + q[i] + " a) " + opt[i][0] + " b) " + opt[i][1] + " c) " + opt[i][2]);
13 System.out.print("Your answer: ");
14 char a = sc.next().toLowerCase().charAt(0);
15 if (a == ans[i]) score += 2;
16 else review.append("Q").append(i + 1).append(" correct: ").append(opt[i][ans[i] - 'a']).append("\n");
17 }
18 System.out.println("Score: " + score + " / " + q.length * 2);
19 System.out.print(review.length() == 0 ? "Perfect!\n" : review);
20 }
21}
PracticePractice 2 of 10 · Easy

Starter 2: Unit Converter

EasyScenario. A menu converts km/miles, Celsius/Fahrenheit, kg/pounds and rupees/dollars.
💡 Concept used: One method per conversion, a switch on the menu choice and a loop until exit.

Your tasks

  1. Menu with 4 conversions + exit
  2. Each conversion in its own method
  3. Read the value
  4. Repeat until 0
🏢 Used in real projects: Converter tools on phones and websites.
Converter.java  JAVA
1import java.util.Scanner;
2
3public class Converter {
4 static double kmToMiles(double k) { return k * 0.621371; }
5 static double cToF(double c) { return c * 9 / 5 + 32; }
6 static double kgToLb(double k) { return k * 2.20462; }
7 static double rsToUsd(double r) { return r / 84.0; } // sample rate
8
9 public static void main(String[] args) {
10 Scanner sc = new Scanner(System.in);
11 while (true) {
12 System.out.print("1 km->mi 2 C->F 3 kg->lb 4 Rs->$ 0 exit: ");
13 int ch = sc.nextInt();
14 if (ch == 0) break;
15 System.out.print(" value: ");
16 double v = sc.nextDouble();
17 String out = switch (ch) {
18 case 1 -> String.format("%.2f km = %.2f miles", v, kmToMiles(v));
19 case 2 -> String.format("%.1f C = %.1f F", v, cToF(v));
20 case 3 -> String.format("%.1f kg = %.1f lb", v, kgToLb(v));
21 case 4 -> String.format("Rs %.0f = $ %.2f", v, rsToUsd(v));
22 default -> "invalid choice";
23 };
24 System.out.println(" " + out);
25 }
26 System.out.println("Goodbye!");
27 }
28}
PracticePractice 3 of 10 · Easy

Starter 3: Restaurant Bill Splitter

EasyScenario. Friends split a restaurant bill with GST, a service charge and an optional tip; invalid input is rejected.
💡 Concept used: Input validation with loops, Math.ceil for rounding up and a printf receipt.

Your tasks

  1. Read bill amount and people (must be > 0)
  2. Add 5% GST and 10% tip
  3. Round each share up to the next rupee
  4. Print a neat receipt
🏢 Used in real projects: Splitwise-style apps.
BillSplit.java  JAVA
1import java.util.Scanner;
2
3public class BillSplit {
4 static double readPositive(Scanner sc, String label) {
5 while (true) {
6 System.out.print(label);
7 double v = sc.nextDouble();
8 if (v > 0) return v;
9 System.out.println(" must be more than 0");
10 }
11 }
12 public static void main(String[] args) {
13 Scanner sc = new Scanner(System.in);
14 double bill = readPositive(sc, "Food bill: ");
15 int people = (int) readPositive(sc, "People: ");
16 double gst = bill * 0.05, tip = bill * 0.10, total = bill + gst + tip;
17 double share = Math.ceil(total / people);
18 System.out.println("-------------------------");
19 System.out.printf("Food Rs %9.2f%nGST 5%% Rs %9.2f%nTip 10%% Rs %9.2f%n", bill, gst, tip);
20 System.out.printf("TOTAL Rs %9.2f%n", total);
21 System.out.printf("Each of %d pays Rs %.0f%n", people, share);
22 }
23}
PracticePractice 4 of 10 · Easy

Starter 4: Number Game with High Score File

EasyScenario. Guess a number from 1-100 with hints; the best score is saved in a file and shown next time.
💡 Concept used: Random with a seed, a loop with hints, and reading/writing a high-score file.

Your tasks

  1. Random(42) secret number
  2. Hints: higher / lower
  3. Count attempts
  4. Load and update best score in highscore.txt
🏢 Used in real projects: Games save progress and scores to files.
NumberGame.java  JAVA
1import java.nio.file.*;
2import java.util.*;
3
4public class NumberGame {
5 public static void main(String[] args) throws Exception {
6 Path hs = Path.of("highscore.txt");
7 int best = Files.exists(hs) ? Integer.parseInt(Files.readString(hs).trim()) : Integer.MAX_VALUE;
8 System.out.println(best == Integer.MAX_VALUE ? "No high score yet" : "Best so far: " + best + " tries");
9 int secret = new Random(42).nextInt(100) + 1;
10 Scanner sc = new Scanner(System.in);
11 int tries = 0, g;
12 do {
13 System.out.print("Guess: ");
14 g = sc.nextInt();
15 tries++;
16 if (g < secret) System.out.println(" higher");
17 else if (g > secret) System.out.println(" lower");
18 } while (g != secret);
19 System.out.println("Got it in " + tries + " tries!");
20 if (tries < best) {
21 Files.writeString(hs, String.valueOf(tries));
22 System.out.println("New high score saved");
23 }
24 }
25}
PracticePractice 5 of 10 · Easy

Starter 5: To-Do Manager

EasyScenario. A command-line to-do app: add, list, mark done, delete and save to a file.
💡 Concept used: ArrayList of records, a menu loop with switch, index validation and saving with Files.write.

Your tasks

  1. record Task(String text, boolean done)
  2. Commands: add, list, done N, del N, save, exit
  3. Validate the task number
  4. Save to todo.txt
🏢 Used in real projects: Every productivity app starts as a to-do list.
TodoApp.java  JAVA
1import java.nio.file.*;
2import java.util.*;
3
4record Task(String text, boolean done) { }
5
6public class TodoApp {
7 public static void main(String[] args) throws Exception {
8 List<Task> tasks = new ArrayList<>();
9 Scanner sc = new Scanner(System.in);
10 while (true) {
11 System.out.print("> ");
12 String[] cmd = sc.nextLine().trim().split(" ", 2);
13 switch (cmd[0]) {
14 case "add" -> tasks.add(new Task(cmd[1], false));
15 case "list" -> { for (int i = 0; i < tasks.size(); i++) System.out.println(" " + (i + 1) + ". [" + (tasks.get(i).done() ? "x" : " ") + "] " + tasks.get(i).text()); }
16 case "done", "del" -> {
17 int n = Integer.parseInt(cmd[1]) - 1;
18 if (n < 0 || n >= tasks.size()) { System.out.println(" no task " + (n + 1)); break; }
19 if (cmd[0].equals("done")) tasks.set(n, new Task(tasks.get(n).text(), true));
20 else System.out.println(" deleted: " + tasks.remove(n).text());
21 }
22 case "save" -> {
23 List<String> lines = new ArrayList<>();
24 for (Task t : tasks) lines.add((t.done() ? "x|" : " |") + t.text());
25 Files.write(Path.of("todo.txt"), lines);
26 System.out.println(" saved " + lines.size() + " tasks");
27 }
28 case "exit" -> { System.out.println("Bye"); return; }
29 default -> System.out.println(" commands: add, list, done N, del N, save, exit");
30 }
31 }
32 }
33}
PracticePractice 6 of 10 · Medium

Capstone 1: Student Result Management System

MediumScenario. A school stores students and marks, computes results, ranks, subject toppers and writes a report file.
💡 Concept used: Classes and records, Map and List collections, Comparator ranking, streams for statistics, file output: Modules 9-18 together.

Your tasks

  1. record Student(roll, name, int[] marks) with total(), percent(), grade()
  2. ResultService: add, rank list, subject toppers, pass %
  3. Grade distribution with groupingBy
  4. Write results.csv
🏢 Used in real projects: School ERP result modules (and board result portals) are built like this. Extend it: read marks from a CSV, add a Scanner menu, store in SQLite.
ResultSystem.java  JAVA
1import java.nio.file.*;
2import java.util.*;
3import java.util.stream.*;
4
5record Student(int roll, String name, int[] marks) {
6 static final String[] SUB = {"Maths", "Science", "English", "Hindi", "SST"};
7 int total() { return IntStream.of(marks).sum(); }
8 double percent() { return total() / 5.0; }
9 boolean passed() { return IntStream.of(marks).allMatch(m -> m >= 33); }
10 String grade() { double p = percent(); return !passed() ? "F" : p >= 90 ? "A+" : p >= 75 ? "A" : p >= 60 ? "B" : "C"; }
11}
12
13class ResultService {
14 private final List<Student> list = new ArrayList<>();
15 void add(Student s) { list.add(s); }
16 List<Student> ranked() {
17 return list.stream().sorted(Comparator.comparingInt(Student::total).reversed().thenComparing(Student::name)).toList();
18 }
19 String topper(int sub) {
20 Student t = Collections.max(list, Comparator.comparingInt(s -> s.marks()[sub]));
21 return Student.SUB[sub] + ": " + t.name() + " (" + t.marks()[sub] + ")";
22 }
23 Map<String, Long> gradeCount() { return list.stream().collect(Collectors.groupingBy(Student::grade, TreeMap::new, Collectors.counting())); }
24 double passPercent() { return list.stream().filter(Student::passed).count() * 100.0 / list.size(); }
25}
26
27public class ResultSystem {
28 public static void main(String[] args) throws Exception {
29 ResultService rs = new ResultService();
30 rs.add(new Student(1, "Aditi Kumari", new int[]{95, 91, 88, 90, 93}));
31 rs.add(new Student(2, "Rohan Mahto", new int[]{72, 68, 80, 75, 70}));
32 rs.add(new Student(3, "Sana Parveen", new int[]{88, 94, 90, 85, 91}));
33 rs.add(new Student(4, "Imran Ansari", new int[]{55, 30, 70, 62, 58}));
34 rs.add(new Student(5, "Kavya Tirkey", new int[]{81, 77, 92, 88, 79}));
35 StringBuilder csv = new StringBuilder("rank,roll,name,total,percent,grade\n");
36 int r = 1;
37 for (Student s : rs.ranked()) {
38 System.out.printf("%d. %-13s %3d %5.1f%% %s%n", r, s.name(), s.total(), s.percent(), s.grade());
39 csv.append(String.format("%d,%d,%s,%d,%.1f,%s%n", r++, s.roll(), s.name(), s.total(), s.percent(), s.grade()));
40 }
41 for (int i = 0; i < 5; i++) System.out.println("Topper " + rs.topper(i));
42 System.out.println("Grades: " + rs.gradeCount() + ", pass " + rs.passPercent() + "%");
43 Files.writeString(Path.of("results.csv"), csv);
44 }
45}
PracticePractice 7 of 10 · Medium

Capstone 2: Bank Account System

MediumScenario. Savings and current accounts with deposits, withdrawals, transfers, interest and a statement; rule violations raise custom exceptions.
💡 Concept used: Abstract class + inheritance + polymorphism, custom checked exceptions, a Map of accounts, and a transaction history list.

Your tasks

  1. abstract Account with history; Savings (min balance 1000, 4% interest) and Current (overdraft 5000)
  2. BankException for rule breaks
  3. Bank service: open, transfer, monthEnd
  4. Print statements
🏢 Used in real projects: Core banking software uses account hierarchies, rule checks and statements. Extend it: save to SQLite with transactions (Module 20), add a PIN login menu.
BankSystem.java  JAVA
1import java.util.*;
2
3class BankException extends Exception { BankException(String m) { super(m); } }
4
5abstract class Account {
6 final String no, owner; protected double bal; final List<String> history = new ArrayList<>();
7 Account(String no, String owner, double open) { this.no = no; this.owner = owner; bal = open; log("OPEN", open); }
8 void log(String what, double amt) { history.add(String.format("%-8s %9.2f bal %10.2f", what, amt, bal)); }
9 void deposit(double a) throws BankException { if (a <= 0) throw new BankException("invalid amount"); bal += a; log("DEPOSIT", a); }
10 void withdraw(double a) throws BankException { if (!allowed(a)) throw new BankException(no + ": withdrawal of " + a + " not allowed"); bal -= a; log("WITHDRAW", a); }
11 abstract boolean allowed(double a);
12 void monthEnd() { }
13}
14class Savings extends Account {
15 Savings(String no, String o, double open) { super(no, o, open); }
16 boolean allowed(double a) { return bal - a >= 1000; }
17 void monthEnd() { double i = bal * 0.04 / 12; bal += i; log("INTEREST", i); }
18}
19class Current extends Account {
20 Current(String no, String o, double open) { super(no, o, open); }
21 boolean allowed(double a) { return bal - a >= -5000; }
22}
23
24class Bank {
25 private final Map<String, Account> accounts = new LinkedHashMap<>();
26 void open(Account a) { accounts.put(a.no, a); }
27 void transfer(String from, String to, double amt) {
28 try {
29 Account f = find(from), t = find(to);
30 f.withdraw(amt);
31 t.deposit(amt);
32 System.out.println("Transferred " + amt + " " + from + " -> " + to);
33 } catch (BankException e) { System.out.println("FAILED: " + e.getMessage()); }
34 }
35 Account find(String no) throws BankException {
36 Account a = accounts.get(no);
37 if (a == null) throw new BankException("no account " + no);
38 return a;
39 }
40 void monthEnd() { accounts.values().forEach(Account::monthEnd); }
41 void statements() { for (Account a : accounts.values()) { System.out.println("== " + a.no + " " + a.owner + " (" + a.getClass().getSimpleName() + ")"); a.history.forEach(h -> System.out.println(" " + h)); } }
42}
43
44public class BankSystem {
45 public static void main(String[] args) {
46 Bank b = new Bank();
47 b.open(new Savings("SB101", "Sunita Devi", 12000));
48 b.open(new Current("CA201", "Mahto Traders", 3000));
49 b.transfer("SB101", "CA201", 5000);
50 b.transfer("SB101", "CA201", 7000);
51 b.transfer("CA201", "SB101", 10000);
52 b.transfer("CA201", "XX999", 100);
53 b.monthEnd();
54 b.statements();
55 }
56}
PracticePractice 8 of 10 · Medium

Capstone 3: Library Manager with SQLite

MediumScenario. A library database: books, members and issues; issue with limits, return with date-based fines, and reports.
💡 Concept used: JDBC with a DAO-style class, PreparedStatement, java.time for due dates and fines, and SQL reports with JOIN.

Your tasks

  1. Tables book, member, issue
  2. issue(): book available and member holds < 2
  3. giveBack(): fine Rs 2/day after 14 days
  4. Reports: books out, fines collected

Run with the SQLite driver: java -cp ".;sqlite-jdbc.jar" LibraryDb.java

🏢 Used in real projects: Library management systems in schools and colleges. Extend it: a Scanner menu, member search, overdue SMS list.
LibraryDb.java  JAVA
1import java.sql.*;
2import java.time.*;
3import java.time.temporal.ChronoUnit;
4
5public class LibraryDb {
6 static Connection c;
7 static void sql(String s) throws SQLException { try (Statement st = c.createStatement()) { st.executeUpdate(s); } }
8 static int count(String q, Object... p) throws SQLException {
9 try (PreparedStatement ps = c.prepareStatement(q)) { for (int i = 0; i < p.length; i++) ps.setObject(i + 1, p[i]); return ps.executeQuery().getInt(1); }
10 }
11 static void issue(int book, int member, LocalDate d) throws SQLException {
12 if (count("SELECT COUNT(*) FROM issue WHERE book_id=? AND returned IS NULL", book) > 0) { System.out.println("X book " + book + " already out"); return; }
13 if (count("SELECT COUNT(*) FROM issue WHERE member_id=? AND returned IS NULL", member) >= 2) { System.out.println("X member " + member + " has 2 books"); return; }
14 try (PreparedStatement ps = c.prepareStatement("INSERT INTO issue (book_id, member_id, issued) VALUES (?, ?, ?)")) {
15 ps.setInt(1, book); ps.setInt(2, member); ps.setString(3, d.toString()); ps.executeUpdate();
16 }
17 System.out.println("Issued book " + book + " to member " + member + " on " + d);
18 }
19 static void giveBack(int book, LocalDate d) throws SQLException {
20 try (PreparedStatement q = c.prepareStatement("SELECT id, issued FROM issue WHERE book_id=? AND returned IS NULL")) {
21 q.setInt(1, book);
22 ResultSet rs = q.executeQuery();
23 if (!rs.next()) return;
24 long late = ChronoUnit.DAYS.between(LocalDate.parse(rs.getString(2)).plusDays(14), d);
25 int fine = (int) Math.max(0, late) * 2;
26 try (PreparedStatement u = c.prepareStatement("UPDATE issue SET returned=?, fine=? WHERE id=?")) {
27 u.setString(1, d.toString()); u.setInt(2, fine); u.setInt(3, rs.getInt(1)); u.executeUpdate();
28 }
29 System.out.println("Returned book " + book + " on " + d + (fine > 0 ? ", fine Rs " + fine : ", no fine"));
30 }
31 }
32 public static void main(String[] args) throws SQLException {
33 c = DriverManager.getConnection("jdbc:sqlite:library.db");
34 for (String t : new String[]{"issue", "book", "member"}) sql("DROP TABLE IF EXISTS " + t);
35 sql("CREATE TABLE book (id INTEGER PRIMARY KEY, title TEXT)");
36 sql("CREATE TABLE member (id INTEGER PRIMARY KEY, name TEXT)");
37 sql("CREATE TABLE issue (id INTEGER PRIMARY KEY AUTOINCREMENT, book_id INT, member_id INT, issued TEXT, returned TEXT, fine INT DEFAULT 0)");
38 sql("INSERT INTO book VALUES (1,'Godan'),(2,'Wings of Fire'),(3,'Malgudi Days'),(4,'Panchatantra')");
39 sql("INSERT INTO member VALUES (10,'Asha'),(20,'Vikas')");
40 LocalDate d0 = LocalDate.of(2026, 10, 1);
41 issue(1, 10, d0); issue(2, 10, d0); issue(3, 10, d0); issue(1, 20, d0); issue(4, 20, d0.plusDays(3));
42 giveBack(1, d0.plusDays(10)); giveBack(2, d0.plusDays(25));
43 ResultSet rs = c.createStatement().executeQuery("SELECT b.title, m.name, i.issued FROM issue i JOIN book b ON b.id=i.book_id JOIN member m ON m.id=i.member_id WHERE i.returned IS NULL");
44 System.out.println("-- Books out --");
45 while (rs.next()) System.out.println(rs.getString(1) + " with " + rs.getString(2) + " since " + rs.getString(3));
46 System.out.println("Fines collected: Rs " + count("SELECT COALESCE(SUM(fine),0) FROM issue"));
47 c.close();
48 }
49}
PracticePractice 9 of 10 · Hard

Capstone 4: Railway Reservation with Waitlist

HardScenario. Book berths on a train: confirmed seats, then a waitlist queue; cancellations promote the first waitlisted passenger; concurrent bookings are thread-safe.
💡 Concept used: TreeMap seat chart, ArrayDeque waitlist, synchronized methods, ExecutorService for concurrent users, records and PNR generation.

Your tasks

  1. Train with 6 berths, TreeMap<Integer, Passenger>
  2. book(): seat or WL position (synchronized)
  3. cancel(pnr): free seat, promote WL head
  4. 8 concurrent booking threads, then cancellations and the final chart
🏢 Used in real projects: IRCTC-style reservation engines manage charts, waitlists and concurrent bookings. Extend it: RAC, coaches, SQLite storage.
Railway.java  JAVA
1import java.util.*;
2import java.util.concurrent.*;
3import java.util.concurrent.atomic.AtomicInteger;
4
5record Passenger(int pnr, String name, int age) { }
6
7class Train {
8 private final String name; private final int berths;
9 private final TreeMap<Integer, Passenger> chart = new TreeMap<>();
10 private final Deque<Passenger> waitlist = new ArrayDeque<>();
11 private static final AtomicInteger PNR = new AtomicInteger(4510000);
12 Train(String name, int berths) { this.name = name; this.berths = berths; }
13
14 synchronized Passenger book(String n, int age) {
15 Passenger p = new Passenger(PNR.incrementAndGet(), n, age);
16 for (int s = 1; s <= berths; s++) if (!chart.containsKey(s)) { chart.put(s, p); return p; }
17 waitlist.addLast(p);
18 return p;
19 }
20 synchronized String cancel(int pnr) {
21 for (var e : chart.entrySet()) {
22 if (e.getValue().pnr() == pnr) {
23 int seat = e.getKey();
24 chart.remove(seat);
25 Passenger next = waitlist.pollFirst();
26 if (next != null) { chart.put(seat, next); return "PNR " + pnr + " cancelled; seat " + seat + " -> " + next.name() + " (from WL)"; }
27 return "PNR " + pnr + " cancelled; seat " + seat + " free";
28 }
29 }
30 return waitlist.removeIf(p -> p.pnr() == pnr) ? "PNR " + pnr + " removed from WL" : "PNR " + pnr + " not found";
31 }
32 synchronized void printChart() {
33 System.out.println("== " + name + " ==");
34 chart.forEach((s, p) -> System.out.printf(" Berth %d: %s (%d)%n", s, p.name(), p.age()));
35 int i = 1;
36 for (Passenger p : waitlist) System.out.println(" WL" + i++ + ": " + p.name());
37 }
38 synchronized int confirmed() { return chart.size(); }
39 synchronized int pnrAtSeat(int s) { return chart.get(s).pnr(); }
40}
41
42public class Railway {
43 public static void main(String[] args) throws Exception {
44 Train t = new Train("18625 Hatia-Patna Exp (S1)", 6);
45 String[] names = {"Anil", "Babita", "Chandan", "Deepa", "Eklavya", "Farida", "Gopal", "Heena"};
46 ExecutorService pool = Executors.newFixedThreadPool(4);
47 for (int i = 0; i < names.length; i++) { int age = 20 + i * 5; String n = names[i]; pool.submit(() -> t.book(n, age)); }
48 pool.shutdown();
49 pool.awaitTermination(5, TimeUnit.SECONDS);
50 System.out.println("Confirmed after rush: " + t.confirmed() + " of 8 requests (2 on waitlist)");
51 System.out.println(t.cancel(t.pnrAtSeat(2)));
52 System.out.println(t.cancel(t.pnrAtSeat(5)));
53 System.out.println(t.cancel(9999999));
54 System.out.println("Final: " + t.confirmed() + " confirmed");
55 }
56}
PracticePractice 10 of 10 · Hard

Capstone 5: E-commerce Orders and Analytics

HardScenario. An online store: products, cart, coupons, orders with status events, and a sales dashboard with streams.
💡 Concept used: Records, enums with data, sealed coupon types with pattern matching, Optional, Map/List collections and Collectors analytics: modern Java end to end.

Your tasks

  1. enum Category with GST rate; record Product, CartLine, Order
  2. sealed Coupon: Flat, Percent, FreeShipping
  3. checkout(): subtotal, GST, coupon, shipping
  4. Analytics: revenue by category, top products, average order value
🏢 Used in real projects: This is the core of Flipkart/Meesho-style checkout and seller dashboards. Extend it: persist orders in SQLite, add order status events (Module 20), expose it later as a Spring Boot REST API.
ECommerce.java  JAVA
1import java.util.*;
2import java.util.stream.*;
3
4enum Category { FASHION(12), ELECTRONICS(18), GROCERY(5); final int gst; Category(int g) { gst = g; } }
5record Product(String sku, String name, Category cat, double price) { }
6record CartLine(Product p, int qty) { double amount() { return p.price() * qty; } }
7sealed interface Coupon permits Flat, Percent, FreeShipping { }
8record Flat(double off, double min) implements Coupon { }
9record Percent(int pct, double max) implements Coupon { }
10record FreeShipping() implements Coupon { }
11record Order(String id, String city, List<CartLine> lines, double total) { }
12
13class Store {
14 final List<Order> orders = new ArrayList<>();
15 private int seq = 1000;
16 Order checkout(String city, List<CartLine> cart, Optional<Coupon> coupon) {
17 double sub = cart.stream().mapToDouble(CartLine::amount).sum();
18 double gst = cart.stream().mapToDouble(l -> l.amount() * l.p().cat().gst / 100).sum();
19 double ship = sub >= 999 ? 0 : 49;
20 double off = coupon.map(c -> switch (c) {
21 case Flat f when sub >= f.min() -> f.off();
22 case Flat f -> 0.0;
23 case Percent p -> Math.min(sub * p.pct() / 100, p.max());
24 case FreeShipping fs -> ship;
25 }).orElse(0.0);
26 Order o = new Order("OD" + (++seq), city, cart, Math.round((sub + gst + ship - off) * 100) / 100.0);
27 orders.add(o);
28 System.out.printf("%s %-9s sub %8.2f gst %7.2f ship %3.0f off %6.2f => Rs %9.2f%n", o.id(), city, sub, gst, ship, off, o.total());
29 return o;
30 }
31}
32
33public class ECommerce {
34 public static void main(String[] args) {
35 Product kurta = new Product("F1", "Cotton kurta", Category.FASHION, 899), buds = new Product("E1", "Earbuds", Category.ELECTRONICS, 1499),
36 rice = new Product("G1", "Basmati 5kg", Category.GROCERY, 545), watch = new Product("E2", "Smart watch", Category.ELECTRONICS, 2999);
37 Store s = new Store();
38 s.checkout("Ranchi", List.of(new CartLine(kurta, 2), new CartLine(rice, 1)), Optional.of(new Flat(200, 1500)));
39 s.checkout("Dhanbad", List.of(new CartLine(buds, 1)), Optional.of(new Percent(10, 100)));
40 s.checkout("Ranchi", List.of(new CartLine(rice, 1)), Optional.of(new FreeShipping()));
41 s.checkout("Jamshedpur", List.of(new CartLine(watch, 1), new CartLine(buds, 2)), Optional.empty());
42 List<CartLine> all = s.orders.stream().flatMap(o -> o.lines().stream()).toList();
43 System.out.println("Revenue by category: " + all.stream().collect(Collectors.groupingBy(l -> l.p().cat(), TreeMap::new, Collectors.summingDouble(CartLine::amount))));
44 System.out.println("Units by product: " + all.stream().collect(Collectors.groupingBy(l -> l.p().name(), TreeMap::new, Collectors.summingInt(CartLine::qty))));
45 System.out.println("Orders by city: " + s.orders.stream().collect(Collectors.groupingBy(Order::city, TreeMap::new, Collectors.counting())));
46 System.out.printf("Average order value: Rs %.2f%n", s.orders.stream().mapToDouble(Order::total).average().orElse(0));
47 }
48}
Module 21What next

Your Java Journey Continues

Core Java (done!)➜SQL + JDBC➜Spring Boot➜REST APIs + JPA➜Deploy
PathLearn next
Backend developerSpring Boot, Spring Data JPA, REST, MySQL/PostgreSQL
Android developerAndroid Studio, Kotlin (easy after Java), Jetpack
Competitive codingData structures and algorithms in Java
Testing / DevOpsJUnit, Mockito, Maven/Gradle, Git, Docker
💡 Concept in shortYou now know the core language every Java job builds on. Keep coding daily: rebuild one capstone with a new feature each week, push it to GitHub, and practise explaining your design out loud.
  • Push all 10 projects to GitHub with READMEs
  • Solve 2 practice problems a day (HackerRank, LeetCode, CodeChef)
  • Build one Spring Boot REST API for a capstone
  • Prepare: OOP, collections, exceptions, streams, threads interview questions
Module 21Recap

Module 21 Recap & Quiz

What you learned

  • 1Plan: requirements -> classes/records -> layers -> small working version -> add features
  • 2Starters practise input, methods, files and lists
  • 3Capstones combine OOP, collections, exceptions, files/JDBC, streams and threads
  • 4Keep UI, rules (service) and storage (DAO) separate
  • 5Test normal, edge and invalid inputs every time
  • 6Publish your projects: they are your portfolio

Quick quiz

Where should SQL code live in a layered app?

Keep storage details in one layer.

Which structure suits a railway waitlist?

First waitlisted, first promoted.

Best first step for a new project?

Plan before coding.
PART 5 COMPLETE

Well done!

You finished Advanced Java & Capstone Projects: 4 modules and 40 practice examples.

Before moving on

  • Re-type every practice example yourself without looking
  • Save your work in D:\java-practice\part5
  • Try the quizzes again until you get all of them right
  • Change one thing in each example and predict the output before you run it
  • Solve the Hard examples without looking at the solution first
{ } Java java Main.java main()
☕ Code Lab🌐 Online compiler
Edit the code here. Java runs on your computer (or an online compiler), not in the browser.
Expected output of the original code
Run it on your computer
  1. Install JDK 21 (adoptium.net) and check: java -version
  2. Download .java into D:\java-practice
  3. Open a terminal there and type java Main.java
  4. Or click Copy code and paste it into the online compiler