Command Palette

Search for a command to run...

PHASE 4Beginner ~29 min· topic 6 of 12

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 final field whose value never changes, like Math.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) or List.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.

One copy for everyonediagram
Rendering diagram…

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".

Main.javawhole filejava
class Temperature {
    static double toFahrenheit(double celsius) {
        return celsius * 9 / 5 + 32;
    }
}

double f = Temperature.toFahrenheit(100);   // 212.0

03Constants 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.

Static blocks and when a class is initialiseddiagram
Rendering diagram…

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.

Main.javawhole filejava
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.

terminal
$ javac -Xlint:static Main.java && java Main
── expected output ──
Main.java:5: warning: [static] static method should be qualified by type name, Main, instead of by an expression
m.hello();
^
1 warning
hello

Try it yourself

  1. 1

    Make the counter per object

    In the Ticket example, remove static from issued. Predict what each ticket prints, then run. (Hint: Ticket.issued in main won't compile any more; fix it or delete those lines.)

  2. 2

    Trigger initialisation differently

    In the Config example, change VERSION to static final String VERSION = String.valueOf(1.0);. It's no longer a compile-time constant. Predict where "Config static block runs" now appears.

  3. 3

    Fix the failing initialiser

    In the Settings example, change "80a" to "80". Predict both lines before running.

Code & diagrams

A shared counter gives every object an id New tab
Sign in to run this example in your browser.

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.0
When does a class get initialised? New tab

Reading VERSION didn't initialise Config: javac copied the literal "1.0" into Main.

Sign in to run this example in your browser.

Expected output

main starts
declared a variable
constant: 1.0
Config static block runs
port: 8080
port again: 8080
A failing static initialiser New tab

The second error hides the real cause. In production, search the logs for the first ExceptionInInitializerError.

Sign in to run this example in your browser.

Expected output

1st use: java.lang.ExceptionInInitializerError
2nd use: java.lang.NoClassDefFoundError: Could not initialize class Settings
Static factory methods with names New tab

Integer.valueOf caches -128..127 by default; 1000 may differ if the JVM is run with -XX:AutoBoxCacheMax.

Sign in to run this example in your browser.

Expected output

Rs 12.00
Rs 12.50
zero is cached: true
valueOf(100) same object:  true
valueOf(1000) same object: false

Break 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.

Main.javawhole filejava
Player a = new Player("Asha");
Player b = new Player("Ben");
System.out.println(a.name + " vs " + b.name);
terminal
$ java Main
── what you'll see ──
Ben vs Ben

Break #2

A static initialiser that throws

Write static int PORT = Integer.parseInt("80a"); in the class that holds main.

terminal
$ java Main
── what you'll see ──
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NumberFormatException: For input string: "80a"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:662)
at java.base/java.lang.Integer.parseInt(Integer.java:778)
at Main.<clinit>(Main.java:2)

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.Class mirror 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 int in 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() vs Child.m() is chosen by the compile-time type (Phase 5). This is one reason mocking frameworks struggle with static methods.

Remember this

  1. 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 Class mirror 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. 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. 3

    A static final field holding an immutable value is a constant, conventionally named in UPPER_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. 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. 5

    Classic uses of static: counters and IDs shared by all instances; constants; utility classes like Math and Arrays (all static methods, a private constructor); and static factory methods such as List.of(...) or Integer.valueOf(...) that create objects with a descriptive name.

  6. 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

01

What is the difference between a static field and an instance field? Give an example where each is the right choice.

02

Why can't a static method use this or instance fields directly?

03

When does the JVM initialise a class, and what runs during initialisation?

04

What are the advantages of a static factory method over a constructor?

05

Why is mutable static state considered risky?

Practice

01

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}.

02

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.

03

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 ExceptionInInitializerError followed by NoClassDefFoundError.

  • Done when you can write a utility class and a static factory method.