Topic 4.6
static Fields, Methods and Blocks
In one line
static means "belongs to the class itself, not to any one object". A static field has exactly one copy shared by everyone, a static method runs without an object, and a static block runs once when the class is first initialised.
Think of it like this
A classroom. Each pupil has their own pencil case (an instance field: one per pupil). The classroom has one wall clock that everyone looks at (a static field: one for the whole class). Move the clock's hands and every pupil sees the new time. Asking "what's 7 times 8?" doesn't need any particular pupil; anybody can work it out (a static method).
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- `static`
- A keyword meaning the member belongs to the class itself, not to each object.
- Static field
- A field with one shared copy for the whole class. Also called a class variable.
- Static method
- A method that runs without any object. It has no
this. - Constant
- A
static finalfield whose value never changes, likeMath.PI. Named in UPPER_SNAKE_CASE. - Static initialiser block
- A
static { ... }block that runs once when the class is first initialised. - Class initialisation
- The one-time step where the JVM sets static fields and runs static blocks, triggered the first time the class is actively used.
- Utility class
- A class with only static methods and a private constructor, such as
Math. You never create objects of it. - Static factory method
- A static method that creates and returns an object, for example
Integer.valueOf(5)orList.of(1, 2).
Step by step
01One copy for everyone
Give Ticket an instance field id and a static field issued. Every new Ticket() increments issued and copies its value into its own id.
Three tickets means three id fields (1, 2, 3) but one issued field (3). Reading Ticket.issued needs no ticket at all.
02Static methods: no object needed
A static method is a function attached to a class name. Temperature.toFahrenheit(100) needs no Temperature object; it computes a result from its argument.
Inside a static method, writing an instance field name is an error (non-static variable ... cannot be referenced from a static context): which object's field would it be? If the method needs an object, take it as a parameter.
Rule of thumb: if a method doesn't use any instance state, make it static. It tells the reader "this depends only on its inputs".
class Temperature {
static double toFahrenheit(double celsius) {
return celsius * 9 / 5 + 32;
}
}
double f = Temperature.toFahrenheit(100); // 212.003Constants with static final
static final gives one shared copy that can't be reassigned. That's a constant: static final int MAX_SEATS = 40;.
When the value is a compile-time constant (a primitive or String literal, or an expression made only of them), javac inlines it: classes that use Bus.MAX_SEATS get the number 40 baked into their bytecode. Change it to 50 and recompile only Bus, and those other classes still use 40 until they're recompiled too.
04Static blocks and when a class is initialised
A static { ... } block runs once, during class initialisation, together with static field initialisers, in source order. Use it when a static field needs more than one expression to set up, such as filling a lookup table.
Initialisation is lazy. The JVM initialises a class just before its first active use: new, a static method call, or access to a static field that isn't a compile-time constant. Merely declaring a variable of that type doesn't trigger it.
If a static initialiser throws, the class is marked as failed. The first use throws ExceptionInInitializerError; every later use throws NoClassDefFoundError: Could not initialize class .... That second message confuses people in production, because the real cause was logged earlier.
05Utility classes and static factories
A utility class groups related static methods: Math, Arrays, Collections, Objects. It has a private constructor so nobody can create a pointless instance.
A static factory method is a static method that returns an object. Unlike a constructor it has a name (Point.origin(), Money.ofRupees(5)), can return a cached object instead of a new one (Integer.valueOf(100) returns a cached Integer for values from -128 to 127), and can return a subtype. Effective Java's very first item recommends considering them instead of constructors.
final class MathUtil {
private MathUtil() { } // no instances
static int clamp(int v, int lo, int hi) {
return Math.max(lo, Math.min(hi, v));
}
}06A static call through a reference ignores the object
Java lets you call a static method through a variable: m.hello() where hello is static. The compiler ignores the object and compiles a plain Main.hello() call (invokestatic). It works even when m is null, with no exception.
That's legal but misleading, so javac -Xlint warns: static method should be qualified by type name, Main, instead of by an expression. Always call static members through the class name.
Try it yourself
- 1
Make the counter per object
In the Ticket example, remove
staticfromissued. Predict what each ticket prints, then run. (Hint:Ticket.issuedinmainwon't compile any more; fix it or delete those lines.) - 2
Trigger initialisation differently
In the Config example, change
VERSIONtostatic final String VERSION = String.valueOf(1.0);. It's no longer a compile-time constant. Predict where "Config static block runs" now appears. - 3
Fix the failing initialiser
In the Settings example, change
"80a"to"80". Predict both lines before running.
Code & diagrams
Expected output
issued before: 0
Ticket #1 Asha (of 3)
Ticket #2 Ben (of 3)
Ticket #3 Chen (of 3)
issued after: 3
F of 100 C = 212.0Reading VERSION didn't initialise Config: javac copied the literal "1.0" into Main.
Expected output
main starts
declared a variable
constant: 1.0
Config static block runs
port: 8080
port again: 8080The second error hides the real cause. In production, search the logs for the first ExceptionInInitializerError.
Expected output
1st use: java.lang.ExceptionInInitializerError
2nd use: java.lang.NoClassDefFoundError: Could not initialize class SettingsInteger.valueOf caches -128..127 by default; 1000 may differ if the JVM is run with -XX:AutoBoxCacheMax.
Expected output
Rs 12.00
Rs 12.50
zero is cached: true
valueOf(100) same object: true
valueOf(1000) same object: falseBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
A static field you meant to be per object
Declare static String name; in a Player class, then create two players with different names.
Player a = new Player("Asha");
Player b = new Player("Ben");
System.out.println(a.name + " vs " + b.name);Break #2
A static initialiser that throws
Write static int PORT = Integer.parseInt("80a"); in the class that holds main.
Myth vs fact
Myth
Static methods are faster, so make everything static.
Fact
After JIT compilation, monomorphic instance calls are inlined just like static ones. Choose static for design reasons (no instance state), not speed.
Myth
Static fields are created when the program starts.
Fact
They're created when their class is initialised, on first active use. A class that's never used is never initialised.
Myth
Calling a static method on a null reference throws NullPointerException.
Fact
It doesn't: the compiler uses only the variable's declared type and emits invokestatic. It still compiles with a warning, and it's still bad style.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Class initialisation (JLS 12.4.2) is thread-safe: the JVM takes an initialisation lock, so a static block runs exactly once even if many threads use the class at the same moment. The initialisation-on-demand holder idiom (
private static class Holder { static final X INSTANCE = new X(); }) uses this for lazy, lock-free singletons. - ▸
Since JDK 8, static fields live in the
java.lang.Classmirror object on the heap; the old PermGen space is gone (replaced by native Metaspace for class metadata). Static fields are GC roots as long as their class loader is alive, which is a classic memory leak in app servers: a static cache that only grows. - ▸
Compile-time constants (JLS 15.29) are inlined into using classes, so changing a
public static final intin a library requires recompiling its users. For values that might change between releases, use a static method or a non-constant expression. - ▸
Static methods can't be overridden, only hidden:
Parent.m()vsChild.m()is chosen by the compile-time type (Phase 5). This is one reason mocking frameworks struggle with static methods.
Remember this
- 1
A static field exists once per class, no matter how many objects you create, even zero. It's stored with the class (on HotSpot, in a
Classmirror object on the heap), and it's created when the class is initialised. Every object and every static method sees the same single copy. - 2
A static method has no
this. It can use its parameters, local variables and other static members, but it can't touch instance fields or call instance methods without an explicit object reference. You call it on the class:Math.max(3, 7),Integer.parseInt("42"). - 3
A
static finalfield holding an immutable value is a constant, conventionally named inUPPER_SNAKE_CASE:static final int MAX_USERS = 100;. If the value is a compile-time constant (a literal or a simple expression of literals), javac copies it into every class that uses it. - 4
A static initialiser block (
static { ... }) runs exactly once, when the class is initialised, after static fields above it have been set. The JVM initialises a class lazily: the first time code creates an instance, calls a static method, or reads/writes a non-constant static field. - 5
Classic uses of
static: counters and IDs shared by all instances; constants; utility classes likeMathandArrays(all static methods, aprivateconstructor); and static factory methods such asList.of(...)orInteger.valueOf(...)that create objects with a descriptive name. - 6
Mutable static state is global state: any code can change it, tests interfere with each other, and with threads (Phase 12) it needs synchronisation. Use static for constants and pure functions freely; use mutable static fields rarely and deliberately.
Explain it without notes
What is the difference between a static field and an instance field? Give an example where each is the right choice.
Why can't a static method use this or instance fields directly?
When does the JVM initialise a class, and what runs during initialisation?
What are the advantages of a static factory method over a constructor?
Why is mutable static state considered risky?
Practice
Write a utility class Stats (private constructor) with static methods max(int[] a) and average(int[] a). Use them on {4, 8, 15, 16, 23, 42}.
Write a class User that gives each new user an id from a static counter and has a static method count(). Create three users and print their ids and the count.
Write a class Colors whose static block fills a static String[] NAMES = new String[3] with "red", "green", "blue" and prints Colors ready. In main, print a line, then Colors.NAMES[1], then Colors.NAMES[2].
Trade-offs
- ↔
Static utility methods are simple and need no object, but they can't be swapped for a different implementation in tests or configuration. When behaviour might vary, put it behind an interface and pass an object instead (Phase 5).
- ↔
Static caches and singletons are convenient, but they're global state with lifetime tied to the class loader. Prefer passing dependencies in through constructors; it makes code testable and lifetimes explicit.
- ↔
Inlined compile-time constants are free at run time but make library upgrades fragile if the value ever changes. Keep truly fixed values as constants and expose changeable ones through methods.
Done when you can
Done when you can explain one-copy-per-class vs one-copy-per-object.
Done when you can write static methods and know what they can and can't access.
Done when you can predict when a class is initialised, including the constant-inlining exception.
Done when you can explain
ExceptionInInitializerErrorfollowed byNoClassDefFoundError.Done when you can write a utility class and a static factory method.