The GST rule that changed
Riya built a billing program for the college canteen. The 5% GST calculation appears in three places: the counter bill, the daily report and the monthly summary. Then the accountant asks her to round the tax to the nearest rupee. She fixes the counter bill and the report, forgets the summary, and the month's totals no longer match.
Sir, I changed the GST code in two places and missed the third. Now the summary is off by a few rupees.
That's the danger of copy-paste. Put the rule in a method: gst(amount). Write it once, then call it from the bill, the report and the summary.
So when the rule changes again, I edit only the method?
Exactly. And your main becomes a short list of steps with good names, like a table of contents. Today we'll also see what the JVM does every time you call a method: it pushes a frame on the stack.
What is a method?
A method is a named block of statements, declared inside a class, that performs a specific task. It is declared once with a header (modifiers, return type, name and parameter list) and a body, and executed whenever it is called (invoked). Each call creates a new stack frame holding the method's parameters and local variables; when the method finishes, it returns control (and optionally a value) to the caller and its frame is removed.
A method is a recipe with a name. You write the recipe once. Whenever you need that dish, you say its name ("make chai"), the steps run, and you may get something back (a cup of chai).
Words you'll meet in this lesson
printLine().void for none.square(int).Method facts at a glance
| Header | static int square(int n) = modifier, return type, name, parameter list |
|---|---|
| Where | Directly inside a class, never inside another method. Order in the class doesn't matter. |
| void | Does a job and gives nothing back. May use return; to stop early. |
| Returning | Must end with return value; of the declared type. The call can be used as a value. |
| static (for now) | Lets main call the method directly, without creating an object (Lesson 6.4 explains). |
| Naming | camelCase verb phrases: calculateTotal, printReceipt, isValid |
Exam tip: the method signature is the name and the parameter list only. The return type, modifiers and parameter names are not part of it. So int add(int a, int b) and long add(int x, int y) have the same signature, and can't both exist in one class.
Three shapes of a method
No input, no result
static void printLine()
Does the same job every time, like printing a divider.
Takes input
static void greet(String name)
The job depends on the value passed in.
Gives a result
static int square(int n)
Calculates and returns a value the caller can use.
public class Main {
static void printLine() {
System.out.println("--------------------");
}
static void greet(String name) {
System.out.println("Welcome, " + name + "!");
}
static int square(int n) {
return n * n;
}
public static void main(String[] args) {
printLine();
greet("Riya");
int s = square(7);
System.out.println("7 squared is " + s);
printLine();
}
}void vs returning methods
void method | Returning method | |
|---|---|---|
| Header | static void printBill() | static double gst(double amt) |
| Gives back | Nothing | One value of the declared type |
return | Optional; only return; (no value) | Required on every path: return value; |
| How it's called | As a statement: printBill(); | Usually inside an expression: total = amt + gst(amt); |
| Typical job | Printing, saving, sending | Calculating, checking, converting |
Classic mistakes: forgetting the brackets (printLine; is a compile error), writing a method inside main (methods go directly inside the class), and calling a non-static method from main (compile error: non-static method cannot be referenced from a static context).
Writing and calling a method
Write the header
Modifierstatic double gst(double amount)static, return typedouble, namegst, one parameter. javac records this as the method's descriptor and checks every call against it.Write the body
The body is compiled into its own block of bytecode. It runs only when someone calls the method, never just because it is written in the file.{ return amount * 0.05; }Call it
The call compiles to andouble tax = gst(200);invokestaticinstruction. The JVM pushes a new frame on the stack, copies the argument (200 widened to 200.0) intoamount, and jumps into the body.mainwaits.Return
The
The value is passed to the caller, the method's frame is popped (its variables vanish), and execution continues inreturnstatement ends the method and hands back 10.0.mainright after the call, which stores 10.0 intax.Call it again, from anywhere
Methods can call other methods. Each call gets its own fresh frame, so the stack grows with nested calls (main → total → gst) and shrinks as each one returns: last in, first out.static double total(double amount) { return amount + gst(amount); }
Frames pushed and popped: both views
Left: the line running right now and what it produces. Right: the call stack, where each method in progress has its own frame.
main → total → gst 6 steps- Call:
mainreachestotal(200). It can't finish this line untiltotalgives back a value, so it waits. - Nested call:
totalstarts withamount = 200.0, and its return line needsgst(amount)first. - Deepest call:
gstcalculates 200.0 × 0.05 = 10.0. - Return:
gsthands back 10.0.totalresumes and adds: 210.0. - Return again:
totalhands back 210.0 andmainstores it int. - Print: the only real output of the program is
210.0.
- The stack holds one frame for
main. Its variablethas no value yet. - Calling
totalpushes a frame on top. The argument 200 was widened to 200.0 and copied intoamount. - Calling
gstpushes a third frame. It has its ownamount, separate from total's, even though the name is the same. gstreturns: its frame is popped and 10.0 is handed to the frame below.totalreturns 210.0 and is popped. The most recent call always finishes first: last in, first out.- Only
mainis left. When it ends, the stack is empty and the program exits.
Methods in real systems
Real applications are thousands of small methods calling each other. Good method names are how teams find their way around a large codebase.
Watch it with an example: one GST method, three bills
The GST rule is written once, in gst(int price), and called for every item. Watch each call push a new frame on the stack and pop it when it returns.
- Before:
mainhas its frame on the stack withtotal = 0. The GST rule is written in one place. - Call 1:
gst(100)pushes a new frame. Its parameterpricegets the value 100. - Return: the method computes 18, returns it, and its frame is popped.
totalbecomes 18. - Call 2:
gst(250): a fresh frame with price = 250 returns 45. Total 63. - Call 3:
gst(50)returns 9. Total 72. - Why methods: if the GST rate changes, you edit one line, not every bill. Real billing systems keep each business rule in one method like this.
1. Business rules in one place
Tax, discount, late fee and delivery charge rules each live in one method. When the government or the business changes a rate, one method changes and every screen and report follows.
2. Validation helpers
Sign-up and KYC forms check a mobile number, PIN code or PAN format. Each check is a small method like isValidPincode(String) that returns true or false and is reused by every form.
3. Stack traces in production logs
When a server crashes, the log shows a stack trace: the frames that were on the call stack, newest first. Developers read it to find which method failed and who called it. You'll see one in the runner below.
4. The Java library is methods
You've already called many methods: System.out.println, Math.max, Arrays.sort, name.length(). Someone wrote them once; millions of programs call them.
Examples are simplified. In larger programs, methods are grouped into classes (Module 6) and most are not static.
The canteen bill, split into methods
public class Main {
static void printHeader() {
System.out.println("=== College Canteen ===");
}
static int lineTotal(String item, int qty, int price) {
int amount = qty * price;
System.out.println(item + " x" + qty + " = Rs " + amount);
return amount;
}
public static void main(String[] args) {
printHeader();
int total = 0;
total += lineTotal("Samosa", 2, 15);
total += lineTotal("Chai", 3, 10);
total += lineTotal("Vada pav", 1, 20);
System.out.println("Total: Rs " + total);
}
}static void printHeader()No input, no result: it only prints.static int lineTotal(String item, int qty, int price)Three parameters in, one int out. It also prints the line, then returns the amount.total += lineTotal("Samosa", 2, 15);The call runs first; its returned value (30) is then added tototal.mainReads like a list of steps. The details live inside well-named methods.
Calling the same method twice
Watch the stack: each call to square pushes a fresh frame, and each return pops it.
Worked examples: real problems, solved and traced in memory
Three small business programs split into methods. Run each one, then step through it and watch every call push a new frame on the stack and every return pop it off again.
Invoice lines with a helper method
Problem statement
A stationery shop's invoice has one line per item. Write a method lineTotal(qty, price) that returns the amount for one line, and use it for two items to get the invoice total.
Notebook: 3 x Rs 45 Pen: 4 x Rs 10
Notebooks: Rs 135 Pens: Rs 40 Total: Rs 175
Approach
- The rule (quantity × price) is written once, inside
lineTotal. maincalls it once per item and stores each returned value.- Watch the stack: during each call there are two frames, and the helper's frame disappears as soon as it returns.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Exam eligibility: methods calling methods
Problem statement
A college allows a student to sit the exam only with at least 75% attendance. Write percent(attended, held), and a method status(attended, held) that calls it and returns "Eligible" or "Not eligible". Check two students.
Riya: 42 of 50 classes Kabir: 33 of 50 classes
Riya: Eligible Kabir: Not eligible
Approach
percentdoes only the arithmetic and returns a double.statuscallspercent, compares with 75 and returns a String.- During the inner call the stack is three frames deep: main → status → percent.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Slab-wise electricity bill
Problem statement
An electricity board charges Rs 3 per unit for the first 100 units and Rs 5 per unit after that, plus a fixed charge of Rs 50. Write energyCharge(units) and bill(units), and print the bill for a home that used 160 units.
units = 160
Units: 160 Bill: Rs 650
Approach
energyChargeapplies the slab rule and returns the energy amount.billcallsenergyChargeand adds the fixed charge.mainonly asks for the bill and prints it; it knows nothing about slabs.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Check yourself
Quick revision
Lesson 5.1 in one screen
- A method is a named block of code inside a class; write once, call many times.
- Header:
static returnType name(parameters), then a body in braces. void= no result; otherwisereturn value;of the declared type.- Signature = name + parameter types (not the return type).
- Each call pushes a stack frame (parameters + locals); return pops it.
- Methods can call methods; the stack is last in, first out.
- Names: camelCase verbs (
calculateTotal,isEligible).
Interview and viva questions
Why do we use methods?
Reuse (write logic once, call it from many places), readability (a well-named call explains itself), easier testing (check one small piece at a time) and easier change (fix a rule in one place). They also let a team divide work: different people write different methods.
What is a stack frame and what does it contain?
A block of memory on the thread's call stack created for one method call. It holds the method's parameters, its local variables and working space for calculations, plus the information needed to return to the caller. It is pushed when the method is called and popped when it returns or throws.
Is the return type part of the method signature?
No. In Java the signature is the method name and the parameter types in order. That's why two methods that differ only in return type can't exist in the same class (more in Lesson 5.4).
Can main() be called by another method?
Yes. main is an ordinary public static method; the JVM simply calls it first. Your code can call main(new String[]{"x"}) like any other method, though there's rarely a good reason to.
Why do our methods need the static keyword right now?
main is static, so it runs without any object. A static method can directly call other static methods of the same class. A non-static method belongs to an object, so calling it from main without one is a compile error. Objects and instance methods start in Module 6.
Practice problems
Every problem here asks you to write at least one method and call it from main. Decide the method's name, what it needs (parameters) and what it gives back (return type) before you write the body. The last three are real business tasks.
A reusable divider line
Write a method printDivider(char symbol, int length) that prints a line of the given symbol. Call it three times with different values.
('-', 20), ('=', 12), ('*', 5)-------------------- ============ *****
Show solution and explanation
public class Main {
static void printDivider(char symbol, int length) {
for (int i = 0; i < length; i++) {
System.out.print(symbol);
}
System.out.println();
}
public static void main(String[] args) {
printDivider('-', 20);
printDivider('=', 12);
printDivider('*', 5);
}
}How it works: the loop is written once, inside the method. Each call supplies a different symbol and length, so one method replaces three copies of the loop. The method is void because its job is to print, not to calculate a value.
Bigger of two numbers
Write max(int a, int b) that returns the larger value. Use it to print the larger of two pairs, and then the largest of three numbers by calling it twice.
(18, 42), (-5, -9), and the three numbers 7, 31, 12
max(18, 42) = 42 max(-5, -9) = -5 Largest of 7, 31, 12 = 31
Show solution and explanation
public class Main {
static int max(int a, int b) {
if (a > b) {
return a;
}
return b;
}
public static void main(String[] args) {
System.out.println("max(18, 42) = " + max(18, 42));
System.out.println("max(-5, -9) = " + max(-5, -9));
int biggest = max(max(7, 31), 12);
System.out.println("Largest of 7, 31, 12 = " + biggest);
}
}How it works: a returning method can be used anywhere a value is allowed, even as an argument to another call. In max(max(7, 31), 12) the inner call runs first and gives 31, then the outer call compares 31 with 12.
Even or odd checker
Write isEven(int n) that returns true for even numbers. Use it in a loop to label the numbers 1 to 6.
numbers 1 to 6
1 is odd 2 is even 3 is odd 4 is even 5 is odd 6 is even
Show solution and explanation
public class Main {
static boolean isEven(int n) {
return n % 2 == 0;
}
public static void main(String[] args) {
for (int i = 1; i <= 6; i++) {
if (isEven(i)) {
System.out.println(i + " is even");
} else {
System.out.println(i + " is odd");
}
}
}
}How it works: n % 2 == 0 is already a boolean, so the method can return it directly; no if-else is needed inside. Methods that answer a yes/no question are usually named isSomething or hasSomething, which makes the calling code read like English.
Celsius to Fahrenheit
Write toFahrenheit(double celsius) using F = C × 9 / 5 + 32, and print a small conversion table.
0, 37, 100 degrees Celsius
0 C = 32.0 F 37 C = 98.6 F 100 C = 212.0 F
Show solution and explanation
public class Main {
static double toFahrenheit(double celsius) {
return celsius * 9 / 5 + 32;
}
public static void main(String[] args) {
System.out.println(0 + " C = " + toFahrenheit(0) + " F");
System.out.println(37 + " C = " + toFahrenheit(37) + " F");
System.out.println(100 + " C = " + toFahrenheit(100) + " F");
}
}How it works: the formula lives in one place. The int arguments 0, 37 and 100 are widened to double automatically when copied into the parameter, so the result keeps its decimal point: 37 °C gives 98.6 °F.
Primes up to 30
Write isPrime(int n) and use it to print every prime number from 2 to 30 on one line.
range 2 to 30
2 3 5 7 11 13 17 19 23 29
Show solution and explanation
public class Main {
static boolean isPrime(int n) {
if (n < 2) {
return false;
}
for (int d = 2; d * d <= n; d++) {
if (n % d == 0) {
return false;
}
}
return true;
}
public static void main(String[] args) {
for (int n = 2; n <= 30; n++) {
if (isPrime(n)) {
System.out.print(n + " ");
}
}
System.out.println();
}
}How it works: as soon as a divisor is found, return false leaves the method immediately, even from inside the loop. Checking divisors only while d * d <= n is enough: if n had a divisor bigger than its square root, it would also have a matching one smaller than it. Because the test is in a method, main stays short and readable.
Count the digits
Write countDigits(long n) that returns how many digits a number has. Treat 0 as having one digit.
7, 2024, 9876543210, 0
1 4 10 1
Show solution and explanation
public class Main {
static int countDigits(long n) {
if (n == 0) {
return 1;
}
int count = 0;
while (n != 0) {
n = n / 10;
count++;
}
return count;
}
public static void main(String[] args) {
System.out.println(countDigits(7));
System.out.println(countDigits(2024));
System.out.println(countDigits(9876543210L));
System.out.println(countDigits(0));
}
}How it works: each division by 10 removes the last digit, so the loop runs once per digit. The method changes its own copy n; the caller's value is untouched (Lesson 5.3 explains why). The special case for 0 is handled first with an early return.
Leap year helper
Write isLeapYear(int year) using the Gregorian rule (divisible by 4, except centuries, unless divisible by 400) and test it on several years.
1900, 2000, 2024, 2026
1900 is not a leap year 2000 is a leap year 2024 is a leap year 2026 is not a leap year
Show solution and explanation
public class Main {
static boolean isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
static void report(int year) {
System.out.println(year + (isLeapYear(year) ? " is" : " is not") + " a leap year");
}
public static void main(String[] args) {
report(1900);
report(2000);
report(2024);
report(2026);
}
}How it works: two small methods, each with one job: isLeapYear decides, report prints. report calls isLeapYear, so for each year the stack briefly holds three frames: main, report and isLeapYear.
Rectangle area and perimeter
Write two methods, area and perimeter, for a rectangle, and a third method describe that calls both and prints a one-line summary.
12 x 5 and 7 x 7
12 x 5: area 60, perimeter 34 7 x 7: area 49, perimeter 28
Show solution and explanation
public class Main {
static int area(int length, int width) {
return length * width;
}
static int perimeter(int length, int width) {
return 2 * (length + width);
}
static void describe(int length, int width) {
System.out.println(length + " x " + width + ": area " + area(length, width)
+ ", perimeter " + perimeter(length, width));
}
public static void main(String[] args) {
describe(12, 5);
describe(7, 7);
}
}How it works: describe is built from smaller methods. Each one is easy to test on its own, and if the perimeter formula were wrong you would fix it in exactly one place.
Restaurant bill with GST
A restaurant charges 5% GST on the food subtotal. Write subtotal(int[] prices), gst(double amount) and printBill(int[] prices), which uses the other two.
prices = {180, 240, 60} (Rs)Subtotal : Rs 480 GST 5% : Rs 24.0 Payable : Rs 504.0
Show solution and explanation
public class Main {
static int subtotal(int[] prices) {
int sum = 0;
for (int p : prices) {
sum += p;
}
return sum;
}
static double gst(double amount) {
return amount * 5 / 100;
}
static void printBill(int[] prices) {
int sub = subtotal(prices);
double tax = gst(sub);
System.out.println("Subtotal : Rs " + sub);
System.out.println("GST 5% : Rs " + tax);
System.out.println("Payable : Rs " + (sub + tax));
}
public static void main(String[] args) {
printBill(new int[]{180, 240, 60});
}
}How it works:
- Each rule has its own method, so when the GST rate changes only
gstis edited. printBillis the coordinator: it calls the helpers and formats the result.new int[]{…}creates the array right in the call (Lesson 4.1).
Salary slip
An employee's monthly pay has HRA = 20% of basic, DA = 10% of basic, and PF = 12% of basic is deducted. Write one method per component and a method that prints the slip.
basic = 30000
Employee : Arjun Gross : Rs 39000.0 PF : Rs 3600.0 Net pay : Rs 35400.0
Show solution and explanation
public class Main {
static double hra(double basic) {
return basic * 0.20;
}
static double da(double basic) {
return basic * 0.10;
}
static double pf(double basic) {
return basic * 0.12;
}
static void printSlip(String name, double basic) {
double gross = basic + hra(basic) + da(basic);
double net = gross - pf(basic);
System.out.println("Employee : " + name);
System.out.println("Gross : Rs " + gross);
System.out.println("PF : Rs " + pf(basic));
System.out.println("Net pay : Rs " + net);
}
public static void main(String[] args) {
printSlip("Arjun", 30000);
}
}How it works:
- Every rule (HRA, DA, PF) is one small method with a clear name, which mirrors how payroll policies are written.
printSlipcallspf(basic)twice. That is fine for a cheap calculation; for a costly one you would store it in a variable once.- Real payroll uses
BigDecimalfor money;doublekeeps this example simple.
Courier charge by weight
A courier company charges Rs 40 for parcels up to 1 kg, Rs 70 up to 5 kg, and Rs 70 plus Rs 12 per extra kg above 5 kg. Write charge(int kg) and print the charge for each parcel and the day's total.
parcels (kg) = {1, 4, 9}1 kg -> Rs 40 4 kg -> Rs 70 9 kg -> Rs 118 Day total: Rs 228
Show solution and explanation
public class Main {
static int charge(int kg) {
if (kg <= 1) {
return 40;
}
if (kg <= 5) {
return 70;
}
return 70 + (kg - 5) * 12;
}
public static void main(String[] args) {
int[] parcels = {1, 4, 9};
int total = 0;
for (int kg : parcels) {
int c = charge(kg);
System.out.println(kg + " kg -> Rs " + c);
total += c;
}
System.out.println("Day total: Rs " + total);
}
}How it works:
- The rate card lives in one method. Each
returnends the method as soon as the right slab is found, so no else is needed. mainonly loops and adds; it does not know the slab rules.- 9 kg: 70 + (9 − 5) × 12 = 118.
Real-world scenario questions (asked in company interviews)
Interviewers rarely ask "what is a method". They ask about duplicated logic, giant methods and stack traces. Decide your answer first, then open the model answer.
Three screens, three different GST amounts
A shop's software shows GST on the bill, in the daily report and in the monthly export. After a rate change, customers notice the bill and the export disagree by a few rupees. You find the same calculation copy-pasted in three files, one of them not updated. What do you do?
The interviewer is checking: do you see duplicated logic as the root cause, and know how a method fixes it?
Show a strong answer
- Fix the outdated copy immediately so customers get correct bills.
- Then remove the duplication: create one method, e.g.
calculateGst(amount), and make all three places call it. - Add a small test for that method with known amounts, so a future rate change is checked once.
- Search the codebase for other copies of the formula before closing the issue.
Avoid: fixing only the one wrong copy. The next change will break it again.
The 400-line main method
A monthly payroll program has everything in main: reading attendance, calculating allowances, deductions, tax, and printing slips. Every change breaks something else and nobody wants to touch it. Your lead asks how you would restructure it.
The interviewer is checking: can you break a big task into well-named methods with one job each?
Show a strong answer
- Identify the steps and extract each into a method with a clear name:
readAttendance,calculateGross,calculateDeductions,calculateTax,printSlip. - Keep each method short and focused on one job; pass in what it needs and return its result instead of sharing many variables.
mainthen reads like the payroll process itself, step by step.- Refactor in small steps and compare the slips before and after each step to be sure the numbers don't change.
Avoid: rewriting everything at once. Small, checked steps are safer.
Reading your first production stack trace
A report page crashes. The log shows java.lang.ArithmeticException: / by zero followed by at Main.average(Main.java:3), at Main.report(Main.java:7), at Main.main(Main.java:12). Where do you look first, and what is the likely fix?
The interviewer is checking: do you know how to read a stack trace as a picture of the call stack?
Show a strong answer
- The top line is where it failed: line 3 of
average, a division where the count was 0. - The lines below show who called it:
report(line 7), called bymain(line 12). So the bad value came fromreport's caller. - Fix: decide what "no data" should mean (print "No data" or return 0) and handle a count of 0 before dividing, in
averageor inreport.
Avoid: reading only the last line or guessing. The trace tells you exactly which call chain failed.
Why did customers get two SMS?
A method calculateDeliveryFee(order) calculates the fee and also sends the customer an SMS with the amount. A new report screen calls the same method to display fees, and customers start receiving duplicate SMS messages. What went wrong, and how should the code be designed?
The interviewer is checking: do you understand that a method's name should match exactly what it does, and that calculation and actions should be separate?
Show a strong answer
- The method has a hidden side effect: its name says "calculate", but it also sends an SMS. Anyone reusing it for a calculation triggers the message.
- Split it:
calculateDeliveryFee(order)only returns the fee;sendFeeSms(order, fee)only sends. The checkout flow calls both; the report calls only the first. - Rule of thumb: a method either answers a question (returns a value) or does an action, and its name says which.
Avoid: adding a boolean flag like calculateDeliveryFee(order, false) to skip the SMS. It hides the problem instead of fixing the design.