Classes And Objects

static vs Instance

static members belong to the class, as one shared copy, while instance members belong to each object. A static method has no this, so it can't touch instance fields. static final makes shared constants, and a static block sets up class state once, lazily, when the class is first used. Every distinction is shown with verified output.

Suggest an edit

static vs Instance — the Class vs the Object

You've used static since main, and you met it again on helper methods. Now it earns a precise definition. A static member belongs to the class itself, so there's exactly one copy, shared by everything. An instance member belongs to each object, with a fresh copy for every new.

That one distinction decides where a value lives, who can see it, and what a method may touch. A static method has no object, so it has no this, so it can't reach instance fields. The same keyword also gives you class-wide constants (static final) and one-time class setup (a static block).

💡 The core idea.

  • A static member belongs to the class — one shared copy — while an instance member belongs to each object.
  • A static method has no this, so it cannot touch instance fields.
  • The same keyword gives class-wide constants (static final) and one-time class setup.

This is the deep pass of the static you've used on methods, now set against the instance state of objects. Every output below was produced by compiling and running the code.

You'll be able to: predict the value of a static field after several objects are built, and name which fields the objects share; fix non-static variable … cannot be referenced from a static context, and decide whether a method should be static; write a static final constant, and explain why reading it may leave its class uninitialized; predict the order in which static initializers, instance initializers and constructor bodies run.

📘 How to read the Intuition boxes. Each one is built in three moves:

  1. The mechanism — what the compiler and the JVM do.
  2. A concrete bite — a specific, runnable failure (often a real compiler error), shown so the trap is visible.
  3. The earned rule — the decision heuristic, now justified rather than asserted, plus its cost.

Table of contents

  1. static fields are shared
  2. static vs instance methods
  3. static final constants
  4. static initialization blocks
  5. Instance initializers and the order of construction
  6. Mental-model summary
  7. Gotcha checklist
  8. Check yourself
  9. Sources

1. static fields are shared

A static field is stored on the class, so every object sees the same one. An instance field is stored on each object, so each object has its own. Here count is shared (how many widgets exist), and id belongs to each widget:

Output:

ids: 1 2 3
count: 3
class Widgetstatic count = 3 (one shared copy)a : Widgetid = 1b : Widgetid = 2c : Widgetid = 3

Analysis. Each constructor incremented the one shared count (1, 2, 3) and copied it into its own id. So the three widgets have distinct ids 1, 2, 3, while count is a single value, 3, that you reach through the class as Widget.count. The diagram shows three objects, each with its own id, all pointing at one class that holds the single count.

Intuition. Mechanism. A static field has exactly one copy, however many objects exist, even zero [1]. It lives with the class. An instance field is created with each object, on that object. Widget.count names the class's copy, and a.id names that object's copy.

Concrete bite. Because the static field is shared, a change through any path shows up for everyone. count reached 3 because three different constructors all incremented the same variable. That's both the feature (a class-wide tally) and the hazard (shared mutable state that any instance can change).

Non-example: a static field read through an object. Java lets you write a.count, and it looks like a's own field. It isn't:

Output:

2 2 2

a.count, b.count and Widget.count are all one variable. Compile with javac -Xlint:static, and javac warns: static variable should be qualified by type name, Widget, instead of by an expression. Write Widget.count, so the reader can see it's shared.

💡 Earned rule. Use a static field for state that belongs to the class as a whole (a counter, a shared cache, a registry). Use an instance field for anything that differs per object.

static mutable state costs you through its sharing, because it acts as a global variable. Changes from anywhere show up everywhere, which makes it hard to reason about. It's also unsafe when several threads change it at once, a theme that comes back in Concurrency: the Basics.


2. static vs instance methods

An instance method runs on an object, so it reaches that object's fields through the implicit this. A static method runs on the class. There's no object, no this, and so no instance fields to reach.

Output:

id 2 of 2
2

Analysis. describe() is an instance method called on w, so it sees both w.id (2) and the shared count (2). total() is static, called as Widget.total() with no object. It can read count because count is also static, but it has no id to read. Instance methods can touch everything, while static methods can touch only static members, unless someone hands them an object.

Intuition. Mechanism. An instance method receives a hidden this, and an unqualified field name resolves against it. A static method has no this [2]. A bare instance field name has nothing to resolve against, so the compiler rejects it.

Concrete bite. Reading an instance field from a static method is a compile error:

Compiler error:

Main.java:4: error: non-static variable id cannot be referenced from a static context
        System.out.println(id);
                           ^
1 error

show() is static, so there's no this, and no particular widget whose id it could print. Writing this.id doesn't help either. javac says non-static variable this cannot be referenced from a static context. This is the rule that stopped Rectangle.area() in Classes & Objects: instance state needs an instance. A static method can read w.id when it gets a Widget w as a parameter.

💡 Earned rule. Make a method static when it doesn't depend on any one object's state, like a utility that works purely from its arguments. Make it an instance method when it operates on this object's fields.

Guess wrong, and you get a compile error the moment a static method reaches for instance state. The payoff is a signature that tells the truth: a static method visibly needs no object, so you can call it without making one.


3. static final constants

Combine static (one shared copy) with final (assigned once, never changed), and you get a constant: a single, unchangeable, class-wide value. By convention, constants are named in UPPER_SNAKE_CASE.

Output:

12.56636
3.14159

Analysis. PI is one shared value, since there's no point giving every circle its own copy of a constant. final means nothing can change it. area() reads it like any field, and Circle.PI reads it through the class. (The JDK already has a more precise one, Math.PI.)

Intuition. Mechanism. static gives one copy, and final forbids reassignment after its single initialization. Some of these fields are special. Take a static final field of a primitive type or String, set to a compile-time constant such as 100. That's a constant variable [3]. javac copies its value into every place that reads it [4]. §4 shows a consequence.

Concrete bite. Try to reassign a static final, and javac rejects it, just as it does an instance final:

Compiler error:

Main.java:3: error: cannot assign a value to static final variable MAX
    static void raise() { MAX = 200; }
                          ^
1 error

MAX is static final, set once at declaration, so raise()'s attempt to change it won't compile. No method can move a constant.

💡 Earned rule. Use static final for fixed, shared values: limits, conversion factors, configuration that never changes at run time. Name them in UPPER_SNAKE_CASE so readers know they're constants.

For values that are truly fixed, this costs you nothing. The discipline is saving it for things that never vary. A static final that you later need to vary forces a redesign.


4. static initialization blocks

You can set a field with a simple value inline. When a class's static state needs computation to set up, a static block, static { … }, does that work once, when the class is initialized [5].

Output:

main start
static block ran
9
16

Analysis. Look at the order: main start printed first, and only then static block ran. The Lookup class isn't initialized until it's first used, the moment main reaches Lookup.SQUARES.

At that point, the static block ran once and filled SQUARES (0, 1, 4, 9, 16). The reads that followed returned 9 and 16. Think of a static block as the constructor for class-level state.

Intuition. Mechanism. A class is initialized lazily, immediately before the first of these [6]:

  • an object of the class is created;
  • a static method of the class is called;
  • a static field of the class is assigned;
  • a static field of the class is read, and it is not a constant variable (§3).

Initialization runs the static field initializers and static blocks in source order, exactly once per class [7]. Object construction happens per object, while class initialization happens once per class. The class that holds main is initialized before main starts [8], so a static block in Main prints before main's first line.

Concrete bite. The single static block ran line proves the "once". main touched Lookup twice (SQUARES[3] and SQUARES[4]), yet the block ran only one time.

The last rule in the list has a visible edge. Reading a constant variable doesn't initialize its class, because javac already copied the value into the reader:

Output:

100
after MAX
Config initialized
0

Config.MAX printed 100 with no initialization, because the 100 was already compiled into Main. Config.calls isn't a constant, so reading it initialized Config, and the block printed first.

Non-example: a static block that throws. A static block runs where the class is first used, so its failure lands there too, wrapped in a new error:

Output (prints start and filling, then a thrown exception):

start
filling
Exception in thread "main" java.lang.ExceptionInInitializerError
	at Main.main(Main.java:12)
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 2
	at Table.<clinit>(Main.java:5)

The real fault is the Caused by line: index 5 in an array of length 2. The JVM reports it as an ExceptionInInitializerError at the line in main that first used Table [7]. <clinit> is the JVM's name for a class's combined static initialization code [9].

The JVM now marks the class as failed. Any later use of it throws NoClassDefFoundError: Could not initialize class Table, and the block never runs again.

💡 Earned rule. Use a static block for class-level setup that needs more than a literal, such as building a lookup table, validating configuration, or registering something. You can rely on its run-once, lazy-on-first-use timing.

The catch is that the moment it runs is subtle: first use, not program start. A static block with side effects or ordering dependencies can surprise you, and a failure inside it breaks the whole class. Keep them simple.


5. Instance initializers and the order of construction

The static block has a per-object twin. An instance initializer is a bare block, { … }, in the class body, and it runs every time an object is created [10]. This program logs every step of the class and object setup:

Output:

-- first new
static field initializer
static block
instance field initializer
instance block
constructor body
-- second new
instance field initializer
instance block
constructor body

Analysis. The first new Order() triggered class initialization: the static field initializer, then the static block, in source order. Then it built the object: the instance field initializer, then the instance block, again in source order, and finally the constructor body. The second new skipped the static part, since the class was already initialized, and repeated only the three instance steps.

Intuition. Mechanism. new works in a fixed order [11]:

  1. Initialize the class, if this is its first use: static initializers and static blocks, top to bottom, once.
  2. Allocate the object; every field holds its default (0, false, null).
  3. Run the instance field initializers and instance blocks, top to bottom.
  4. Run the rest of the constructor body.

Both "top to bottom" steps follow textual order: the order of the lines in the file.

Non-example: reading a field before its declaration. Because static initializers run top to bottom, javac rejects an initializer that reads a field declared further down [12]:

Compiler error:

Main.java:2: error: illegal forward reference
    static int a = b + 1;
                   ^
1 error

When a's initializer runs, b hasn't been set to 5 yet. Swap the two lines, and a becomes 6.

💡 Earned rule. Put object setup in the constructor. Reach for an instance block only for code that every constructor must share, and read a class's initializers top to bottom, the way the JVM runs them.

Spread initializers across a class, and you pay with an order that's textual, not logical. Move a line, and you change what runs first. One constructor that does the setup keeps the order in one place.


6. Mental-model summary

Principle Consequence
A static member belongs to the class; an instance member to each object One shared count vs a per-object id; reach statics via ClassName.x
a.count on a static field still names the one shared field a.count, b.count and Widget.count are the same variable
A static method has no this It cannot read instance fields, unless it is handed an object
static final is a shared, unchangeable constant Reassigning it won't compile; name it UPPER_SNAKE_CASE
A primitive or String static final with a constant value is copied into its readers Reading it does not initialize its class
A class initializes once, on first use: new, a static call, or a non-constant static field static blocks run then, in source order; a throw there is ExceptionInInitializerError
new runs instance initializers and instance blocks in source order, then the constructor body Initializer order is textual; reading a later static field is an illegal forward reference
Shared mutable static state acts as a global variable Convenient as a tally or cache, but hard to reason about and unsafe under threads

7. Gotcha checklist

Symptom Likely cause Fix
non-static variable id cannot be referenced from a static context a static method (or main) used an instance field make the method non-static, or pass it an object and read w.id
non-static variable this cannot be referenced from a static context this inside a static method same fix: a static method has no current object
non-static method area() cannot be referenced from a static context ClassName.method() on an instance method call it on an object
A counter or cache behaves "globally" across objects it's a static field, shared by all instances use an instance field for per-object state
a.count changed when you only touched b count is static; the object prefix is misleading write Widget.count; -Xlint:static flags the other form
cannot assign a value to static final variable MAX code tried to change a constant set it once; redesign if it must vary
A static block runs later than expected, or not at all classes initialize on first use; a constant read does not count don't rely on a static block's side effects happening early
ExceptionInInitializerError, Caused by: … a static initializer or block threw fix the Caused by line; later uses throw NoClassDefFoundError
illegal forward reference an initializer reads a field declared below it reorder the declarations

✅ Check yourself

One check per objective. Answer before you open anything.

A class has, in this order: a static block printing S, an instance block printing I, and a constructor printing C. main calls new on it twice. What prints? And would static block ran print if main never mentioned Lookup?

S, I, C, then I, C. The class initializes once, on the first new. Each object then runs its instance block, then its constructor body. The §5 proof shows the same order with field initializers added.

No. Lookup is initialized only when it's first used. A class that main never touches is never initialized, so its static block never runs.


📚 Sources

  1. The Java Language Specification, Java SE 25, §8.3.1.1 "static Fields" ("exactly one incarnation of the field, no matter how many instances") — https://docs.oracle.com/javase/specs/jls/se25/html/jls-8.html#jls-8.3.1.1
  2. The Java Language Specification, Java SE 25, §8.4.3.2 "static Methods" — https://docs.oracle.com/javase/specs/jls/se25/html/jls-8.html#jls-8.4.3.2
  3. The Java Language Specification, Java SE 25, §4.12.4 "final Variables" (constant variable) — https://docs.oracle.com/javase/specs/jls/se25/html/jls-4.html#jls-4.12.4
  4. The Java Language Specification, Java SE 25, §13.1 "The Form of a Binary" (a constant variable "must be resolved at compile time") — https://docs.oracle.com/javase/specs/jls/se25/html/jls-13.html#jls-13.1
  5. The Java Language Specification, Java SE 25, §8.7 "Static Initializers" — https://docs.oracle.com/javase/specs/jls/se25/html/jls-8.html#jls-8.7
  6. The Java Language Specification, Java SE 25, §12.4.1 "When Initialization Occurs" — https://docs.oracle.com/javase/specs/jls/se25/html/jls-12.html#jls-12.4.1
  7. The Java Language Specification, Java SE 25, §12.4.2 "Detailed Initialization Procedure" (initializers "in textual order"; ExceptionInInitializerError) — https://docs.oracle.com/javase/specs/jls/se25/html/jls-12.html#jls-12.4.2
  8. The Java Language Specification, Java SE 25, §12.1.3 "Initialize the Initial Class or Interface: Execute Initializers" — https://docs.oracle.com/javase/specs/jls/se25/html/jls-12.html#jls-12.1.3
  9. The Java Virtual Machine Specification, Java SE 25, §2.9.2 "Class Initialization Methods" — https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-2.html#jvms-2.9.2
  10. The Java Language Specification, Java SE 25, §8.6 "Instance Initializers" — https://docs.oracle.com/javase/specs/jls/se25/html/jls-8.html#jls-8.6
  11. The Java Language Specification, Java SE 25, §12.5 "Creation of New Class Instances" — https://docs.oracle.com/javase/specs/jls/se25/html/jls-12.html#jls-12.5
  12. The Java Language Specification, Java SE 25, §8.3.3 "Restrictions on Field References in Initializers" — https://docs.oracle.com/javase/specs/jls/se25/html/jls-8.html#jls-8.3.3

🧪 Predict, then check.

  1. Predict the output of new Widget(); new Widget(); System.out.println(Widget.count); for the §1 class.
  2. Decide whether a method static double areaOf(double r) (using only r and PI) would compile inside Circle, and whether static double area() (using the instance field radius) would.
  3. Predict whether static block ran would print if main never mentioned Lookup at all, and explain why.

Your Turn

Before you move on, check your understanding with the coach — explain the idea, apply it, weigh the trade-offs, then defend your reasoning.

Mark as readBook versionfd26460