Java is a statically-typed object-oriented language. Java programs are organised as a collection of classes and interfaces. Classes extend a single superclass, implement a number of interfaces, and define various fields and methods. Interfaces can declare any number of super-interfaces, along with methods that implementing classes must define.

Hello.java

package test;

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello world!");
    }
}

This defines a class Hello in the package test. This class contains a single static main method which writes a short message to the console.

This file can be compiled with javac:

$ javac -d . Hello.java

This outputs a Hello.class file in the ./test directory. The main method can be invoked with:

$ java test.Hello
Hello world!

Entry point

Java program execution begins from a named class specified to the java runtime. In this previous example this was the class test.Hello. The JVM loads this class and executes its main method, which must have the signature public static void main(String[]) i.e. a static method called main which takes a single string array parameter. This array receives all the argument provided to the java command which do not configure the execution of the JVM itself.

Loading classes

In order to execute the main method of the test.Hello class, the JVM must be able to locate and load the binary definition of the class. The job of locating and loading classes is done by class loaders. These are implementations of the java.lang.ClassLoader class. The JVM defines a default class loader, called the ‘bootstrap class loader’.

Classpath

The JVM locates user classes by searching a list of locations defined by the ‘classpath’. By default, the classpath usually just contains the current directory the java command is invoked from. The full name of the class dictates the expected location of the .class file containing the binary representation of the class. For a simple top-level class like test.Hello, this location is constructed by simply replacing the . characters in the class name with / path separator characters, and adding a .class extension. So the class test.Hello should exist at test/Hello.class under one of the directories on the classpath. Since the default classpath contains just the current directory, the only candidate location is at ./test/Hello.class.

If you change into the test directory and run the java command again you will receive an error:

$ cd test
$ java test.Hello
Error: Could not find or load main class test.Hello

This is because within the test directory, no test/Hello.class file exists so the test.Hello class cannot be loaded.

Within this directory, the Hello.class file does exist, so you might try the following:

$ java Hello
Error: Could not find or load main class Hello
Caused by: java.lang.NoClassDefFoundError: test/Hello (wrong name: Hello)

In this case, the Hello.class file was located, however the class defined there has a different name test.Hello so loading fails.

Since classes are resolved via their full names relative to the classpath, you need to put the previous directory (i.e. the current parent directory) on the classpath. This can be done with the -classpath option to the java command:

$ java -classpath ".." test.Hello
Hello world!

change back to the parent directory where the Hello.java file is defined

$ cd

System classes

The test.Hello class makes reference to another class java.lang.System which is defined by the core Java library. Since there’s no java/lang/System.class file under the current directory, you might wonder how it is loaded. The answer is that the bootstrap classloader has its own internal mechanism for locating core library classes. The only requirement for classloaders is that they can resolve and load classes given their full (binary) names. There is no requirement that these exist as .class files on disk. They can be loaded from the network, a database or defined dynamically depending on how the classloader is implemented. The bootstrap classloader knows where to locate core Java classes and loads them as required.

Older JVMs (before version 9) did ship core Java classes in a Java archive. This was usually located at jre/lib/rt.jar within the JVM distribution. The bootstrap classloader was additionally configured with a ‘bootstrap classpath’ containing this core archive. Since the advent of Java modules in Java 9, core classes are distributed in a more efficient format.

You can tell the java command to log more information on the class loading process with the -verbose option.

$ java -verbose:class test.Hello
...
[0.006s][info][class,load] java.lang.System source: shared objects file
...
[0.027s][info][class,load] test.Hello source: file:/classpath/directory/

This will usually show a lot of output as each class and their references need to be loaded as part of the program execution.