- Understand the basic structure of a Java program
- Learn essential Java syntax elements through a "Hello World" example
- Grasp the concept and importance of the main method in Java
- Recognize the significance of proper code formatting and syntax rules
Every programming language has syntax—the grammatical rules that determine whether your code compiles or crashes spectacularly with error messages. Java's syntax is verbose compared to Python or Ruby, inheriting much of its structure from C and C++. That verbosity is deliberate. Java prioritizes clarity and type safety over brevity, which matters when you're maintaining a million-line codebase written by dozens of developers over a decade.
The "Hello World" program is programming's equivalent of a soundcheck. It verifies your toolchain works, introduces basic syntax, and establishes the minimal structure required for a runnable Java application. You'll write more sophisticated code soon enough, but understanding why every keyword and punctuation mark exists in this simple program prevents confusion later when you're debugging why your servlet container won't start or your lambda expression won't compile.
This lesson dissects Java's fundamental syntax using the simplest possible program as a reference. You'll learn what makes Java code legal versus illegal, why the language enforces certain structural requirements, and how the JVM interprets the code you write.
Here's the canonical first Java program:
package academy.javapro.module2;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}Five lines of actual code to print one string. Python does this in a single print() call. JavaScript needs one console.log(). But Java's verbosity encodes information the compiler and JVM use for type checking, memory management, and runtime optimization. Each piece serves a purpose.
The package declaration package academy.javapro.module2; establishes this class's namespace. Packages prevent naming collisions and organize code logically. Without packages, you'd be in the global namespace where every class name must be unique across your entire application. That works until you want both a com.amazonaws.auth.User class and your own User class representing application users. Packages make both possible by giving them distinct fully-qualified names.
The class declaration public class HelloWorld defines a type named HelloWorld. Java is object-oriented to its core—you can't write free-floating functions like in C or Python. Every method must belong to a class. The public access modifier means this class is visible to code in other packages. If you omit public, the class becomes package-private, visible only within academy.javapro.module2.
The filename must match the public class name exactly: HelloWorld.java. Java enforces this mapping to make source files locatable. When the compiler sees an import statement or a reference to an unknown class, it searches for a file matching that class name in the appropriate package directory. This one-to-one relationship between public classes and files is non-negotiable.
Curly braces {} delimit scope in Java. The opening brace after the class name marks the beginning of the class body. Everything inside belongs to this class. The closing brace at the end terminates the class definition. Proper brace placement and indentation aren't just style—they're essential for readability. Most Java code follows the "Egyptian brackets" or K&R style (opening brace on the same line), though some developers prefer Allman style (opening brace on the next line). Pick one and be consistent.
The main method is where program execution begins:
public static void main(String[] args) {
System.out.println("Hello World!");
}This exact signature is required. Changing any part breaks the contract the JVM expects. When you run java academy.javapro.module2.HelloWorld, the JVM searches for a method with this precise signature and invokes it. No main method? Runtime error. Wrong signature? The JVM won't recognize it as an entry point.
public makes the method accessible to the JVM, which exists outside your class and needs to call this method to start your program. If you make main private, the JVM can't access it—compilation succeeds, but execution fails with a runtime error complaining about missing or inaccessible main method.
static means this method belongs to the class itself, not to instances of the class. When the JVM starts your program, no objects exist yet. The JVM can't call an instance method because there's no instance to call it on. Static methods are class-level functionality, callable without instantiating the class. You'll see HelloWorld.main(args) conceptually, though the JVM handles the invocation internally.
void specifies the return type—in this case, nothing. The main method doesn't return a value to the JVM. Instead, programs communicate exit status through System.exit(int status), where zero conventionally means success and non-zero indicates errors. Most programs don't call System.exit() explicitly; the JVM infers successful completion when main returns normally.
String[] args is a parameter accepting command-line arguments. When you run java HelloWorld arg1 arg2 arg3, the JVM packages those arguments into a String array and passes it to main. The parameter name args is conventional but not required—you could name it arguments or commandLineParameters. The type String[] is required, though you can write it as String args[] using C-style array syntax (don't do this; the Java community standardized on String[] args decades ago).
The method body between the curly braces contains the executable code. System.out.println("Hello World!"); is a statement—a complete instruction terminated by a semicolon. Java requires semicolons to mark statement boundaries. Missing semicolons are rookie mistakes that the compiler catches immediately with clear error messages.
System.out.println("Hello World!"); demonstrates several Java concepts in one line:
System is a class in the java.lang package, automatically imported into every Java file. It provides system-level utilities and access to standard input, output, and error streams.
out is a static field of type PrintStream declared in the System class. Because it's public and static, you access it through the class name: System.out. This field represents standard output—typically your console or terminal.
println() is a method of PrintStream that prints its argument followed by a platform-specific line terminator. The method is overloaded—PrintStream defines multiple println() methods accepting different argument types (String, int, double, Object, etc.). The compiler selects the appropriate version based on your argument type. Here, "Hello World!" is a String literal, so the compiler calls println(String x).
String literals in Java use double quotes. Single quotes are for char literals—'A' is a character, "A" is a one-character String. Attempting to pass a char where a String is expected usually works because of autoboxing and String conversion, but the types are fundamentally different. Strings are objects on the heap, chars are primitive values.
The semicolon terminates the statement. Java's grammar requires semicolons to distinguish where one statement ends and another begins. Some languages use newlines as statement separators (Python), others use them but make semicolons optional (JavaScript, Kotlin). Java chose mandatory semicolons for unambiguous parsing. You can write multiple statements on one line if you really want: System.out.println("Hello"); System.out.println("World");. Readable? No. Legal? Yes.
Java enforces strict naming rules for classes and files. Class names must be valid Java identifiers: they start with a letter, underscore, or dollar sign, followed by any combination of letters, digits, underscores, or dollar signs. Keywords like class, public, void are forbidden as identifiers—they're reserved by the language.
Convention dictates PascalCase for class names: HelloWorld, CustomerRepository, HttpRequestHandler. The first letter of each word is capitalized. This isn't enforced by the compiler, but violating it marks you as inexperienced. You could name a class helloWorld or HELLOWORLD, and Java would accept it. Your colleagues would not.
The filename must match the public class name exactly, including case: HelloWorld.java for class HelloWorld. File systems on Windows are case-insensitive, so helloworld.java might seem to work during development, but this breaks when you deploy to Linux servers with case-sensitive filesystems. Develop with strict case matching to avoid painful debugging sessions in production.
A source file can contain multiple classes, but only one can be public. The public class determines the filename. You might have:
package academy.javapro.module2;
public class HelloWorld {
public static void main(String[] args) {
Helper helper = new Helper();
helper.greet();
}
}
class Helper {
void greet() {
System.out.println("Hello from Helper!");
}
}This is legal—one public class (HelloWorld) and one package-private class (Helper) in HelloWorld.java. The Helper class isn't accessible outside the package because it lacks the public modifier. This technique is occasionally useful for tightly coupled utility classes, but most developers prefer one class per file for easier navigation and maintenance.
Java is case-sensitive throughout. System and system are different identifiers. public works; Public doesn't. This extends to method names, variable names, package names—everything. The compiler won't guess what you meant. Typos fail compilation immediately, which is actually helpful. Better a compile-time error than a runtime bug from calling the wrong method.
Whitespace (spaces, tabs, newlines) is mostly irrelevant to Java's grammar. The compiler treats multiple spaces as equivalent to one space and ignores most newlines. You could theoretically write an entire program on a single line. Don't. Readable code uses whitespace to group related statements, separate logical blocks, and maintain consistent indentation.
Indentation in Java is convention, not syntax. Python enforces indentation as block structure; Java uses braces. You can write:
public class HelloWorld{public static void main(String[] args){System.out.println("Hello World!");}}It compiles. It's hideous. Professional code uses consistent indentation (typically four spaces or one tab per level) to show nesting visually. Your IDE handles this automatically through auto-formatting.
Comments in Java come in three flavors: single-line (// comment), multi-line (/* comment */), and Javadoc (/** documentation */). Comments are ignored by the compiler—they're for human readers. Javadoc comments have special significance because the javadoc tool parses them to generate API documentation, but syntactically they're just multi-line comments with extra asterisks.
Beginner mistakes follow predictable patterns. Missing semicolons are the classic error. The compiler reports where it noticed the problem, which isn't always where you forgot the semicolon. A missing semicolon on line 5 might generate an error on line 6 because the compiler keeps parsing, expecting more of the incomplete statement.
Mismatched braces break everything. Java uses braces for class bodies, method bodies, control structures, and initializer blocks. Every opening brace needs a closing brace. IDEs highlight matching pairs and auto-insert closing braces, but you can still create mismatches when refactoring code. The compiler reports brace mismatches, though the line numbers can be misleading if you have many levels of nesting.
Incorrect capitalization causes "cannot find symbol" errors. Reference system.out.println() instead of System.out.println() and the compiler complains it can't find system. The error message usually points to the problem clearly, but novices sometimes stare at obviously correct code without noticing the case difference.
Forgetting the main method signature is common. Write public void main(String[] args) (missing static) or public static void main() (missing parameters) and the program compiles but won't run. The JVM needs the exact signature. Some students memorize it as an incantation without understanding why each keyword exists, then make subtle mistakes when writing it from memory.
Java syntax is verbose but predictable. The structure enforces strong typing, explicit class boundaries, and clear method signatures. This verbosity isn't accidental—it enables the compiler to catch errors early and the JVM to optimize code aggressively at runtime.
The "Hello World" program demonstrates core syntactic requirements: package declarations for namespace management, public class definitions matching filenames, the static main method with its exact signature for program entry, and method invocations with proper syntax for statement termination. Each element serves the language's design goals around type safety, object orientation, and platform independence.
Understanding Java's syntax precision helps you write code that compiles correctly the first time. The language won't infer what you meant from context like Python might. It demands exactness: correct case, required semicolons, matching braces, precise method signatures. This strictness frustrates beginners who've used more forgiving languages, but it prevents entire categories of bugs that dynamic languages catch only at runtime—if they catch them at all.
The rules you've learned here—identifier naming, file conventions, statement syntax, method signatures—apply to every Java program you'll write. Simple programs might feel over-structured with their required classes and boilerplate main methods. Complex enterprise systems with thousands of classes rely on these same structural rules to remain comprehensible and maintainable. The syntax scales because it's consistent from the smallest utility to the largest application.