Object-Oriented Programming Basics

Static Fields & Methods

15 min Lesson 6 of 14

Static Fields & Methods

Every field and method you have written so far belongs to a specific instance — each BankAccount object has its own balance, and calling deposit() on one object does not touch another. Sometimes, however, you need data or behaviour that belongs to the class itself, shared equally by every instance. That is exactly what the static keyword gives you.

Instance Members vs. Class Members

The difference is simple but important:

  • Instance member — each object has its own copy. Accessing it requires an object reference (account.balance).
  • Static (class) member — one copy shared by the whole class. Accessed through the class name (BankAccount.totalAccounts).

Think of it this way: a Car class might track how many cars have ever been manufactured. That count does not belong to one car — it belongs to the concept of Car itself.

Static Fields

Declare a static field with the static keyword before the type:

public class BankAccount { // static field — one copy, shared by every BankAccount object private static int totalAccounts = 0; // instance field — each object has its own private double balance; public BankAccount(double initialBalance) { this.balance = initialBalance; totalAccounts++; // increment the shared counter on every new account } public static int getTotalAccounts() { return totalAccounts; } }
public class Main { public static void main(String[] args) { System.out.println(BankAccount.getTotalAccounts()); // 0 BankAccount a1 = new BankAccount(500.0); BankAccount a2 = new BankAccount(1200.0); System.out.println(BankAccount.getTotalAccounts()); // 2 } }

No matter how many BankAccount objects exist, totalAccounts is the same single integer in memory. Creating a new object increments it for everyone.

Static Methods

A static method is called on the class, not on an instance. Because no object is involved, a static method cannot access instance fields or call instance methods — there is no this reference.

public class MathHelper { // pure utility — no state, no instance needed public static double circleArea(double radius) { return Math.PI * radius * radius; } public static int clamp(int value, int min, int max) { if (value < min) return min; if (value > max) return max; return value; } }
public class Main { public static void main(String[] args) { double area = MathHelper.circleArea(5.0); System.out.println(area); // 78.53981633974483 int clamped = MathHelper.clamp(150, 0, 100); System.out.println(clamped); // 100 } }

You have already used static methods throughout Java: Math.sqrt(), Integer.parseInt(), and the entry point public static void main(String[] args) are all static.

Why is main static? The JVM needs to call main without creating an object first. Static lets the runtime call a method directly on the class — no constructor required.

Constants with static final

A constant is a value that never changes. In Java, constants are declared static final and named in UPPER_SNAKE_CASE by convention:

public class PhysicsConstants { public static final double SPEED_OF_LIGHT = 299_792_458.0; // metres per second public static final double GRAVITATIONAL_CONSTANT = 6.674e-11; }
public class Circle { public static final double PI = 3.14159265358979; private double radius; public Circle(double radius) { this.radius = radius; } public double area() { return PI * radius * radius; // uses the class constant } }
  • static — one copy for the class, no memory wasted per object.
  • final — the value cannot be reassigned after initialisation.
Prefer constants over magic numbers. Writing Circle.PI tells the reader what the value means. Writing 3.14159 scattered across dozens of methods is a maintenance hazard — if you ever need to change precision, you have to find every occurrence.

Static Initializer Blocks

Sometimes a static field needs more than a simple assignment to initialise — for example, building a lookup table. Use a static initialiser block:

import java.util.HashMap; import java.util.Map; public class HttpStatus { public static final Map<Integer, String> CODES; static { CODES = new HashMap<>(); CODES.put(200, "OK"); CODES.put(404, "Not Found"); CODES.put(500, "Internal Server Error"); } }
public class Main { public static void main(String[] args) { System.out.println(HttpStatus.CODES.get(404)); // Not Found } }

The block runs once when the class is first loaded by the JVM — before any object is created.

Common Pitfalls

Static methods cannot use this or instance fields. If you call an instance method from a static context, the compiler immediately flags it: "non-static method cannot be referenced from a static context". This is one of the most common beginner errors in Java.
Accessing a static member through an object reference compiles but is misleading. Writing a1.getTotalAccounts() looks like it calls something on a1, but it reads the shared class value. Always use the class name (BankAccount.getTotalAccounts()) to make intent obvious.

When to Use Static

  • Utility / helper methods — pure calculations with no state (e.g., Math.abs()).
  • Constants — values that are the same for every user of the class.
  • Class-level counters or caches — data that belongs to the type, not an instance.
  • Factory methods — static methods that construct and return objects (covered later).

Avoid making everything static just to avoid creating objects. Overusing static undermines OOP — it breaks encapsulation, makes testing harder, and introduces hidden shared state that can cause subtle bugs.

Summary

Static fields and methods belong to the class, not to individual objects. Use static for shared counters, utility functions, and constants (static final). Static initialiser blocks set up complex static state when the class loads. Keep static use focused — reach for it when the concept genuinely belongs to the type, not when you just want to skip creating an instance.