Why this matters
- A single character wrong in the
mainsignature and the JVM refuses to start, with an error that does not explain why. - Access modifiers are the only tool the language gives you for enforcing boundaries, and the default one is the one most people cannot name.
- Initialisation order decides whether a field is set before you read it, and getting it wrong produces nulls that look impossible.
The entry point
public static void main(String[] args) { }
Each word is required, and for a reason.
public— the JVM calls this from outside your class, so it must be visible everywhere.static— it runs before any object exists, so it cannot belong to an instance.void— the JVM ignores return values; the exit code comes fromSystem.exitinstead.main— the name the launcher looks for. It is not a keyword, just a convention the JVM enforces.String[] args— command-line arguments. The array is empty, nevernull, when none are passed.
Packages and the file layout
A package is a namespace that also maps to a directory. The declaration must be the first statement in the file.
package com.prepzone.billing;
import java.util.List; // one class
import java.util.concurrent.*; // every class in that package
import static java.lang.Math.max; // a static member, usable unqualified
public class Invoice {
public static void main(String[] args) {
System.out.println(max(10, 20));
}
}
Rules worth knowing
- The file must be named after its
publicclass, and that class must sit in a directory path matching its package. - A file may contain several classes but only one
publicone. java.langis imported automatically, which is whyStringandSystemneed no import.- A wildcard import does not include sub-packages —
java.util.*does not bring injava.util.concurrent. - Importing two classes with the same simple name from different packages forces you to write one of them fully qualified.
Access modifiers
Four levels, and the one with no keyword is the one people forget.
| Aspect | Modifier | Visible to |
|---|---|---|
| private | Only the declaring class | Nothing outside, not even a subclass |
| (none) — package-private | Every class in the same package | Not subclasses in other packages |
| protected | Same package, plus subclasses anywhere | Not unrelated classes elsewhere |
| public | Everywhere | Everywhere |
private
ModifierOnly the declaring classVisible toNothing outside, not even a subclass(none) — package-private
ModifierEvery class in the same packageVisible toNot subclasses in other packagesprotected
ModifierSame package, plus subclasses anywhereVisible toNot unrelated classes elsewherepublic
ModifierEverywhereVisible toEverywhere
Omitting a modifier is a real choice called package-private, not an absence of one.
The practical guidance is to start every field private and every method as narrow as it can
be, then widen only when something genuinely needs access. Widening later is easy; narrowing a
published API is not.
Initialisation order
This is the part that causes surprising nulls. Static members initialise once when the class is first used; instance members initialise on every construction.
public class Order {
static String region = log("1. static field");
static { log("2. static block"); }
String id = log("3. instance field");
{ log("4. instance block"); }
Order() { log("5. constructor"); }
static String log(String step) {
System.out.println(step);
return step;
}
public static void main(String[] args) {
new Order();
System.out.println("---");
new Order();
}
}
1. static field
2. static block
3. instance field
4. instance block
5. constructor
---
3. instance field
4. instance block
5. constructor
The order, stated plainly
- Static field initialisers and static blocks run once, in source order, when the class is first loaded and initialised.
- For each new object: instance field initialisers and instance initialiser blocks run in source order, then the constructor body.
- With inheritance, the parent's static work happens before the child's, and the parent's constructor completes before the child's body begins.
Common misreadings
- "
static void mainis fine." It compiles, but the JVM cannot call it. The launcher requirespublic. - "
protectedmeans subclasses only." It also grants access to every class in the same package. For subclass-only access there is no modifier. - "
argsis null when no arguments are given." It is a zero-length array, soargs.lengthis safe without a null check. - "The constructor runs first." Field initialisers and instance blocks run before the constructor body.
- "A class with no modifier is public." It is package-private, invisible outside its package.
Quick recall
Everything you need if you only revisit this box.
public static void main(String[] args)—publicso the JVM can reach it,staticso no object is needed, and the parameter type is fixed even though the name is not.argsis always a real array; an empty command line gives length0.- A
packagedeclaration must be the first statement, and the directory path must match it. - Four access levels:
private, package-private (no keyword),protected,public.protectedincludes the whole package. - Order per object: instance field initialisers → instance blocks → constructor body. Statics run once, before all of it.
- Never call an overridable method from a constructor — the override runs before the subclass fields exist.
Test yourself
Answer these before moving on — recall is what makes it stick.