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: 

🔗 GitHub Repo


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.