Module 5 · Object-Oriented Fundamentals ⏱ 18 min

static Members, Constants, Utility Classes

By the end of this lesson you will be able to:
  • Explain the difference between instance and static fields
  • Call static methods correctly and predict shared state across instances
  • Design a utility class that cannot be instantiated

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
Instance fields live inside each object; a static field lives once in the class.

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.

Main.java — a static counter tracks how many Visitor objects have been created. Real, compiling Java.
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
A utility class groups static methods under a private constructor so it is never instantiated.
Main.java — a utility class with a private constructor and static helpers. Real, compiling Java.
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.

Exercise

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);
    }
}
Exercise

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));
    }
}
Exercise

You write static void show() { System.out.println(this.name); } inside a class. What happens?

Exercise

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));
    }
}
Exercise

You want a class MathUtils with methods like max(int, int) that nobody should ever instantiate. Which design achieves this?

Recap

  • static fields belong to the class, not to any instance. Every object shares the same value.
  • static methods have no this and cannot access instance fields directly. Use them for pure helpers and factory logic.
  • static final defines a constant. Name it UPPER_SNAKE_CASE and treat it as immutable.
  • A utility class has a private constructor 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.

Checkpoint quiz

What does static mean when applied to a field?

Why can a static method not use this?

Go deeper — technical resources