Methods in Java — taking apart the public static that’s scary at first
The first time I saw a method in Java, I remember staring at the first line before even understanding what the method did: public static int calculateAverage(int a, int b). In Python, a function starts with def and that’s it. Here there were four words before even reaching the name, and each one seemed to carry its own meaning that nobody explained all at once. If the same thing is happening to you, don’t worry — we’re going to take that line apart word by word, because once you know what each piece does, they stop being scary.
Table of Contents
From function to method
In Python you define a standalone function, no context needed:
def calculate_average(a, b):
return (a + b) / 2
print(calculate_average(7, 9))
In Java, this doesn’t exist. There are no free-standing functions — everything that would be a function in Python has to live inside a class in Java, and it’s called a method. For now, while we haven’t covered classes in depth yet, you’ll write all your methods inside the same class where your main lives, like this:
public class Calculator {
public static double calculateAverage(int a, int b) {
return (a + b) / 2.0;
}
public static void main(String[] args) {
System.out.println(calculateAverage(7, 9));
}
}
Notice something: calculateAverage sits outside main, at the same level, inside the class Calculator. That’s key — a method never goes inside another method.
Anatomy of a method
Let’s take that first line that gave me so much trouble and split it into its pieces:
public static double calculateAverage(int a, int b) {
| Piece | What it is |
|---|---|
public | Access modifier — who can use this method |
static | For now: “that’s just how it’s done” — explained properly when we reach classes |
double | The data type the method returns |
calculateAverage | The method’s name |
(int a, int b) | The parameters, with their required type |
Let’s go through them one by one.
The access modifier: public and private
In Python, technically everything is accessible from anywhere — at most, you use a leading underscore (_variable) as a convention to say “this is internal, don’t touch it”, but nothing actually stops you. Java does stop you, and it does it with access modifiers.
The two you’ll use the most at first are:
public static void greet() {
// Any class in your program can call this method
}
private static int calculateDiscount(double price) {
// Can only be called from inside this same class
}
public: the method is visible from any other class in your program. It’s what you need for something to be usable “from outside”.private: the method can only be called from inside the same class where it’s written. If another class tries to call it, it won’t compile.
Marking a method private and then trying to call it from main in another class, staring at the compile error without understanding why. If your method needs to be used “from outside”, it has to be public. private is for helper methods that only make sense inside their own class, like an intermediate calculation nobody else needs to see.
It’s natural to wonder: what if I don’t put any modifier at all? There’s a third visibility level (the so-called “package-private”, with no keyword) that you’ll see in detail once we reach classes and visibility. For now, always use public or private explicitly, so you’re not relying on the default.
static: for now, “that’s just how it’s done”
static is the piece that raises the most questions, and rightly so: you won’t fully understand it until we cover classes and objects, because it has to do with the difference between a method that belongs to the class in general and one that belongs to each individual object you create from that class.
For now, keep this in mind: as long as all your code lives inside a single class with a main, all your methods will be static. It’s what lets main — which is also static — call them directly, without first having to create an object of the class. When we get to the classes-and-objects topic, you’ll see methods without static and you’ll understand exactly when to use each one.
The return type: what Python never made you state
In Python, a function can return whatever it wants without announcing it beforehand:
def process(x):
if x > 0:
return x * 2
return "negative" # a completely different type, and Python says nothing!
In Java this is impossible. A method declares, right before its name, the exact type of what it’s going to return, and it’s required to honor that promise every time:
public static int doubleIt(int x) {
return x * 2; // it must return an int, exactly that
}
If the method doesn’t return anything, the keyword void goes where the return type would be:
public static void showGreeting(String name) {
System.out.println("Hello, " + name);
// no return, or at most an empty "return;" to cut things short early
}
void isn’t “doesn’t return anything for now” — it’s a promise the compiler enforces. If a method declares void and you try to do return 5; inside it, it won’t compile. And the other way around: if you declare int and forget a return on some path through the method, it won’t compile either. Java doesn’t let you “forget” to return something the way you might in Python.
Parameters also need a type
Every parameter of a method in Java carries its declared type right in front of it, one by one:
public static double calculatePrice(double base, int quantity, boolean withTax) {
double total = base * quantity;
if (withTax) {
total *= 1.21;
}
return total;
}
You can’t write calculatePrice(base, quantity, withTax) without types, the way you would in Python. Each parameter is, in practice, a local variable that only exists inside the method, with its type fixed from the start.
Calling a method
Calling one of your own methods works just like calling any function you already know, such as System.out.println():
public class Grades {
public static String classify(double grade) {
if (grade >= 9) {
return "Excellent";
} else if (grade >= 7) {
return "Very Good";
} else if (grade >= 5) {
return "Pass";
}
return "Fail";
}
public static void main(String[] args) {
double myGrade = 8.5;
// We call the method and store what it returns in a variable
String result = classify(myGrade);
System.out.println("Classification: " + result);
// It can also be used directly inside another expression
System.out.println("Grade " + 6.0 + " is: " + classify(6.0));
}
}
It’s natural to wonder: can I have two methods with the same name? Yes, as long as they can be told apart by their parameters — a different number of parameters, or parameters of a different type. This is called method overloading:
public static int add(int a, int b) {
return a + b;
}
public static double add(double a, double b) {
return a + b;
}
public static int add(int a, int b, int c) {
return a + b + c;
}
When you call add(3, 4), Java looks at the types you pass and decides on its own which of the three versions to use. This doesn’t exist in Python quite the same way — there, if you define two functions with the same name, the second one simply replaces the first.
A very practical use of overloading is faking “default” values. Java doesn’t have parameters with default values like Python does (def fun(x, y=10)), so instead a method gets overloaded:
public static double calculatePrice(double base, int quantity) {
return calculatePrice(base, quantity, false); // calls the version below
}
public static double calculatePrice(double base, int quantity, boolean withTax) {
double total = base * quantity;
if (withTax) {
total *= 1.21;
}
return total;
}
Seeing methods in action
Let’s put it all together in a full example, with a typical case: checking whether a student ID number is valid before processing it, similar to what any university form does under the hood.
public class StudentValidator {
// private: it's an internal calculation, nobody outside needs to call it directly
private static boolean hasCorrectLength(String id) {
return id.length() == 8;
}
// public: this one we do need to be able to call from main
public static boolean isIdValid(String id) {
if (!hasCorrectLength(id)) {
return false; // we cut things short here — no need to check anything else
}
// we check that every character is a digit
for (int i = 0; i < id.length(); i++) {
if (!Character.isDigit(id.charAt(i))) {
return false;
}
}
return true;
}
public static void main(String[] args) {
String[] tests = {"12345678", "123", "1234567a"};
for (String id : tests) {
System.out.println(id + " → " + (isIdValid(id) ? "valid" : "not valid"));
}
}
}
isIdValid is public because it’s the one used from outside; hasCorrectLength is private because it’s an intermediate step that only makes sense inside this class. This is exactly the kind of decision you’ll be making constantly: does something outside need this, or is it an internal detail?
See it with Python Tutor
Select Java from the dropdown and paste this into pythontutor.com:
public class Demo {
public static int doubleIt(int x) {
return x * 2;
}
public static int tripleSum(int x) {
int result = doubleIt(x) + x;
return result;
}
public static void main(String[] args) {
int value = tripleSum(4);
System.out.println(value);
}
}
Step through it and pay attention to the call stack: when main calls tripleSum(4), a new frame opens just for that call, with its own x. Inside it, the call to doubleIt(x) opens another frame on top, with its own copy of x — separate from tripleSum‘s, even though it’s named the same. When doubleIt finishes with its return, that frame disappears and the result goes back to the exact point where it was called, to continue the result calculation. Every method call gets its own space, isolated from the others, even when they reuse variable names.
Summary
- Java has no free-standing functions — everything lives inside a class, and it’s called a method
publiclets a method be called from outside the class;private, only from insidestaticis, for now, “that’s just how it’s done” — you’ll fully understand it once we reach classes and objects- The return type is mandatory and Java enforces it: if it says
int, it must return aninton every path; if it returns nothing, it’s declaredvoid - Parameters always carry their declared type, one by one
- You can have several methods with the same name (overloading) as long as they’re told apart by the number or type of their parameters
Did this article help you? Here is how you can support SergioLearns →
