Every field you have written so far has belonged to an object. Each BankAccount carries its own balance, each Person its own name. But some data should not be duplicated: a count of every object ever created, a shared configuration flag, or the value of pi. Java provides the static keyword for exactly this — a field or method that belongs to the class itself, not to any individual instance. Understanding static is essential for counting, shared caches, constants, and the utility classes that fill every Java standard library.
flowchart LR O1["obj1.value = 10"] O2["obj2.value = 20"] S["shared count = 2"] O1 -.->|"separate"| O2 O1 -->|"shares"| S O2 -->|"shares"| S style S fill:#c2410c,color:#fff
static fields: one copy for every instance
When you mark a field static, the JVM creates exactly one storage location for it, regardless of how many objects you build. Every instance reads and writes that same location, which makes static perfect for counters, shared registries, or global defaults. It is also dangerous if misused: a mutable static field is essentially a global variable, and global variables make programs hard to test and debug because any code anywhere can change them.
Access a static field through the class name — Visitor.count — rather than through an instance. Writing v.count compiles, but it misleads the reader into thinking the field varies per object. Good Java style reserves the class-name form for static members so the code's intent is obvious at a glance.
public class Main {
static class Visitor {
static int count = 0;
String name;
Visitor(String name) {
this.name = name;
count++;
}
}
public static void main(String[] args) {
Visitor a = new Visitor("A");
Visitor b = new Visitor("B");
System.out.println(Visitor.count);
System.out.println(a.count);
}
}
static methods and constants
A static method also belongs to the class, not to any object. It cannot access instance fields or call instance methods directly, because there is no this — no particular object is in play. Math.max(3, 7) is the classic example: it needs two numbers and returns one; it has no hidden state to consult.
A static final field is a constant. The final keyword prevents reassignment, and static shares the single value across the program. By convention, constant names are UPPER_SNAKE_CASE. The compiler may even inline the value at compile time, so changing a constant requires recompiling everything that uses it.
flowchart TD A["MathUtils"] --> B["private constructor"] A --> C["static int max(int, int)"] A --> D["static int min(int, int)"] E["caller"] -->|"MathUtils.max(3,7)"| C style B fill:#b45309,color:#fff
public class Main {
static final class Limits {
static final int MAX = 100;
private Limits() {}
static int clamp(int value) {
if (value > MAX) {
return MAX;
}
return value;
}
}
public static void main(String[] args) {
System.out.println(Limits.MAX);
System.out.println(Limits.clamp(150));
System.out.println(Limits.clamp(50));
}
}
The static context trap
Because a static method has no instance, it has no this. Any attempt to refer to an instance field or call an instance method from a static context is a compile error. The compiler is not being pedantic — it literally has no object to read from. The fix is either to make the field or method static too, or to create an object and ask it. Beginners often trip over this inside main, which is static: you cannot simply call greet() from main unless greet is also static, because main has no implicit object.
Shared static state. Predict the exact two-line output.
public class Main {
static class Visitor {
static int count = 0;
String name;
Visitor(String name) {
this.name = name;
count++;
}
}
public static void main(String[] args) {
Visitor a = new Visitor("A");
Visitor b = new Visitor("B");
System.out.println(Visitor.count);
System.out.println(a.count);
}
}
count is static, so both objects share it.
Each constructor call increments the same counter.
a.count compiles but reads the shared value, which is 2.
Static method call. Predict the exact two-line output.
public class Main {
static class Calculator {
static int square(int n) {
return n * n;
}
}
public static void main(String[] args) {
System.out.println(Calculator.square(4));
System.out.println(Calculator.square(5));
}
}
square(4) returns 4 * 4.
square(5) returns 5 * 5.
You write static void show() { System.out.println(this.name); } inside a class. What happens?
Static methods belong to the class, not any instance, so there is no this. The compiler rejects this immediately.
Constants and static helpers. Predict the exact three-line output.
public class Main {
static class Limits {
static final int MAX = 100;
static int clamp(int value) {
if (value > MAX) {
return MAX;
}
return value;
}
}
public static void main(String[] args) {
System.out.println(Limits.MAX);
System.out.println(Limits.clamp(150));
System.out.println(Limits.clamp(50));
}
}
MAX is 100 and is printed first.
clamp(150) exceeds MAX, so it returns 100.
clamp(50) is within range, so it returns 50.
You want a class MathUtils with methods like max(int, int) that nobody should ever instantiate. Which design achieves this?
A private constructor prevents instantiation. Marking the class final prevents subclassing that might expose a constructor. Abstract does not prevent instantiation of concrete subclasses. A public constructor allows new MathUtils(). An interface is not the right tool for a concrete utility class.
Recap
staticfields belong to the class, not to any instance. Every object shares the same value.staticmethods have nothisand cannot access instance fields directly. Use them for pure helpers and factory logic.static finaldefines a constant. Name itUPPER_SNAKE_CASEand treat it as immutable.- A utility class has a
privateconstructor and only static members, so it can never be instantiated. - Mutable static fields are global variables in disguise: convenient at first, but they make testing and reasoning about code far harder.
Next you will learn about the contracts every object must honour — equals, hashCode, and toString — and why breaking them silently corrupts collections.