THE SYLLABUS TASK
What you need to build
Create a Java program to maintain hospital patient records.
Full syllabus requirements: classes, fields and methods
One value per line. Commands: 1 register followed by ID/name/age/problem/doctor/fee; 2 ID/new doctor; 3 ID/additional fee; 4 display; 0 exit. IDs unique, age 0–130; at most 100 patients.
UNDERSTAND THE IDEA
Explanation
The Patient constructor uses this.field = parameter to distinguish instance fields from equally named parameters. A record keeps identity and clinical description together with its assigned doctor and accumulated fee.
The menu driver stores patients in an ArrayList and locates a record by ID for updates. A duplicate registration is consumed but ignored; doctor and fee updates act on the existing object.
TURN THE IDEA INTO STEPS
Step-by-step algorithm
- Read a menu command.
- For registration, validate fields and reject duplicate IDs.
- For updates, locate the matching patient.
- Change the doctor or add a positive fee.
- Display all current reports and continue until exit.
SEE THE CORE OPERATION
Flowchart
This chart focuses on the named operation or core OOP decision. Diamonds show conditions; labeled branches show outcomes. Read it with the complete algorithm and source for input handling and other operations. Scroll within the diagram on a phone.
Update an existing patient
Main.java
import java.util.*;
public class Main {
private static final Scanner IN = new Scanner(System.in).useLocale(Locale.ROOT);
static String text() {
if (!IN.hasNextLine()) throw new IllegalArgumentException("Incomplete input.");
String value = IN.nextLine().trim();
if (value.isEmpty() || value.length() > 200) throw new IllegalArgumentException("Text must have 1–200 characters.");
return value;
}
static int integer(int low, int high) {
int value = Integer.parseInt(text());
if (value < low || value > high) throw new IllegalArgumentException("Integer out of range.");
return value;
}
static double number(double low, double high) {
double value = Double.parseDouble(text());
if (!Double.isFinite(value) || value < low || value > high) throw new IllegalArgumentException("Number out of range.");
return value;
}
static void money(String label, double value) { System.out.printf(Locale.ROOT, "%s: %.2f%n", label, value); }
static class Patient {
private final int patientId, age;
private final String name, problem;
private String doctorAssigned;
private double feePaid;
Patient(int patientId, String name, int age, String problem, String doctorAssigned, double feePaid) {
this.patientId = patientId; this.name = name; this.age = age; this.problem = problem;
this.doctorAssigned = doctorAssigned; this.feePaid = feePaid;
}
void updateDoctor(String doctor) { if (doctor.trim().isEmpty()) throw new IllegalArgumentException("Doctor required."); doctorAssigned = doctor; }
void addFee(double amount) {
if (!Double.isFinite(amount) || amount <= 0 || feePaid + amount > 1000000000) throw new IllegalArgumentException("Invalid fee."); feePaid += amount;
}
void displayReport() { System.out.printf(Locale.ROOT,"%d | %s | age %d | %s | %s | fee %.2f%n", patientId, name, age, problem, doctorAssigned, feePaid); }
}
static Patient find(List<Patient> patients, int id) {
for (Patient p : patients) if (p.patientId == id) return p;
throw new IllegalArgumentException("Patient not found.");
}
// Commands: 1=register (id/name/age/problem/doctor/fee), 2=id then new doctor,
// 3=id then additional fee, 4=display all, 0=exit. One field per line.
public static void main(String[] args) {
List<Patient> patients = new ArrayList<>();
System.out.println("1 Register | 2 Update doctor | 3 Add fee | 4 Display | 0 Exit");
try {
while (true) {
int command = integer(0, 4); if (command == 0) break;
if (command == 1) {
int id = integer(1, 1000000); String name = text(); int age = integer(0, 130);
String problem = text(), doctor = text(); double fee = number(0, 1000000);
boolean duplicate = patients.stream().anyMatch(p -> p.patientId == id);
if (duplicate) System.out.println("Duplicate patient ID ignored.");
else if (patients.size() == 100) System.out.println("Registry is full.");
else { patients.add(new Patient(id,name,age,problem,doctor,fee)); System.out.println("Registered."); }
} else if (command == 2) { Patient p = find(patients,integer(1,1000000)); p.updateDoctor(text()); System.out.println("Doctor updated."); }
else if (command == 3) { Patient p = find(patients,integer(1,1000000)); p.addFee(number(0,1000000)); System.out.println("Fee added."); }
else { System.out.println("Patients:"); for (Patient p : patients) p.displayReport(); }
}
} catch (IllegalArgumentException e) { System.out.println("Invalid input: " + e.getMessage()); }
}
}
Copy a source file or download the project to preserve its file names.
USE THE RIGHT ENVIRONMENT
How to compile and run
Install a JDK (Java 11 or newer; JDK 17 is suitable), extract this project’s ZIP into its own folder, then open a terminal in that folder.
javac -encoding UTF-8 Main.java
java MainThe standalone source is named Main.java and its entry class is Main. You may also paste it into an online Java compiler and supply the sample input there. Choose Java explicitly; these pages do not change the site compiler or import code automatically.
Each experiment has its own folder because many use the public class Main. Compile and run one experiment at a time.
UNDERSTAND THE JAVA CODE
Line-by-line explanation
These steps follow the exact Java source above. Related statements are grouped; line numbers count blank lines and restart for each file. Start with main to follow execution, then open the constructors, methods or event handlers it uses.
Main.java
Lines 1–4Import utilities and create the shared input reader
import java.util.*;
public class Main {
private static final Scanner IN = new Scanner(System.in).useLocale(Locale.ROOT);java.util.* imports utility type names such as Scanner, Locale and collections. Main is the public class matching Main.java. The private static final IN field holds one shared Scanner for System.in; final prevents replacing that reference. The scanner uses Locale.ROOT.
Lines 5–10Read one nonempty trimmed input line
static String text() {
if (!IN.hasNextLine()) throw new IllegalArgumentException("Incomplete input.");
String value = IN.nextLine().trim();
if (value.isEmpty() || value.length() > 200) throw new IllegalArgumentException("Text must have 1–200 characters.");
return value;
}text checks whether a line exists, reads it and removes leading/trailing whitespace. Reject empty text and lengths above 200 by throwing IllegalArgumentException. Otherwise return the string. One field is read per line, so these helpers do not mix nextInt with leftover newlines.
Lines 11–15Parse and range-check an integer field
static int integer(int low, int high) {
int value = Integer.parseInt(text());
if (value < low || value > high) throw new IllegalArgumentException("Integer out of range.");
return value;
}Integer.parseInt converts the returned string to int or throws NumberFormatException, a subtype of IllegalArgumentException. Reject values outside the caller’s inclusive low/high limits, then return the accepted integer.
Lines 16–20Parse and range-check a finite decimal field
static double number(double low, double high) {
double value = Double.parseDouble(text());
if (!Double.isFinite(value) || value < low || value > high) throw new IllegalArgumentException("Number out of range.");
return value;
}Double.parseDouble reads the string as a double. Double.isFinite rejects NaN and infinities as well as the explicit low/high range check. Return only a finite accepted value; exceptions transfer control to a matching catch block.
Line 21Format a labelled numeric result consistently
static void money(String label, double value) { System.out.printf(Locale.ROOT, "%s: %.2f%n", label, value); }money is a formatting helper: %s prints the label, %.2f prints two decimal places, and %n ends the line. Locale.ROOT fixes locale-sensitive formatting. The helper can also display non-currency quantities; it does not change the stored value.
Lines 22–26Represent one patient’s stable and editable fields
static class Patient {
private final int patientId, age;
private final String name, problem;
private String doctorAssigned;
private double feePaid;Patient stores final ID, age, name and problem, plus editable doctorAssigned and feePaid. Each object holds its own current record.
Lines 27–30Initialise a new patient object
Patient(int patientId, String name, int age, String problem, String doctorAssigned, double feePaid) {
this.patientId = patientId; this.name = name; this.age = age; this.problem = problem;
this.doctorAssigned = doctorAssigned; this.feePaid = feePaid;
}The constructor copies its supplied values into instance fields. The command driver validates ID, age and opening fee before calling it.
Lines 31–34Update the doctor or add a checked fee
void updateDoctor(String doctor) { if (doctor.trim().isEmpty()) throw new IllegalArgumentException("Doctor required."); doctorAssigned = doctor; }
void addFee(double amount) {
if (!Double.isFinite(amount) || amount <= 0 || feePaid + amount > 1000000000) throw new IllegalArgumentException("Invalid fee."); feePaid += amount;
}updateDoctor rejects blank trimmed text. addFee requires a positive finite amount and checks the resulting fee total before increasing it. A rejected fee leaves the old total unchanged.
Lines 35–36Print a patient report
void displayReport() { System.out.printf(Locale.ROOT,"%d | %s | age %d | %s | %s | fee %.2f%n", patientId, name, age, problem, doctorAssigned, feePaid); }
}Format the record fields and fee on one line. The closing brace ends Patient.
Lines 37–40Find an existing object by ID
static Patient find(List<Patient> patients, int id) {
for (Patient p : patients) if (p.patientId == id) return p;
throw new IllegalArgumentException("Patient not found.");
}Scan the list and return the matching Patient reference. The surrounding Main class can access this nested class’s private fields. Throw an error if no ID matches rather than returning an unrelated record.
Lines 41–46Start an empty registry and explain the input commands
// Commands: 1=register (id/name/age/problem/doctor/fee), 2=id then new doctor,
// 3=id then additional fee, 4=display all, 0=exit. One field per line.
public static void main(String[] args) {
List<Patient> patients = new ArrayList<>();
System.out.println("1 Register | 2 Update doctor | 3 Add fee | 4 Display | 0 Exit");
try {Use an ArrayList of Patient references and print the available operations. The try block encloses the command-reading session.
Lines 47–51Read a command and the complete registration fields
while (true) {
int command = integer(0, 4); if (command == 0) break;
if (command == 1) {
int id = integer(1, 1000000); String name = text(); int age = integer(0, 130);
String problem = text(), doctor = text(); double fee = number(0, 1000000);Exit on 0. For registration, validate ID, age and fee and read each text field. Reading a field does not yet add a Patient to the registry.
Lines 52–55Reject a duplicate ID or full registry before adding
boolean duplicate = patients.stream().anyMatch(p -> p.patientId == id);
if (duplicate) System.out.println("Duplicate patient ID ignored.");
else if (patients.size() == 100) System.out.println("Registry is full.");
else { patients.add(new Patient(id,name,age,problem,doctor,fee)); System.out.println("Registered."); }anyMatch uses a lambda predicate to test whether an existing patient has the same ID. Only a new ID within the 100-record limit creates and adds a Patient.
Lines 56–59Update the found patient or display every record
} else if (command == 2) { Patient p = find(patients,integer(1,1000000)); p.updateDoctor(text()); System.out.println("Doctor updated."); }
else if (command == 3) { Patient p = find(patients,integer(1,1000000)); p.addFee(number(0,1000000)); System.out.println("Fee added."); }
else { System.out.println("Patients:"); for (Patient p : patients) p.displayReport(); }
}find returns the actual object stored in the list, so changing its doctor or fee changes the registry record. Display loops through the stored objects. The brace ends command processing.
Lines 60–62Report invalid input and close the enclosing scopes
} catch (IllegalArgumentException e) { System.out.println("Invalid input: " + e.getMessage()); }
}
}The catch handles IllegalArgumentException, including invalid numeric strings from the helpers, and prints its message. The final braces close main and Main. Java objects become eligible for garbage collection when no live reference needs them; this program does not manually free them.
FOLLOW THE STATE
Dry run
- Patient 1 registers with Dr A and fee 100.
- Updating ID 1 changes doctorAssigned to Dr B.
- Adding fee 50 produces feePaid 150.
- The displayed report retains the original ID, name, age and problem.
CHECK THE BEHAVIOUR
Sample input & output
These outputs were captured by running the exact delivered Java project.
Sample 1
1
1
Ravi
20
Fever
Dr A
100
2
1
Dr B
3
1
50
4
0
1 Register | 2 Update doctor | 3 Add fee | 4 Display | 0 Exit
Registered.
Doctor updated.
Fee added.
Patients:
1 | Ravi | age 20 | Fever | Dr B | fee 150.00
Sample 2
1
1
Ravi
20
Fever
Dr A
100
1
1
Priya
19
Cold
Dr B
50
4
0
1 Register | 2 Update doctor | 3 Add fee | 4 Display | 0 Exit
Registered.
Duplicate patient ID ignored.
Patients:
1 | Ravi | age 20 | Fever | Dr A | fee 100.00
Common mistakes
- Without this, assigning a parameter to itself leaves the field unchanged.
- Use a unique patient ID to select updates.
- addFee accumulates; it does not replace the paid total.
WHY THIS GROWTH RATE?
Time and space complexity
Lookup/duplicate checking O(n); one report O(L); all reports O(nL); stored records O(n), excluding text.
Finding an ID scans at most n references in this implementation. Registration also checks the list for a duplicate ID, so it is not unconditional O(1).
Once a patient is located, replacing a doctor reference or adding a fee is fixed state work. Displaying every report additionally writes its text.
These bounds describe the implemented data processing. Input/output text length, scheduling, framework rendering and runtime/library internals are identified separately where they matter. Educational limits bound the actual examples.
PREPARE FOR YOUR LAB VIVA
Related viva questions & answers
Try answering aloud, then expand to check your reasoning. Questions focus on this program’s classes, state and behavior.
What does this.patientId = patientId mean?
The left side is the current object’s field; the right side is the constructor parameter. The assignment copies the supplied ID into that object.
Why is registration prevented for a duplicate patient ID?
Updates locate a patient by that identifier. Allowing duplicates would make the target ambiguous and could modify the wrong record.
Why does updateDoctor modify an existing Patient rather than create a new one?
The patient’s identity and other data remain the same. Replacing just the doctor field expresses the requested update without losing the accumulated fee.
How does addFee differ from setting feePaid directly?
It validates a positive amount and adds it to the existing total. A direct replacement would discard earlier payments.
Why is the Patient lookup linear here?
The ArrayList stores records but has no ID-to-record index. The find method checks IDs one by one until a match is found.
What happens to a failed lookup in this menu driver?
It throws a clear IllegalArgumentException, which the driver reports before ending the invalid session. It never fabricates or updates a nonexistent patient.