1. Static Void Function Declarations
Creating functions
A function is a block of code that can be run. We have seen how to run functions such as Math.abs(-1), and here is the first function we will write besides void main:
public class MyProgram { public static void main(String[] args) { doSomeThing(); //This is a "function call" or "method call" doSomeThing(); //This is also a function call doSomeThing(); //We sometimes call these "method executions" } //This is a function declaration: static void doSomeThing() { System.out.println("Rainbow Dash"); } //(the function declaration has ended) }
If you run this program, you will see that it outputs "Rainbow Dash" three times. The program starts in the public static void main. Then, each doSomeThing() is a "function call" which jumps down to the function declaration. Then, when the function finishes running all its lines, the execution jumps back to where we were in main.
The syntax of a function call is function_name( arguments... ). We will learn more about arguments (parameters, inputs) later, so for now a function call is simply function_name(). The parenthesis are required.
Let us see another declaration:
public class MyProgram { static void marvin() { for(int i = 0; i < 3; i++) System.out.println("The paranoid android"); } public static void main(String[] args) { doSomeThing(); //Rainbow Dash marvin(); //Prints The paranoid android three times doSomeThing(); //Rainbow Dash } static void doSomeThing() { System.out.println("Rainbow Dash"); } }
Notice that the order of these declarations is not important (unlike in C or C++). However, it is important that every function is declared within a class, and it is important that functions are not within another function (until we learn about anonymous classes much later).
Why do we write functions?. As your programs get longer, you will want to split off chunks of it into smaller programs. Recall when we constructed algorithms by putting one algorithm in another. We will be doing the same with function calls, which will allow us to understand and write our program in small chunks.
The syntax of a function declaration
At bare minimum, a function declaration has the syntax RETURN_TYPE function_name(){}. In the case of the above examples, we have the two functions with the names "main" and "doSomeThing". The return type for both functions is "void", which means nothing. We will learn more about return types below.
static: In addition, you will notice that both main and doSomeThing have the word "static" in front of them. We will not fully appreciate this keyword until we learn about Classes. Specifically, static means that the function can be run without an associated Object. For now, every function you will write needs the static keyword.
public: The main function has another keyword in front of it, "public". Later on, when we have programs with multiple files and multiple classes, we will use these visibility modifiers. Visibility modifiers control what is allowed (permitted) to run a function. For now, you can leave out the keyword, which is called using the default visibility.
We will learn more about what the String[] args means below.
The call stack
When Java starts a program, it looks for the public static void main(String[] args), and jumps to the first line in that function. Then, when the program calls another function, Java keeps track of the path that it took to reach the function. This allows Java to jump back to the caller function when a function finishes. Here is an example where a function calls a function:
public class MyProgram { public static void main(String[] args) { marvin(); //Prints Paranoid, then Android three times } static void marvin() { for(int i = 0; i < 3; i++) { paranoid(); } } static void paranoid() { System.out.println("Paranoid"); System.out.println("Android"); } }
We can follow the execution of this function:
//01. Call main() //02. [main()] Call marvin() //03. [main()] [marvin()] Enter loop, i = 0 //04. [main()] [marvin()] Call paranoid() //05. [main()] [marvin()] [paranoid()] Output Paranoid //06. [main()] [marvin()] [paranoid()] Output Android //07. [main()] [marvin()] [paranoid()] Exit paranoid() //08. [main()] [marvin()] Iterate Loop(), i = 1 //09. [main()] [marvin()] Call paranoid() //10. [main()] [marvin()] [paranoid()] Output Paranoid //11. [main()] [marvin()] [paranoid()] Output Android //12. [main()] [marvin()] [paranoid()] Exit paranoid() //13. [main()] [marvin()] Iterate Loop(), i = 2 //14. [main()] [marvin()] Call paranoid() //15. [main()] [marvin()] [paranoid()] Output Paranoid //16. [main()] [marvin()] [paranoid()] Output Android //17. [main()] [marvin()] [paranoid()] Exit paranoid() //18. [main()] [marvin()] Iterate Loop(), i = 3 //19. [main()] [marvin()] Exit Loop() //20. [main()] [marvin()] Exit marvin() //21. [main()] Exit main() //22. Program ends
Indeed, you can use a "debugger" to follow the program execution while it is running.