Introduction
The first piece of code most Java students see is the “Hello World” application. This program prints the message “Hello World” to the console and terminates. But, despite the simplicity of what the program does, the code itself has a lot going on.
Our first Java program bombards us with keywords like class
, public
, static
, and void
. There’s a String[]
variable thrown in there as well, for good measure. Why is all of this code here, and what does it do?
In this blog post, we’ll break down Java’s “Hello World” program, and explain what all of the component pieces actually mean.
The “Hello World” Application
Let’s take a look at one version of the code for “Hello World” (we’ll use some other versions as well, but this one is the classic):
public class HelloWorldPrinter
{
public static void main(String[] args)
{
System.out.println("Hello World");
}
}
When I’m tutoring Java programming at the beginning of a semester, I’ll refer to the first two lines of code (the ones containing “class
” and “main
“) as “magic”. They’re simply what we have to surround our “actual” code with, to get our program to run. I’ll also simplify “System.out
“, saying it’s just something that goes in front of “println()
” to “make it work”.
These simplifications work well enough at the beginning of the semester. But, by the end of an introductory course in Java programming, we’d like to fully understand all the pieces of code in “Hello World” , and what each word really does. Let’s dive right in.
The class
Declaration
First, let’s focus on the class
keyword. This keyword denotes that HelloWorldPrinter
is a class. We can instantiate objects of this class:
public class HelloWorldPrinter
{
public static void main(String[] args)
{
HelloWorldPrinter hwp = new HelloWorldPrinter();
}
}
That call to new
works because there’s an implicit constructor in the HelloWorldPrinter
class:
public class HelloWorldPrinter
{
// public HelloWorldPrinter()
// {
// }
public static void main(String[] args)
{
HelloWorldPrinter hwp = new HelloWorldPrinter();
}
}
To get one possible implementation of “Hello World” working (different from the classic implementation we saw earlier), we could add an instance method to our class, which will be responsible for producing the output:
public class HelloWorldPrinter
{
// public HelloWorldPrinter()
// {
// }
public void outputMessage()
{
System.out.println("Hello World");
}
public static void main(String[] args)
{
HelloWorldPrinter hwp = new HelloWorldPrinter();
hwp.outputMessage();
}
}
The public
Modifier
The public
access modifier is used in both the class
declaration and the main
method declaration. In both of these cases, the access modifier means that the class and the method can be used by or called from any other code.
As an example, let’s consider the following version of HelloWorldPrinter
:
public class HelloWorldPrinter
{
public static void outputMessage()
{
System.out.println("Hello World");
}
public static void main(String[] args)
{
HelloWorldPrinter.outputMessage();
}
}
Another class can make use of the HelloWorldPrinter
class and its public
methods, regardless of where in the codebase the other class is located:
public class SomeOtherClass
{
public static void main(String[] args)
{
System.out.println("Hello Mars");
HelloWorldPrinter.outputMessage();
System.out.println("Goodbye Mars");
}
}
Compiling and running the code, we see that SomeOtherClass
‘ main
method produces its own output on either side of the HelloWorldPrinter
class’ output:
% javac HelloWorldPrinter.java SomeOtherClass.java
% java SomeOtherClass
Hello Mars
Hello World
Goodbye Mars
As an interesting aside, there’s no reason we couldn’t instead call HelloWorldPrinter
‘s main
method from SomeOtherClass
. Because HelloWorldPrinter
‘s main
method is public
, we can call it from anywhere:
public class SomeOtherClass
{
public static void main(String[] args)
{
System.out.println("Hello Mars");
HelloWorldPrinter.main(null);
System.out.println("Goodbye Mars");
}
}
(We just use null
as the argument to main
, since HelloWorldPrinter
‘s main
method ignores its args
parameter anyway.)
The static
Keyword
The next keyword we come to is the static
keyword, used in the method declaration for main
:
public class HelloWorldPrinter
{
public static void main(String[] args) {...}
}
The static
keyword means that the method doesn’t belong to any object of the HelloWorldPrinter
class. Instead, it belongs to the class itself.
Let’s illustrate this difference by adding both a static method and an instance method to the HelloWorldPrinter
class:
public class HelloWorldPrinter
{
public static void outputStatic()
{
System.out.println("Hello Static");
}
public void outputInstance()
{
System.out.println("Hello Instance");
}
public static void main(String[] args) {...}
}
Instance methods can only be called on objects, and cannot be called on the class itself:
public class HelloWorldPrinter
{
public void outputInstance() {...}
public static void main(String[] args)
{
HelloWorldPrinter.outputInstance(); // WRONG
HelloWorldPrinter hwp = new HelloWorldPrinter();
hwp.outputInstance();
}
}
Static methods, on the other hand, should only be called on the class itself, and shouldn’t be called on objects:
public class HelloWorldPrinter
{
public static void outputStatic() {...}
public static void main(String[] args)
{
HelloWorldPrinter.outputStatic();
HelloWorldPrinter hwp = new HelloWorldPrinter();
hwp.outputStatic(); // WRONG
}
}
With that in mind, what does it mean for us that the main
method is static — that the place where our code starts belongs to the HelloWorldPrinter
class, not to any object of that class? It means any instance variables or instance methods in the HelloWorldPrinter
class won’t be available to the static main
method directly; we have to construct an object of type HelloWorldPrinter
to use them.
In fact, because the main
method will always be static, it’s a common idiom to construct a new object that represents your application and its resources, then invoke a single method on that object to run the application:
public class HelloWorldPrinter
{
private void runProgram() {...}
public static void main(String[] args)
{
HelloWorldPrinter hwp = new HelloWorldPrinter();
hwp.runProgram();
}
}
The void
Return Type
The void
keyword in the main
method declaration indicates that the main
method doesn’t return a value. We can contrast this with a method that maybe answers a question, such as what’s the sum of two numbers:
public static int sum(int a, int b)
{
return a + b;
}
The main
method simply runs and terminates, without any returned value. If you want to terminate the main method early, the following code that uses just a return
statement is correct:
public static void main(String[] args)
{
System.out.println("Hello World");
return;
}
But, the following code is not, because main
is a void
method:
public static void main(String[] args)
{
System.out.println("Hello World");
return 0; // WRONG
}
The main
Method Name
The name of the main
method is significant because it tells the Java Virtual Machine (JVM) where to begin execution of your code. When you execute the JVM on a class, it looks for a main
method in the class to start running.
Let’s go back to our project with two classes:
public class HelloWorldPrinter
{
public static void main(String[] args)
{
System.out.println("Hello World");
}
}
and:
public class SomeOtherClass
{
public static void main(String[] args)
{
System.out.println("Hello Mars");
}
}
We can compile the code:
% javac HelloWorldPrinter.java SomeOtherClass.java
Then, when we tell the JVM which class we want to execute, the JVM will look for the main
method in that class to run:
% java HelloWorldPrinter
Hello World
or:
% java SomeOtherClass
Hello Mars
Your code in a large project could have numerous main
methods in different classes, each representing a single application in a suite of programs.
The String[]
Array
The String[]
array in the main
method signature is a parameter that’s filled by the JVM with all the arguments provided by the user on the command line. One example where a user might want to pass an argument to a program on the command line could be a choice of configuration file to use:
% java CriticalSafetyControlApp daytimeConfig.xml
Even though it doesn’t involve critical safety controls, let’s change up our “Hello World” program to look through its command-line arguments:
public class HelloWorldPrinter
{
public static void main(String[] args)
{
for (String a : args) {
System.out.println("Hello " + a);
}
}
}
If we pass three arguments on the command line, each one will be placed into the args
array:
% javac HelloWorldPrinter.java
% java HelloWorldPrinter Alice Bob Carol
Hello Alice
Hello Bob
Hello Carol
If no arguments are provided on the command line, args
will be an empty array. So, in this case, no greetings are printed:
% java HelloWorldPrinter
Unlike with the name main
, there’s nothing special about the variable name args
— using that name is simply convention. Nothing is stopping you from using a different variable name, expect the confusion you’d cause:
public class HelloWorldPrinter
{
// PLEASE DON'T
public static void main(String[] foo)
{
for (String f : foo) {
System.out.println("Hello " + f);
}
}
}
The System.out
Object
It’s easy to forget, just like with the HelloWorldPrinter
class, that the System
class is itself a class. It’s easy to forget because it’s in the part of the Java language that gets automatically imported into every project. You never need to write:
import java.lang.System; // UNNECESSARY
public class HelloWorldPrinter {...}
If we dive into the Java API for the System
class, we see that it has a static
field that is public
in accessibility called out
. That is to say, the out
object, being static
, belongs to the System
class itself.
From the API page for System
, the out
field is defined as:
public static final PrintStream out
This field definition tells us that System.out
is an object of the PrintStream
class. But, what use is that information to us? Let’s dive one layer deeper into the Java API documentation.
The PrintStream
Class
Now that we’ve drilled down into the System
class to see that System.out
is a PrintStream
object, let’s learn more about it. If we go to the Java API page for the PrintStream
class, we can see what methods are available to us.
Of note for our first Java application, the PrintStream
class has numerous overloaded println
methods, such as:
public void println(String x)
It’s this instance method of the PrintStream
object that produces our application’s output.
As a study exercise on the topic of overloaded methods, determine which println()
method will be executed by a PrintStream
object when different data types are given as arguments.
Summary
For a first program we learn as computer science students, there’s certainly a lot happening in Java’s version of “Hello World”. For most of the semester, I’m happy for students to think of a lot of the code in “Hello World” as “magic”. It’s just the stuff surrounding the code we actually care about.
But, as the end of an introductory term in Java approaches, it’s worth looking back at the first “Hello World” code we wrote. Of course it’s possible that, in your semester, you won’t have covered the meaning of all of the keywords in the code. But, see how many of them you can not only recognize, but also explain.
I also recommend you take the opportunity, while looking at the “Hello World” code, to review concepts like static fields (such as System.out
), instance methods (such as println
), and static methods (such as main
). Finding examples of these types of fields and methods in the Java standard library can also be a good exercise to familiarize yourself with the extensive API documentation.
Java’s seemingly simple “Hello World” program has a surprisingly large number of study topics bunched together in just a few lines of code!
For more tips, and to arrange for personalized tutoring for yourself or your study group, check out Vancouver Computer Science Tutoring.