INTRODUCTION:
This is a complete walkthrough of how my console‑based Airline Reservation System works. I’ll explain the flow exactly like I built it: what loads first, which methods run, what data structures I used, and the logic inside each feature (view, book, cancel, admin). I’ll include small snippets only; the full code is on GitHub.
WHAT THE PROGRAM DOES:
- Stores flights (ID, source, destination, price).
- Shows available flights with the number of free seats.
- Lets a user book a seat and automatically writes the booking to file + generates a small ticket text file.
- Lets a user cancel a booking (seat becomes free again) and removes that booking record from file.
- Has a simple admin area (password‑protected) to add/remove flights.
- Uses plain text files for persistence under a Data/ folder.
1) PROJECT SKELETON & STATE (The variables that hold everything)
At the top of the program I define constants and arrays to hold all the in‑memory state:
static final int MAX_FLIGHTS = 100;
static final int ROWS = 5; // seat grid: A–E
static final int COLS = 4; // seat numbers: 1–4
static String[] flightIDs = new String[MAX_FLIGHTS];
static String[] sources = new String[MAX_FLIGHTS];
static String[] dests = new String[MAX_FLIGHTS];
static double[] prices = new double[MAX_FLIGHTS];
// seats[flightIndex][row][col] holds 'O' (open) or 'X' (booked)
static char[][][] seats = new char[MAX_FLIGHTS][ROWS][COLS];
static int flightCount = 0;
static Scanner scanner = new Scanner(System.in);
Why I chose this:
- Arrays keep it simple for a first‑semester project (no DB).
- Seats are a 3D array: flight → row → column. I label rows A..E and columns 1..4, so seat codes look like A1, B3, E4.
2) THE BOOT SEQUENCE - main()
When the app starts, it immediately loads saved data and then opens the menu:
public static void main(String[] args) {
loadFlights(); // read Data/flights.txt
loadBookings(); // read Data/bookings.txt and mark seats as booked
mainMenu(); // show options and route to features
}
That’s the entire startup story: load flights → load bookings → show menu.
3) LOADING FLIGHTS - loadFlights()
Flights are stored in CSV format inside Data/flights.txt, one flight per line:
<FLIGHT_ID>,<SOURCE>,<DESTINATION>,<PRICE>
Example:
PK101,ISB,LHE,7500
PK102,ISB,KHI,12000
The method:
- Ensures the Data/ folder exists.
- If flights.txt doesn’t exist, it prints a friendly message and returns.
- Reads the file line‑by‑line, splits by comma, and fills the arrays.
- Initializes every seat to 'O' (open) for the new flight.
Key snippet:
File file = new File("Data\\flights.txt");
file.getParentFile().mkdirs();
if (!file.exists()) {
System.out.println("Data\\flights.txt not found. No flights loaded.");
return;
}
Scanner fileScanner = new Scanner(file);
while (fileScanner.hasNextLine() && flightCount < MAX_FLIGHTS) {
String line = fileScanner.nextLine().trim();
if (line.isEmpty()) continue;
String[] parts = line.split(",");
if (parts.length < 4) continue; // guard against bad lines
flightIDs[flightCount] = parts[0];
sources[flightCount] = parts[1];
dests[flightCount] = parts[2];
prices[flightCount] = Double.parseDouble(parts[3]);
// set all seats to Open for this flight
for (int r = 0; r < ROWS; r++)
for (int c = 0; c < COLS; c++)
seats[flightCount][r][c] = 'O';
flightCount++;
}
Result: after loadFlights(), I have a list of flights in memory and an all‑open seat map for each one.
4) LOADING BOOKINGS - loadBookings()
Bookings live in Data/bookings.txt. Each line is a CSV record:
<FLIGHT_ID>,<NAME>,<SEAT>
Example:
PK101,Ali,A1
PK101,Arham,B2
This Method:
- Replays these bookings back into the in‑memory seat maps:
- It reads each record, finds the corresponding flight index by FLIGHT_ID.
- It converts the seat code (e.g., A1) into row/col indices.
- It marks that seat 'X' (booked) inside seats[flightIndex][row][col].
Key conversion logic:
String seat = parts[2].toUpperCase();
int row = seat.charAt(0) - 'A'; // 'A'→0, 'B'→1, ...
int col = Integer.parseInt(seat.substring(1)) - 1; // "1"→0, "2"→1, ...
if (row >= 0 && row < ROWS && col >= 0 && col < COLS) {
seats[index][row][col] = 'X';
}
Result: after loadBookings(), every seat that was booked earlier shows up as 'X' in memory.
5) THE USER EXPERIENCE - mainMenu()
This is the console menu that loops forever until the user exits:
=============================
| FLIGHT RESERVATION SYSTEM |
=============================
1. View all flights
2. Book ticket
3. Cancel ticket
4. Admin login
5. Exit
Implementation outline:
while (true) {
// print menu
String choice = scanner.nextLine();
switch (choice) {
case "1": displayFlights(); break;
case "2": bookTicket(); break;
case "3": cancelTicket(); break;
case "4": adminMenu(); break;
case "5": System.exit(0); break;
default: System.out.println("Invalid choice.");
}
}
Idea: keep mainMenu() dumb and let each feature method handle its own input/validation.
6) SHOWING FLIGHTS - displayFlights()
I print a small table with ID, From, To, Price, Seats Left. The “Seats Left” value is computed by counting 'O' in the seat grid of each flight:
System.out.printf("%-10s %-10s %-10s %-10s %-10s%n",
"ID", "From", "To", "Price", "Seats Left");
for (int i = 0; i < flightCount; i++) {
int free = 0;
for (int r = 0; r < ROWS; r++)
for (int c = 0; c < COLS; c++)
if (seats[i][r][c] == 'O') free++;
System.out.printf("%-10s %-10s %-10s %-10.1f %-10d%n",
flightIDs[i], sources[i], dests[i], prices[i], free);
}
Why it matters: before booking, the user sees availability at a glance.
7) BOOKING A TICKET - bookTicket() [Stept by step]
This is the most interactive method. Here’s the flow:
7.1) Pick a flight
First I call displayFlights() so the user can see options.
Then I ask for a Flight ID and find its index.
System.out.print("Enter Flight ID to book: ");
String flightID = scanner.nextLine().toUpperCase();
int index = findFlightIndex(flightID); // implemented as a simple loop
if (index == -1) { System.out.println("Flight not found."); return; }
Under the hood I simply loop 0..flightCount-1 and compare flightIDs[i].equals(flightID).
7.2) Show the seat map
I print a neat grid so users can visually choose an available seat:
1 2 3 4
A O O X O
B O O O O
C O O O O
D O O O O
E O O O O
Snippet I use for printing:
System.out.print(" ");
for (int c = 1; c <= COLS; c++) System.out.print(c + " ");
System.out.println();
for (int r = 0; r < ROWS; r++) {
System.out.print((char)('A' + r) + " ");
for (int c = 0; c < COLS; c++) {
System.out.print(seats[index][r][c] + " ");
}
System.out.println();
}
7.3) Read & validate the seat code
I accept seat codes like A1, C3, etc.
I validate format and range.
System.out.print("Enter seat (e.g., A1): ");
String seat = scanner.nextLine().toUpperCase();
if (seat.length() < 2) { System.out.println("Invalid seat format."); return; }
int row = seat.charAt(0) - 'A';
int col;
try { col = Integer.parseInt(seat.substring(1)) - 1; }
catch (NumberFormatException e) { System.out.println("Invalid seat number."); return; }
if (row < 0 || row >= ROWS || col < 0 || col >= COLS) {
System.out.println("Seat out of range.");
return;
}
if (seats[index][row][col] == 'X') { System.out.println("Seat already booked."); return; }
7.4) Capture name & confirm
System.out.print("Enter your name: ");
String name = scanner.nextLine();
if (name.trim().isEmpty()) { System.out.println("Name cannot be empty."); return; }
7.5) Commit in memory + persist to file
Mark the seat as booked in memory.
Append a line to Data/bookings.txt.
Generate a small ticket file with the booking details.
seats[index][row][col] = 'X';
// 1) Append to bookings.txt
FileWriter fw = new FileWriter("Data\\bookings.txt", true);
fw.write(flightID + "," + name + "," + seat + "\n");
fw.close();
// 2) Generate a ticket text file
String ticketFile = "Data\\ticket_" + flightID + "_" + seat + ".txt";
PrintWriter tw = new PrintWriter(ticketFile);
tw.println("||| FLIGHT TICKET DETAILS |||");
tw.println("Name: " + name);
tw.println("Flight ID: " + flightID);
tw.println("From: " + sources[index]);
tw.println("To: " + dests[index]);
tw.println("Seat: " + seat);
tw.println("Price: " + prices[index]);
tw.close();
That’s it, the booking is reflected both in memory and on disk. If you restart the app, loadBookings() will replay it.
8) CANCEL A BOOKING - cancelTicket() [Step by step]
The cancellation mirrors booking, with a careful extra step to remove the line from bookings.txt.
8.1) Identify the booking
System.out.print("Enter Flight ID to cancel: ");
String flightID = scanner.nextLine().toUpperCase();
int index = findFlightIndex(flightID);
if (index == -1) { System.out.println("Flight not found."); return; }
System.out.print("Enter seat (e.g., A1): ");
String seat = scanner.nextLine().toUpperCase();
// validate row/col exactly like in booking
8.2) Free the seat in memory
if (seats[index][row][col] == 'O') { System.out.println("This seat is not currently booked."); return; }
seats[index][row][col] = 'O';
8.3) Remove the record from bookings.txt safely (temp‑file swap)
I use the common temp file pattern to rewrite the bookings file without the canceled line:
File file = new File("Data\\bookings.txt");
File temp = new File("Data\\temp_bookings.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
PrintWriter writer = new PrintWriter(new FileWriter(temp));
String line; boolean found = false;
while ((line = reader.readLine()) != null) {
if (line.trim().isEmpty()) continue;
String[] parts = line.split(",");
if (parts.length < 3) continue;
String id = parts[0];
String bookedSeat = parts[2];
if (id.equals(flightID) && bookedSeat.equals(seat)) { found = true; continue; }
writer.println(line); // keep all other lines
}
reader.close(); writer.close();
// replace original with temp
if (file.delete()) {
if (!temp.renameTo(file)) System.out.println("Error updating bookings file.");
} else {
System.out.println("Error updating bookings file.");
}
Why temp files? It’s a safer way to “edit” a flat file: write a new file with everything you want to keep, then swap.
9) ADMIN AREA - adminMenu()
Admin entry is protected by a simple password (for demo only):
System.out.print("Enter admin password: ");
String pwd = scanner.nextLine();
if (!pwd.equals("admin123")) { System.out.println("Incorrect password."); return; }
Admin options are:
1) Add flight
2) Remove flight
3) Back to main menu
The admin loop dispatches to addFlight() and removeFlight().
In a real system, you’d never hard‑code passwords, for this assignment it’s fine.
10) ADDING A FLIGHT - addFlight()
The method does four things: 1) Validates inputs (non‑empty ID, unique ID, valid price). 2) Sets up the seat map ('O' everywhere). 3) Appends the new flight to Data/flights.txt (CSV). 4) Increments flightCount.
Key bits:
String flightID = scanner.nextLine().toUpperCase();
// check duplicates
for (int i = 0; i < flightCount; i++)
if (flightIDs[i].equals(flightID)) { System.out.println("Flight ID already exists."); return; }
// read src, dst, price ...
for (int r = 0; r < ROWS; r++)
for (int c = 0; c < COLS; c++)
seats[flightCount][r][c] = 'O';
PrintWriter pw = new PrintWriter(new FileWriter("Data\\flights.txt", true));
pw.println(flightID + "," + src + "," + dst + "," + price);
pw.close();
flightCount++;
Note: I also ensure the Data/ folder exists before writing.
11) REMOVING A FLIGHT - removeFlight()
Removal touches both memory and files:
11.1) Remove from in‑memory arrays
I find the flight by ID and shift arrays left to fill the gap:
int index = findFlightIndex(flightID);
if (index == -1) { System.out.println("Flight not found."); return; }
for (int i = index; i < flightCount - 1; i++) {
flightIDs[i] = flightIDs[i + 1];
sources[i] = sources[i + 1];
dests[i] = dests[i + 1];
prices[i] = prices[i + 1];
seats[i] = seats[i + 1];
}
flightCount--;
11.2) Rewrite flights.txt without this flight
Again I use the temp‑file approach to keep only the remaining flights.
11.3) Also clean up bookings.txt
I remove all bookings that belong to the deleted flight, then swap the temp file into place.
Result: the flight disappears from memory, from flights.txt, and all its bookings are purged from bookings.txt.
12) INPUT VALIDATION & EDGE CASES HANDLED
Missing files: if flights.txt or bookings.txt don’t exist, the app doesn’t crash; it prints a hint and continues.
Bad CSV lines: I check parts.length and skip malformed rows.
Seat format: I verify A..E and 1..4 ranges, and reject invalid strings.
Duplicate flight IDs on add: not allowed.
Empty names: not allowed.
Seat already booked / not booked: handled with clear messages.
These small checks make the console UX feel a lot more professional.
13) FILE FORMATS (So you can recreate them)
Data/flights.txt, one line per flight:
FLIGHT_ID,SOURCE,DEST,PRICE
PK101,ISB,LHE,7500
PK102,ISB,KHI,12000
Data/bookings.txt, one line per booking:
FLIGHT_ID,NAME,SEAT
PK101,Ali,A1
PK102,Sara,B3
Ticket files, created automatically on booking: Data/ticket_<FLIGHT_ID>_<SEAT>.txt
14) OTHER IMPORTANT THINGS
14.1) EXECUTION MAP
Start
└─ main()
├─ loadFlights() // build flights & seat maps in memory
├─ loadBookings() // mark previously booked seats as X
└─ mainMenu() // loop:
├─ displayFlights()
├─ bookTicket()
│ ├─ pick flight → show seat map
│ ├─ validate seat & name
│ ├─ mark 'X' in seats[][][]
│ ├─ append to bookings.txt
│ └─ write ticket_<id>_<seat>.txt
├─ cancelTicket()
│ ├─ validate flight & seat
│ ├─ mark 'O' in seats[][][]
│ └─ rewrite bookings.txt without that line
└─ adminMenu()
├─ addFlight() // append to flights.txt
└─ removeFlight() // shift arrays, rewrite flights.txt & bookings.txt
14.2) EDUCATIONAL VALUE FOR BEGINNERS.
- Arrays & 3D arrays: practical way to model flights and seating.
- File I/O: File, Scanner, BufferedReader, FileWriter, PrintWriter, temp‑file swaps.
- Input handling & validation: prevents crashes and improves UX.
- Modular design with methods: each feature lives in its own method, so mainMenu() stays clean.
- State + persistence: memory reflects the current session, files keep data across runs.
- All of this is first‑semester friendly, but the end result feels like a small real system.
14.3) IMPORTANT SCREENSHOTS
Main menu:
Flights table with available seats:
Seat grid before/after booking:
Ticket text file preview:
Admin interface sample:
14.4) SOURCE CODE LINK:
CLOSING NOTE:
If you’re a CS student starting out: pick a small but realistic problem, keep the data model simple, and wire persistence early. Even with only arrays and text files, you can ship something that looks and feels like a real app.
If you want the same structure: copy my method layout (loadFlights, loadBookings, displayFlights, bookTicket, cancelTicket, adminMenu, addFlight, removeFlight) and evolve it feature by feature.
0 Comments