Methods in Java
-
Last Updated: June 25, 2025
-
By: javahandson
-
Series
Learn Java in a easy way
Methods in Java are named blocks of code that do one job and hand the result back to you. This guide walks you through method syntax, parameters, return types, overloading, overriding, and the static, final, abstract, and synchronized keywords.
Think about making tea. You boil the water, add the leaves, pour in milk, and strain it into a cup. Nobody rediscovers those steps every morning. You just say “make tea” and your hands take over.
A method works the same way. You write the steps once, give them a name, and call that name whenever you need them. Java runs the steps and comes back with an answer.
Skip methods, and a program turns into one long blob of code. Copy the same ten lines into five places, and one bug means five separate fixes. Methods pull that logic into a single home.
Here is the fun part. Every Java program you have ever run already leans on methods. The main method starts your program. System.out.println prints your output. Math.sqrt does your square roots. You have called methods since your very first Hello World.
So let us slow down and take a method apart, piece by piece.
We start with the plain idea of a method, then build up to the tricky bits. Here is the plan:
You need very little to follow along. If you can write a class with a main method, you are ready. Each idea comes with a short example you can paste into your editor.
A method is a named block of code that performs one task. You hand it some input, it does the work, and it hands back a result. That is the whole idea.
Picture a vending machine. You feed in a coin and press a button. Out comes a snack. You never see the motors and levers inside, and you do not need to.
A method gives you that same deal. The caller supplies the input and takes the output. Everything in between stays tucked away inside the method body.
This hiding is not laziness. It means you can rewrite the insides later without touching a single caller. As long as the input and output stay the same, the rest of your program never notices.
Beginners often ask why they should bother. Why not write everything inside main? Five good reasons:
calculateGrade(marks) tells the story better than fifteen lines of arithmetic.That last point is abstraction, one of the four pillars of object-oriented programming. Methods are how you put it into practice.
Here is a method that adds two numbers. Read it once, then we will name each part.
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
Calculator calc = new Calculator();
int sum = calc.add(10, 20);
System.out.println("Sum: " + sum); // Output: Sum: 30
}
}Four small things happen here. The class holds a method called add. That method takes two int values and returns their total. Inside main, we create a Calculator object and call add on it. The result lands in sum.
Change the numbers and the method still works. That flexibility comes from the parameters, and it is why methods beat copied code every single time.
Every Java method follows the same template. Some parts are optional, but the order never changes.
accessModifier otherModifiers returnType methodName(parameterList) throws ExceptionType {
// method body
return value; // only when the return type is not void
}Read left to right, that says: who may call me, how I behave, what I give back, what I am called, what I need, and what may go wrong. Let us take those one at a time.
The access modifier decides who may call your method. Java gives you four levels.
public opens the method to every class in every package.protected allows the same package plus any subclass, wherever it lives.private locks the method inside its own class.Pick the tightest one that still works. A private helper method leaves you free to rename or delete it tomorrow. A public one becomes a promise to everybody who uses your class. Our guide to access specifiers in Java digs deeper into the four levels.
The return type says what kind of value comes back. It might be a primitive like int or double. Objects work just as well, such as String or Student. You can even hand back an array such as int[].
When a method hands back nothing at all, you write void. A void method still does real work. It just does not produce a value for the caller to store.
The return type sits right before the method name and Java never lets you skip it. Forget it, and the compiler assumes you meant a constructor and complains.
Names follow a simple convention. Start with a lowercase letter, then capitalise each new word: calculateTotal, isEmpty, findStudentById.
Use a verb. A method does something, so its name should say what. getBalance reads well. balance alone reads like a variable and confuses the next person.
Two habits pay off fast. Methods that answer yes or no usually start with is or has. Methods that fetch a value usually start with get. Follow the crowd here, because every Java developer already expects it.
Parameters are the inputs your method needs. Each one gets a type and a name, and commas separate them.
public void greet() { } // no parameters
public void greet(String name) { } // one parameter
public double area(double length, double width) { } // two parametersEmpty parentheses mean the method needs nothing from you. That happens often with methods that only read the object’s own fields.
Keep the list short. Three or four parameters is plenty. Once you pass seven values, callers start mixing up the order, and nobody catches it until runtime.
Interviewers love this distinction, so let us make it crisp. A method signature in Java means just two things: the method name and the parameter types, in order.
The signature does not include the return type. It also leaves out the access modifier, the parameter names, and any throws clause. Those belong to the wider declaration.
Why does this matter? Because Java uses the signature to tell two methods apart. That single rule explains most of the overloading behaviour we cover in section 8.
An instance method belongs to an object. So you need an object before you can call it.
public class Greeter {
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
public static void main(String[] args) {
Greeter g = new Greeter(); // create the object first
g.sayHello("Suraj"); // Output: Hello, Suraj!
}
}The dot does the work. g.sayHello(...) means “run sayHello on the object that g points to”. Ten different Greeter objects can each run that method on their own data.
Inside an instance method you may also drop the dot entirely. Calling sayHello("Suraj") from another instance method works fine, because Java quietly uses this.
A static method belongs to the class, not to any object. Use the class name and you are done.
public class MathUtils {
public static int square(int n) {
return n * n;
}
public static void main(String[] args) {
System.out.println(MathUtils.square(7)); // Output: 49
System.out.println(Math.max(3, 9)); // Output: 9
}
}No new, no object, no ceremony. That is why utility classes such as Math and Arrays fill themselves with static methods.
One catch trips up nearly every beginner. A static method cannot touch instance fields or instance methods directly, because no object exists yet. Section 11.1 shows the exact error message.
What actually happens the moment you call a method? The JVM pushes a new frame onto the call stack.
That frame holds the method’s parameters, its local variables, and a note about where to resume afterwards. Think of it as a fresh sheet of paper for that one call.
When the method finishes, the JVM pops the frame and throws that sheet away. Control jumps back to the caller, carrying the return value with it. Local variables inside the method vanish at that moment.
This also explains a famous crash. A method that calls itself forever keeps stacking frames until the stack runs out of room. The JVM then throws StackOverflowError.
public class StackDemo {
static int factorial(int n) {
if (n <= 1) {
return 1; // base case pops the deepest frame
}
return n * factorial(n - 1);
}
public static void main(String[] args) {
System.out.println(factorial(5)); // Output: 120
}
}Five nested calls stack up here, and then five frames unwind one by one. Drop the base case and the program crashes within a second.
People swap these two words all the time, but they mean different things.
int a.10.Short version: you declare parameters and you pass arguments. Get that straight and a lot of documentation suddenly reads more clearly.
Here is the single most misunderstood rule in the language. Java passes everything by value. Always. No exceptions.
Passing by value means the method receives a copy of whatever you handed over. Change the copy, and the original stays exactly as it was.
public class PassByValue {
static void tryToChange(int number) {
number = 99; // changes only the local copy
}
public static void main(String[] args) {
int x = 5;
tryToChange(x);
System.out.println(x); // Output: 5
}
}The value 5 gets copied into number. Inside the method we overwrite that copy with 99. Back in main, x never budged.
Now for the part that confuses people. Objects behave the same way, yet the effect looks different.
When you pass an object, Java copies the reference, not the object itself. Both the caller and the method now point at one shared object. So changing the object’s fields inside the method does affect the caller.
Reassigning the parameter is another story. That only re-points the local copy of the reference. The caller keeps looking at the original object.
class Box {
String label = "old";
}
public class ObjectParam {
static void modify(Box b) {
b.label = "new"; // affects the shared object
}
static void replace(Box b) {
b = new Box(); // only re-points the local copy
b.label = "ignored";
}
public static void main(String[] args) {
Box box = new Box();
modify(box);
System.out.println(box.label); // Output: new
replace(box);
System.out.println(box.label); // Output: new
}
}Notice the second print. replace looked like it swapped the box, yet nothing changed outside. That is pass by value doing its job on the reference.
Sometimes you do not know how many values a caller will pass. Varargs handle that with three dots after the type.
public class VarargsDemo {
static int sum(int... numbers) {
int total = 0;
for (int n : numbers) {
total += n;
}
return total;
}
public static void main(String[] args) {
System.out.println(sum()); // Output: 0
System.out.println(sum(5)); // Output: 5
System.out.println(sum(1, 2, 3, 4, 5)); // Output: 15
}
}Inside the method, numbers behaves as an ordinary int[]. Java builds that array for you at the call site, so you can even pass zero values.
Two rules keep varargs legal. A method may declare at most one varargs parameter, and it must sit last in the list. Break either rule and the code will not compile.
The simplest methods hand back a number, a character, or a boolean. Declare the type, then use return with a matching value.
static double average(int a, int b) {
return (a + b) / 2.0;
}
static boolean isEven(int n) {
return n % 2 == 0;
}
// average(5, 8) -> 6.5
// isEven(7) -> falseThe return keyword does two jobs at once. It hands the value back, and it ends the method right there. Any code after it never runs.
A void method returns nothing. Printing, saving to a file, and updating a field all fit this shape nicely.
You may still write a bare return; inside a void method. It carries no value and simply exits early, which is handy for guard clauses.
static void printGrade(int marks) {
if (marks < 0) {
System.out.println("Invalid marks");
return; // bail out early
}
System.out.println("Marks: " + marks);
}That early exit keeps the happy path flat instead of burying it in nested if blocks. Small habit, big readability win.
Methods are not limited to primitives. A method can return a String, a custom object, an array, or a whole collection.
class Student {
String name;
Student(String name) { this.name = name; }
}
public class ReturnDemo {
static Student createStudent(String name) {
return new Student(name);
}
static int[] firstFive() {
return new int[]{1, 2, 3, 4, 5};
}
public static void main(String[] args) {
System.out.println(createStudent("Suraj").name); // Output: Suraj
System.out.println(firstFive().length); // Output: 5
}
}Returning objects unlocks a neat trick. A method that returns this lets callers chain calls together, which is exactly how StringBuilder lets you write sb.append("a").append("b").
A non-void method must return a value on every possible route through the code. Miss one branch, and the compiler stops you with “missing return statement”.
// Will not compile: what happens when marks is 40?
static String grade(int marks) {
if (marks >= 50) {
return "Pass";
}
// no return here
}
// Fixed
static String gradeFixed(int marks) {
if (marks >= 50) {
return "Pass";
}
return "Fail";
}The compiler does not guess your intent. It checks every branch and demands a value from each one. Treat that error as a helpful nudge rather than a nuisance.
Java developers group methods in several ways. Some groupings describe where the method came from, and others describe a keyword you attached to it. Here are the seven you will meet most often.
Predefined methods ship with Java itself. The library gives you thousands across classes such as Math, String, Arrays, and Collections.
public class PredefinedDemo {
public static void main(String[] args) {
System.out.println(Math.sqrt(25)); // Output: 5.0
System.out.println(Math.abs(-14)); // Output: 14
String text = "Hello, JavaHandsOn!";
System.out.println(text.length()); // Output: 19
System.out.println(text.toUpperCase()); // Output: HELLO, JAVAHANDSON!
}
}Learn the common ones and you write far less code. Before you hand-roll a helper, check whether the library already solved it.
User-defined methods are the ones you write. Everything in this article so far falls into this bucket.
You control the name, the parameters, the return type, and the body. That freedom is the point, because your business rules will never live in the standard library.
A static method belongs to the class. Add the static keyword and callers reach it through the class name, with no object involved.
class Counter {
static int count = 0;
static void increment() {
count++; // fine: count is also static
}
}
// Counter.increment();
// Counter.count is now 1Static methods suit helpers that depend only on their arguments. Conversion routines, validators, and factory methods all fit well. Our static keyword in Java guide covers static blocks and nested classes too.
An instance method belongs to an object, so it can read and change that object’s fields. Most methods you write will land here.
class BankAccount {
private double balance;
void deposit(double amount) {
balance += amount; // touches this object's field
}
double getBalance() {
return balance;
}
}Two accounts each keep their own balance, and each call works on its own object. Instance methods also let you use this and super, which static methods cannot.
An abstract method declares a name and a signature but no body. It ends with a semicolon instead of braces.
Such a method can only live inside an abstract class or an interface. Any concrete subclass must supply the body, otherwise the compiler rejects it.
abstract class Shape {
abstract double area(); // no body
void describe() {
System.out.println("Area is " + area());
}
}
class Circle extends Shape {
double radius = 2.0;
@Override
double area() {
return Math.PI * radius * radius;
}
}
// new Circle().describe(); -> Area is 12.566370614359172Look at describe. It calls area() without knowing which shape will run. That is polymorphism, and abstract methods are how you set it up. See our abstract class guide for the full picture.
Mark a method final and no subclass may override it. The parent keeps control of that behaviour for good.
class Payment {
final void audit() {
System.out.println("Audit log written");
}
}
class CardPayment extends Payment {
// void audit() { } // compile error: cannot override a final method
}Subclasses still inherit and call a final method. They simply cannot replace it. Use this when correctness depends on the exact steps running, such as an audit trail or a security check.
A synchronized method lets only one thread run it at a time on a given lock. That protects shared data when several threads work at once.
class Counter {
private int count = 0;
synchronized void increment() {
count++; // one thread at a time
}
int getCount() {
return count;
}
}Which lock does it grab? An instance method locks the object itself, so this. A static synchronized method locks the Class object instead.
That difference matters more than it looks. A static synchronized method and an instance synchronized method hold different locks, so they never block each other.
Locking costs time, so keep synchronized sections small. Guard the few lines that touch shared state, and leave the rest outside.
Overloading means several methods in the same class share one name but take different parameters. The compiler picks the right one at compile time, which people call compile-time polymorphism.
public class Printer {
void show(int a) {
System.out.println("int: " + a);
}
void show(double a) {
System.out.println("double: " + a);
}
void show(String a) {
System.out.println("String: " + a);
}
void show(int a, int b) {
System.out.println("two ints: " + a + ", " + b);
}
public static void main(String[] args) {
Printer p = new Printer();
p.show(5); // Output: int: 5
p.show(5.5); // Output: double: 5.5
p.show("hi"); // Output: String: hi
p.show(3, 4); // Output: two ints: 3, 4
}
}You already use overloading every day. System.out.println comes in ten flavours so it can print an int, a double, a char, or an object without you thinking about it.
With several candidates on the table, how does javac choose? It runs three passes and stops at the first one that finds a match.
int to long. No boxing, no varargs.int can become Integer.This order explains a classic puzzle. Given show(long) and show(Integer), a call to show(5) picks show(long). Widening wins because pass one runs first.
When two candidates tie in the same pass, the compiler picks the more specific one. When neither is more specific, you get an “ambiguous method call” error and must add a cast.
Try to overload on the return type alone and the code fails to compile. Section 3.6 already told you why: the return type is not part of the signature.
class Broken {
int value() { return 1; }
// double value() { return 1.0; } // compile error: already defined
}Think about the call value(); on its own line. Java would have no way to tell which one you meant, so it bans the situation outright.
Overriding means a subclass replaces a method it inherited. The JVM decides which version runs at runtime, based on the actual object. People call this runtime polymorphism.
protected may become public.private, static, and final methods stay off limits.class Animal {
void speak() {
System.out.println("Some sound");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("Woof!");
}
}
public class OverrideDemo {
public static void main(String[] args) {
Animal a = new Dog(); // reference type Animal, object type Dog
a.speak(); // Output: Woof!
}
}Read that last call carefully. The variable has type Animal, yet the Dog version runs. The object decides, not the reference.
Always write @Override above an overriding method. The annotation changes nothing at runtime, yet it saves hours of debugging.
Here is the payoff. Misspell the name, or get a parameter type wrong, and you have quietly created a brand new method instead of an override. The parent version keeps running and you wonder why your change did nothing.
With @Override in place, the compiler checks your work. If nothing in the parent matches, compilation fails on the spot.
An override may narrow the return type to a subclass. Java 5 introduced this, and it removes a lot of casting.
class Animal {
Animal reproduce() {
return new Animal();
}
}
class Dog extends Animal {
@Override
Dog reproduce() { // covariant: Dog is an Animal
return new Dog();
}
}
// Dog puppy = new Dog().reproduce(); // no cast neededCallers who hold a Dog reference now get a Dog back. Callers who hold an Animal reference still see an Animal, so nothing breaks.
Declare a static method in a child with the same signature as one in the parent, and Java calls that hiding, not overriding.
The difference shows up at the call site. Overridden instance methods resolve from the object at runtime. Hidden static methods resolve from the reference type at compile time.
class Parent {
static void greet() {
System.out.println("Parent greet");
}
}
class Child extends Parent {
static void greet() {
System.out.println("Child greet");
}
}
public class HidingDemo {
public static void main(String[] args) {
Parent p = new Child();
p.greet(); // Output: Parent greet
}
}The object is a Child, yet the parent version runs. The reference type Parent made that call at compile time. Always call static methods through the class name to avoid this trap.
These two words look alike and mean very different things. Interviewers ask about the difference constantly, so here it is side by side.
| Aspect | Overloading | Overriding |
|---|---|---|
| Where | Same class, or a parent and child | Parent class and child class only |
| Parameters | Must differ | Must match exactly |
| Return type | Free to differ | Same type or a subtype |
| Resolved | At compile time | At runtime |
| Polymorphism | Compile-time (static) | Runtime (dynamic) |
| Access modifier | Any modifier works | Cannot become stricter |
| Inheritance | Not required | Required |
| static and final | Both overload fine | Neither one overrides |
One line to remember it. Overloading changes the parameters, while overriding changes the behaviour.
The other question that comes up in every interview. When should a method be static?
| Aspect | Static method | Instance method |
|---|---|---|
| Belongs to | The class | An object |
| Needs an object | No | Yes |
| Typical call | ClassName.method() |
object.method() |
| Can read instance fields | Only through an object | Directly |
| this and super | Unavailable | Available |
| Inheritance behaviour | Hidden in a subclass | Overridden in a subclass |
| Memory | One copy per class | Shared code, per-object data |
| Good fit | Utility and helper logic | Behaviour tied to object state |
A simple test settles most cases. Does the method need any field of a specific object? If not, make it static.
This one hits every beginner in week one. Your main method is static, so it has no object to work with.
public class Wrong {
void greet() {
System.out.println("Hi");
}
public static void main(String[] args) {
greet(); // error: non-static method greet() cannot be
// referenced from a static context
}
}Two fixes work. Create an object first with new Wrong().greet(), or mark greet as static. Pick the first when the method touches object state, and the second when it does not.
Plenty of developers write a swap(int a, int b) method and wonder why the caller’s variables never change.
Section 5.2 explained the reason. The method got copies, so it swapped copies. Java gives you no way around this.
What works instead? Return the new values, wrap them in an object or array, or change the fields of an object you both share.
An override may open a method up, never lock it down. Turning a public parent method into a protected child method fails to compile.
The reason is sound. Somebody holding a parent reference expects to call that method. Tightening access in the child would break that promise at runtime.
A varargs parameter must come last, and only one may appear.
// Wrong: varargs is not the last parameter
// static void log(String... messages, String level) { }
// Right
static void log(String level, String... messages) { }The reason is practical. Varargs swallows every remaining argument, so nothing could ever reach a parameter placed after it.
A method that validates input, saves to a database, sends an email, and writes a log is doing four jobs. Testing it becomes painful, and reusing any single piece becomes impossible.
Watch for warning signs. Long parameter lists, deep nesting, and names containing “and” all hint that a method wants to split.
Aim for one clear job per method. If you struggle to name it in a few words, that is your cue to break it apart.
Time to pull everything together. We will build a tiny report card program.
The program takes a student name and a set of marks. It calculates the total and the average, converts the average into a grade, and prints a small report.
Notice how each of those verbs becomes its own method. That mapping from “what it does” to “one method” is the habit worth building.
public class ReportCard {
// static: depends only on its arguments, uses varargs for any count
static int total(int... marks) {
int sum = 0;
for (int m : marks) {
sum += m;
}
return sum;
}
// returns a double, so the average keeps its decimals
static double average(int... marks) {
if (marks.length == 0) {
return 0.0; // guard clause avoids divide by zero
}
return (double) total(marks) / marks.length;
}
// every branch returns a value
static char grade(double average) {
if (average >= 90) return 'A';
if (average >= 75) return 'B';
if (average >= 60) return 'C';
if (average >= 40) return 'D';
return 'F';
}
// void: it prints, it does not calculate
static void printReport(String name, int... marks) {
double avg = average(marks);
System.out.println("Student : " + name);
System.out.println("Total : " + total(marks));
System.out.printf("Average : %.2f%n", avg);
System.out.println("Grade : " + grade(avg));
}
public static void main(String[] args) {
printReport("Suraj", 88, 92, 79, 85);
}
}Student : Suraj Total : 344 Average : 86.00 Grade : B
Four marks add up to 344, and dividing by four gives 86.00. That average falls in the B band, so the grade prints as B.
Look at how the methods lean on each other. average calls total instead of repeating the loop. printReport calls all three and formats the output.
Every method here earns its place:
total shows varargs handling any number of marks.average shows a guard clause and a cast to keep the decimals.grade shows why a non-void method needs a return on every path.printReport shows a void method that composes the other three.Now try changing the grade bands. You edit one method, and the rest of the program keeps working untouched. That is the payoff methods give you.
A: A method is a named block of code that performs one task. It takes optional inputs called parameters, runs its body, and may return a value. Methods give you reuse, smaller pieces of logic, and a clean way to hide detail from callers.
A: Overloading gives several methods the same name with different parameter lists, and the compiler picks one at compile time. Overriding replaces an inherited method in a subclass using the identical signature, and the JVM picks one at runtime based on the actual object.
A: The signature is the method name plus the parameter types in order. It leaves out the return type, the access modifier, the parameter names, and the throws clause. Java uses the signature to tell two methods apart, which is why you cannot overload on return type alone.
A: Java always passes by value. For a primitive, the method receives a copy of the value. For an object, the method receives a copy of the reference, so both sides point at one object. Changing the object’s fields affects the caller, but reassigning the parameter does not.
A: No. A static method in a subclass with the same signature hides the parent version instead. The compiler resolves hidden static methods from the reference type, so a Parent reference always runs the Parent version even when the object is a Child.
A: Both keywords refer to a current object, and a static method runs without one. The class loads and the static method becomes callable long before anybody creates an instance. To reach instance data from a static method, accept an object as a parameter.
A: Varargs let a method accept any number of arguments using three dots, as in int… numbers. Inside the method the parameter behaves as an array. A method may declare only one varargs parameter, and it must appear last in the parameter list.
A: A covariant return type lets an overriding method return a subclass of the parent method’s return type. Java 5 added this. If the parent returns Animal, the child may return Dog, and callers holding a Dog reference then skip the cast.
A: The compiler reports “missing return statement” and refuses to build. A non-void method must produce a value on every path through the code. Adding a final return after the if-else chain fixes it. A void method faces no such rule.
A: An instance synchronized method locks the object it runs on, meaning this. A static synchronized method locks the Class object instead. Because those are two different locks, a static and an instance synchronized method never block each other.
A: No, and each clash has the same root. An abstract method exists so a subclass can supply the body. Marking it private hides it from subclasses, final forbids the override, and static ties it to the class rather than to an object.
A: The JVM calls main from outside your class, so it must stay public. It runs before any object exists, so it must stay static. It hands nothing back to the JVM, so its return type is void. The String array carries command line arguments.
Let us wrap up what we covered. A method is a named block of code that does one job, and it turns a sprawling program into small parts you can name, test, and reuse.
Every declaration follows the same shape: an access modifier, optional keywords, a return type, a name, and a parameter list. The signature is only the name and the parameter types, which is why the return type cannot distinguish two methods.
Java passes every argument by value. Primitives arrive as copies of the value, and objects arrive as copies of the reference. Change an object’s fields and the caller sees it. Reassign the parameter and the caller sees nothing.
Overloading and overriding solve different problems. Overloading offers the same name with different parameters and resolves at compile time. Overriding replaces inherited behaviour and resolves at runtime from the real object.
The keywords each add one rule. static ties a method to the class, final blocks any override, abstract leaves the body to a subclass, and synchronized admits one thread at a time.
Keep methods small and give them honest names. Open your editor, write the report card program from section 12, and change the grade bands. Nothing cements this faster than running the code yourself.