Methods and dividing the program into smaller parts
So far, we've used various commands: value assignment, calculations, conditional statements, and loops.
Printing to the screen has been done with the statement System.out.println()
, and the reading of values with Integer.valueOf(scanner.nextLine())
. if
has been used in conditional statements, and while
and for
in loops. We notice that printing and reading operations somewhat differ from if
, while
, and for
in that the print and read commands are followed by parentheses, which may include parameters passed to the command. The ones that "end in parentheses" are not actually commands, but methods.
Technically speaking, a method is a named set of statements. It's a piece of a program that can be called from elsewhere in the code by the name given to the method. For instance System.out.println("I am a parameter given to the method!")
calls a methods that performs printing to the screen. The internal implementation of the method — meaning the set of statements to be executed — is hidden, and the programmer does not need to concern themselves with it when using the method.
So far all the methods we have used have been ready-made Java methods. Next we will learn to create our own methods.
Custom Methods
A method means a named set consisting of statements that can be called from elsewhere in the program code by its name. Programming languages offer pre-made methods, but programmers can also write their own ones. It would, in fact, be quite exceptional if a program used no methods written by the programmer, because methods help in structuring the program. From this point onward nearly every program on the course will therefore contain custom-created methods.
In the code boilerplate, methods are written outside of the curly braces of the main
, yet inside out the "outermost" curly braces. They can be located above or below the main.
import java.util.Scanner;
public class Example {
public static void main(String[] args) {
Scanner scanned = new Scanner(System.in);
// program code
}
// your own methods here
}
Let's observe how to create a new method. We'll create the method greet
.
public static void greet() {
System.out.println("Greetings from the method world!");
}
And then we'll insert it into a suitable place for a method.
import java.util.Scanner;
public class Example {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// program code
}
// your own methods here
public static void greet() {
System.out.println("Greetings from the method world!");
}
}
The definition of the method consists of two parts. The first line of the definition includes the name of the method, i.e. greet
. On the left side of the name are the keywords public static void
. Beneath the line containing the name of the method is a code block surrounded by curly brackets, inside of which is the code of the method — the commands that are executed when the method is called. The only thing our method greet
does is write a line of text on the screen.
Calling a custom method is simple: write the name of the methods followed by a set of parentheses and the semicolon. In the following snippet the main program (main) calls the greet method four times in total.
import java.util.Scanner;
public class ProgramStructure {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// program code
System.out.println("Let's try if we can travel to the method world:");
greet();
System.out.println("Looks like we can, let's try again:");
greet();
greet();
greet();
}
// own methods
public static void greet() {
System.out.println("Greetings from the method world!");
}
}
The execution of the program produces the following output:
Let's try if we can travel to the method world: Greetings from the method world! Looks like we can, let's try again: Greetings from the method world! Greetings from the method world! Greetings from the method world!
The order of execution is worth noticing. The execution of the program happens by executing the lines of the main method (main
) in order from top to bottom, one at a time. When the encountered statement is a method call, the execution of the program moves inside the method in question. The statements of the method are executed one at a time from top to bottom. After this the execution returns to the place where the method call occured, and then proceeds to the next statement in the program.
Strictly speaking, the main program (main
) itself is a method. When the program starts, the operating system calls main
. The main method is the starting point for the program, since the execution begins from its first line. The execution of a program ends at the end of the main method.
From here on out, when introducing methods, we will not explicitly mention that they must be located in the correct place. Methods cannot be defined e.g. inside other methods.
On Naming Methods
The names of methods begin with a word written entirely with lower-case letters, and the rest of the words begin with an upper-case letter - this style of writing is known as camelCase. Additionally, the code inside methods is indented by four characters.
In the code example below the method is poorly named. It begins with an upper-case letter and the words are separated by _ characters. The parentheses after the method name have a space between and indentation in the code block is incorrect.
public static void This_method_says_woof ( ) {
System.out.println("woof");
}
In contrast the method below is correctly named: The name begins with a lower-case letter and the words are joined together with the camelCase style, meaning that each word after the first begins with an upper-case letter. The parentheses sit next to one another and the contents are correctly indented (the method has its own code block, so the indentation of the code is four characters).
public static void thisMethodSaysWoof() {
System.out.println("woof");
}
Log in to view the quiz
Method Parameters
Parameters are values given to a method that can be used in its execution. The parameters of a method are defined on the uppermost line of the method within the parentheses following its name. The values of the parameters that the method can use are copied from the values given to the method when it is executed.
In the following example a parameterized method greet
is defined. It has an int
type parameter called numOfTimes
.
public static void greet(int numOfTimes) {
int i = 0;
while (i < numOfTimes) {
System.out.println("Greetings!");
i++;
}
}
We will call the method greet
with different values. The parameter numOfTimes
is assigned the value 1
on the first call, and 3
on the second.
public static void main(String[] args) {
greet(1);
System.out.println("");
greet(3);
}
Greetings!
Greetings! Greetings! Greetings!
Just like when calling the predefined method System.out.println
, you can pass an expression as a parameter.
public static void main(String[] args) {
greet(1 + 2);
}
Greetings! Greetings! Greetings!
If an expression is used as a parameter for a method, the expression is evaluated prior to the method call. Above, the expression evaluates to 3
and the final method call is of the form greet(3);
.
Multiple Parameters
A method can be defined with multiple parameters. When calling such a method, the parameters are passed in the same order.
public static void sum(int first, int second) {
System.out.println("The sum of numbers " + first + " and " + second + " is " + (first + second));
}
sum(3, 5);
int number1 = 2;
int number2 = 4;
sum(number1, number2);
The sum of numbers 3 and 5 is 8 The sum of numbers 2 and 4 is 6
Parameter Values Are Copied in a Method Call
As a method is called the values of its parameters are copied. In practice, this means that both the main method and the method to be called can use variables with the same name. However, changing the value of the variables inside the method does not affect the value of the variable in the main method that has the same name. Let's examine this behavior with the following program.
public class Example {
public static void main(String[] args) {
int min = 5;
int max = 10;
printNumbers(min, max);
System.out.println();
min = 8;
printNumbers(min, max);
}
public static void printNumbers(int min, int max) {
while (min < max) {
System.out.println(min);
min++;
}
}
}
The output of the program is:
5 6 7 8 9
8 9
Beneath, you'll find the same program visualized step-by-step. Changing the values of the variables in the method printNumbers does not affect the values in the main method, even though they have the same names.
So, method parameters are distinct from the variables (or parameters) of other methods, even if they had the same name. As a variable is passed to a method during a method call, the value of that variable gets copied to be used as the value of the parameter variable declared in the method definition. Variables in two separate methods are independent of one another.
To further demonstrate this point, let's consider the following example. We define a variable called number
in the main method. That variable is passed as a parameter to the method incrementByThree
.
// main program
public static void main(String[] args) {
int number = 1;
System.out.println("The value of the variable 'number' in the main program: " + number);
incrementByThree(number);
System.out.println("The value of the variable 'number' in the main program: " + number);
}
// method
public static void incrementByThree(int number) {
System.out.println("The value of the method parameter 'number': " + number);
number = number + 3;
System.out.println("The value of the method parameter 'number': " + number);
}
The execution of the program produces the following output.
The value of the variable 'number' in the main program: 1 The value of the method parameter 'number': 1 The value of the method parameter 'number': 4 The value of the variable 'number' in the main program: 1
When the variable number
is incremented inside the method, there's no issue. This, however, is not reflected in the number
variable of the main program. The number
variable living in the main program is different from the number
variable of the method.
The parameter number
is copied for the method's use, i.e., a new variable called number
is created for incrementByThree
method, to which the value of the variable number
in the main program is copied during the method call. The variable number
inside the method incrementByThree
exists only for the duration of the method's execution and has no relation to the variable of the same name in the main program.
Log in to view the quiz
Methods Can Return Values
The definition of a method tells whether that method returns a value or not. If it does, the method definition has to include the type of the returned value. Otherwise the keyword void
is used in the definition. The methods we've created so far have been defined with the keyword void
, meaning that they have not returned values.
public static **void** incrementByThree() {
...
}
The keyword void
means that the method returns nothing. If we want the method to return a value, the keyword must be replaced with the type of the return variable. In the following example, there is a method called alwaysReturnsTen
which returns an integer-type (int
) variable (in this case the value 10).
To actually return a value, we use the command return
followed by the value to be returned (or the name of the variable whose value is to be returned).
public static int alwaysReturnsTen() {
return 10;
}
The method defined above returns an int
-type value of 10
when called. For the return value to be used, it must be stored in a variable. This is done the same way as regular value assignment to a variable, by using an equals sign.
public static void main(String[] args) {
int number = alwaysReturnsTen();
System.out.println("the method returned the number " + number);
}
The return value of the method is placed in an int
type variable as with any other int
value. The return value can also be used in any other expression.
double number = 4 * alwaysReturnsTen() + (alwaysReturnsTen() / 2) - 8;
System.out.println("the result of the calculation " + number);
All the variable types we've encountered so far can be returned from a method.
Type of return value | Example |
---|---|
Method returns no value |
|
Method returns `int` type variable |
|
Method returns `double` type variable |
|
When execution inside a method reaches the command return
, the execution of that method ends and the value is returned to the calling method.
The lines of source code following the command return
are never executed. If a programmer adds source code after the return to a place which can never be reached during the method's execution, the IDE will produce an error message.
For the IDE, a method such as the following is faulty.
public static int faultyMethod() {
return 10;
System.out.println("I claim to return an integer, but I don't.");
}
The next method works since it is possible to reach every statement in it — even though there is source code below the return command.
public static int functioningMethod(int parameter) {
if (parameter > 10) {
return 10;
}
System.out.println("The number received as parameter is ten or less.");
return parameter;
}
If a method has the form public static void nameOfMethod()
it is possible to return from it — in other words, to stop its execution in that place — with the return
command that is not followed by a value. For instance:
public static void division(int numerator, int denominator) {
if (denominator == 0) {
System.out.println("Can not divide by 0!");
return;
}
System.out.println("" + numerator + " / " + denominator + " = " + (1.0 * numerator / denominator));
}
Defining Variables Inside Methods
Defining variables inside methods is done in the same manner as in the "main program". The following method calculates the average of the numbers it receives as parameters. Variables sum
and avg
are used to help in the calculation.
public static double average(int number1, int number2, int number3) {
int sum = number1 + number2 + number3;
double avg = sum / 3.0;
return avg;
}
One way to call the method is as follows.
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter the first number: ");
int first = Integer.valueOf(scanner.nextLine());
System.out.print("Enter the second number: ");
int second = Integer.valueOf(scanner.nextLine());
System.out.print("Enter the third number: ");
int third = Integer.valueOf(scanner.nextLine());
double averageResult = average(first, second, third);
System.out.print("The average of the numbers: " + averageResult);
}
Variables defined in a method are only visible inside that method. In the example above, this means that the variables sum
and avg
defined inside the method average
are not visible in the main program. A typical mistake while learning programming is to try and use a method in the following way.
public static void main(String[] args) {
int first = 3;
int second = 8;
int third = 4;
average(first, second, third);
// trying to use a method's internal variable, DOES NOT WORK!
System.out.print("The average of the numbers: " + avg);
}
In the above example, an attempt is made to use the variable avg
that has been defined inside the method average
and print its value. However, the variable avg
only exists inside the method average
, and it cannot be accessed outside of it.
The following mistakes are also commonplace.
public static void main(String[] args) {
int first = 3;
int second = 8;
int third = 4;
// trying to use the method name only, DOES NOT WORK!
System.out.print("The average of the numbers: " + average);
}
Above, there is an attempt to use the name of the method average
as if it were a variable. However, a method has to be called.
As well as placing the method result into a helper variable, another way that works is to execute the method call directly inside the print statement:
public static void main(String[] args) {
int first = 3;
int second = 8;
int third = 4;
// calling the method inside the print statement, DOES WORK!
System.out.print("The average of the numbers: " + average(first, second, third));
}
Here, the method call occurs first returning the value 5.0, which is then printed with the help of the print statement.
Log in to view the quiz
Calculating the Return Value Inside a Method
The return value does not need to be entirely pre-defined - it can also be calculated. The return command that returns a value from the method can also be given an expression that is evaluated before the value is returned.
In the following example, we'll define a method sum that adds the values of two variables and returns their sum. The values of the variables to be summed are received as method parameters.
public static int sum(int first, int second) {
return first + second;
}
When the execution of the method reaches the statement return first + second;
, the expression first + second
is evaluated, and then its value is returned.
The method is called in the following way. Below, the method is used to add the numbers 2 and 7 together. The value resulting from the method call is placed into the variable sumOfNumbers
.
int sumOfNumbers = sum(2, 7);
// sumOfNumbers is now 9
Let's expand the previous example so that the numbers are entered by a user.
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter the first number: ");
int first = Integer.valueOf(scanner.nextLine());
System.out.print("Enter the second number: ");
int second = Integer.valueOf(scanner.nextLine());
System.out.print("The combined sum of the numbers is: " + sum(first, second));
}
public static int sum(int first, int second) {
return first + second;
}
In the example above, the method's return value is not stored in a variable but is instead directly used as part of the print operation. The print command's execution is done by the computer first evaluating the string "The combined sum of the numbers is: "+ sum(first, second)
. The computer first looks for the variables first
and second
and copies their values as the values of the method sum
's parameters. The method then adds the values of the parameters together, after which it returns a value. This value takes the place of the sum
method call, whereby the sum is appended to the string "The combined sum of the numbers is: "
.
Since the values passed to a method are copied to its parameters, the names of the parameters and the names of the variables defined on the side of the caller have, in fact, nothing to do with each other. In the previous example, both the variables of the main program and the method parameters were named the same (first
and second
) "by accident". The code below will function in precisely the same manner even though the variables are named differently:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter the first number: ");
int number1 = Integer.valueOf(scanner.nextLine());
System.out.print("Enter the second number: ");
int number2 = Integer.valueOf(scanner.nextLine());
System.out.print("The total sum of the numbers is: " + sum(number1, number2));
}
public static int sum(int first, int second) {
return first + second;
}
Now the value of the variable number1
is copied as the value of the method parameter first
, and the value of the variable number2
is copied as the value of the parameter second
.
Log in to view the quiz
Execution of Method Calls and the Call Stack
How does the computer remember where to return after the execution of a method?
The environment that executes Java source code keeps track of the method being executed in the call stack. The call stack contains frames, each of which includes information about a specific method's internal variables and their values. When a method is called, a new frame containing its variables is created in the call stack. When the execution of a method ends, the frame relating to a method is removed from the call stack, which leads to execution resuming at the previous method of the stack.
The right side of the visualization below displays the functioning of the call stack. When a method is called, a new frame is created in the stack, which is removed upon exit from the method call.
When a method is called, the execution of the calling method is left waiting for the execution of the called method to end. This can be visualized with the help of a call stack. The call stack refers to the stack formed by the method calls — the method currently being executed is always on the top of the stack, and when that method has finished executing the execution moves on to the method that is next on the stack. Let's examine the following program:
public static void main(String[] args) {
System.out.println("Hello world!");
printNumber();
System.out.println("Bye bye world!");
}
public static void printNumber() {
System.out.println("Number");
}
The execution begins from the first line of the main
method when the program is run. The command on this line prints the text "Hello world!"
. The call stack of the program looks as follows:
main
Once the print command has been executed, we move on to the next command, which calls the method printNumber
. Calling this method moves the execution of the program to the beginning of the method printNumber
. Meanwhile, the main
method will await for the execution of the method printNumber
to end. While inside the method printNumber
, the call stack looks like this:
printNumber main
Once the method printNumber
completes, we return to the method that is immediately below the method printNumber
in the call stack — which in this case is the method main
. printNumber
is removed from the call stack, and the execution continues from the line after the printNumber
method call in the main
method. The state of the call stack is now the following:
main
Call Stack and Method Parameters
Let's examine the call stack in a situation where parameters have been defined for the method.
public static void main(String[] args) {
int beginning = 1;
int end = 5;
printStars(beginning, end);
}
public static void printStars(int beginning, int end) {
while (beginning < end) {
System.out.print("*");
beginning++; // same as beginning = beginning + 1
}
}
The execution of the program begins on the first line of the main
method. The next two lines create the variables beginning
and end
, and also assign values to them. The state of the program prior to calling the method printStars
:
When printStars
is called, the main
method enters a waiting state. The method call causes new variables beginning
and end
to be created for the method printStars
, to which the values passed as parameters are assigned to. These values are copied from the variables beginning
and end
of the main
method. The state of the program on the first line of the execution of the method printStars
is illustrated below.
When the command beginning++
is executed within the loop, the value of the variable beginning
that belongs to the method currently being executed changes.
As such, the values of the variables in the method main
remain unchanged. The execution of the method printStart
would continue for some time after this. When the execution of that method ends, the execution resumes inside the main
method.
Let's observe the same program by visualizing its execution step-by-step. The application used for visualization grows the call stack downwards — on the right side, the method on top is always main
, under which go the methods being called.
Call Stack and Returning a Value from a Method
Let's now study an example where the method returns a value. The main
method of the program calls a separate start
method, inside of which two variables are created, the sum
method is called, and the the value returned by the sum
method is printed.
public static void main(String[] args) {
start();
}
public static void start() {
int first = 5;
int second = 6;
int sum = sum(first, second);
System.out.println("Sum: " + sum);
}
public static int sum(int number1, int number2) {
return number1 + number2;
}
At the beginning of the start
method's execution the call stack looks as in the following illustration since it was called from the main
method. The method main
has no variables of its own in this example:
When the variables first
and second
have been created in the start
method (i.e., the first two rows of that method have been executed), the situation is the following:
The command int sum = sum(first, second);
creates the variable sum
in the method start
and calls the method sum
. The method start
enters a waiting state. Since the parameters number1
and number2
are defined in the method sum
, they are created right at the beginning of the method's execution, after which the values of the variables given as parametes are copied into them.
The execution of the method sum
adds together the values of the variables number1
and number2
. The command return
returns the sum of the numbers to the method that is one beneath it in the call stack - the method start
in this case. The returned value is set as the value of the variable sum
.
After that, the print command is executed, and then we return to the main
method. Once the execution reaches the end of the main
method, the execution of the program ends.
Method Calling Another Method
As we noticed earlier, other methods can be called from within methods. An additional example of this technique is given below. We'll create the method multiplicationTable
that prints the multiplication table of the given number. The multiplication table prints the rows with the help of the printMultiplicationTableRow
method.
public static void multiplicationTable(int max) {
int number = 1;
while (number <= max) {
printMultiplicationTableRow(number, max);
number++;
}
}
public static void printMultiplicationTableRow(int number, int coefficient) {
int printable = number;
while (printable <= number * coefficient) {
System.out.print(" " + printable);
printable += number;
}
System.out.println("");
}
The output of the method call multiplicationTable(3)
, for instance, looks like this.
Below is a visualization of the method call multiplicationTable(3)
. Notice how the information about the internal state of the calling method is stored in the call stack.
Remember to check your points from the ball on the bottom-right corner of the material!