static Keyword
The static keyword marks a member as belonging to the class itself rather than to individual objects. A static field has one copy shared by the whole program; a static method can be called without creating any object. static can be applied to fields, methods, initializer blocks, nested classes, and imports. Knowing when something should be static, and when it must not be, is a core design skill.
Instance Members Versus Static Members
public class Visitor {
private static int totalVisitors = 0; // one copy, shared by all Visitor objects
private final String name; // one copy per object
public Visitor(String name) {
this.name = name;
totalVisitors++;
}
public String getName() { return name; }
public static int getTotalVisitors() { return totalVisitors; }
}
new Visitor("Ada");
new Visitor("Linus");
new Visitor("Grace");
System.out.println(Visitor.getTotalVisitors()); // 3
Class Visitor (loaded once)
┌──────────────────────────┐
│ static totalVisitors = 3 │◄──── shared by all instances
└──────────────────────────┘
Objects:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ name="Ada" │ │ name="Linus" │ │ name="Grace" │
└──────────────┘ └──────────────┘ └──────────────┘
Each Visitor has its own name. There is a single totalVisitors, stored with the class, and every constructor call increments the same variable.
| Instance member | Static member | |
|---|---|---|
| Belongs to | Each object | The class |
| Copies | One per object | One in total |
| Accessed via | An object reference: v.getName() | The class name: Visitor.getTotalVisitors() |
| Exists when | The object is created | The class is loaded |
Can use this | Yes | No |
Static Fields
A static field holds data shared by every instance, or data that belongs to the type rather than to any object.
Shared counters and state
public class Ticket {
private static int nextId = 1;
private final int id;
public Ticket() {
this.id = nextId++;
}
public int getId() { return id; }
}
Ticket a = new Ticket();
Ticket b = new Ticket();
System.out.println(a.getId() + ", " + b.getId()); // 1, 2
Each ticket receives a unique ID from the shared counter. (In multithreaded code this counter would need synchronization or an AtomicInteger, because two threads could read the same value; this is covered in the multithreading section.)
Constants: static final
The most common static fields are constants: values that never change and belong to the class.
public class Tax {
public static final double GST_RATE = 0.18;
public static final int MAX_LINE_ITEMS = 100;
public static final String CURRENCY = "INR";
}
double gst = amount * Tax.GST_RATE;
static provides one shared copy; final prevents reassignment. Constants are named in UPPER_SNAKE_CASE. Math.PI, Integer.MAX_VALUE, and System.out are static fields of JDK classes.
For a static final field holding a mutable object, final only protects the reference:
public static final List<String> ROLES = new ArrayList<>();
ROLES.add("hacker"); // allowed: the list itself is mutable
public static final List<String> ROLES = List.of("ADMIN", "EDITOR", "VIEWER"); // truly constant
Use immutable collections for constants that are collections.
Static Methods
A static method does not operate on an object. It is called using the class name:
public class Validators {
public static boolean isValidPin(String pin) {
return pin != null && pin.matches("\\d{6}");
}
public static boolean isValidPhone(String phone) {
return phone != null && phone.matches("[6-9]\\d{9}");
}
}
if (Validators.isValidPin("600042")) { ... }
Typical uses:
| Use | Examples |
|---|---|
| Pure functions of their arguments | Math.sqrt, Integer.parseInt, String.valueOf |
| Factory methods | List.of, LocalDate.of, Path.of, Optional.empty |
| Utility classes | Arrays, Collections, Objects, Files |
| Program entry point | public static void main(String[] args) |
main is static because the JVM must call it before any object of your class exists.
Restrictions
A static method has no current object, so it cannot directly access instance fields, call instance methods, or use this:
public class Counter {
private int count;
public static void main(String[] args) {
count++; // compile error: non-static variable count cannot be
// referenced from a static context
increment(); // compile error: non-static method
Counter c = new Counter();
c.count++; // fine: accessed through a specific object
}
void increment() { count++; }
}
The reverse is always allowed: instance methods can read static fields and call static methods freely, since the class's static members are available everywhere in the class.
Static methods cannot be overridden
Static methods are resolved at compile time from the declared type, not at runtime from the object. A subclass that declares a static method with the same signature hides the parent's method rather than overriding it:
class Parent { static String who() { return "parent"; } }
class Child extends Parent { static String who() { return "child"; } }
Parent p = new Child();
System.out.println(p.who()); // "parent": decided by the declared type
Calling a static method through an instance reference compiles (with a warning) but is misleading for exactly this reason. Always call static methods through the class name: Parent.who().
Static Initializer Blocks
A static block runs once, when the class is first loaded, and is used for static setup that does not fit in a single expression:
import java.util.HashMap;
import java.util.Map;
public class CountryCodes {
private static final Map<String, String> CODES;
static {
Map<String, String> m = new HashMap<>();
m.put("IN", "India");
m.put("US", "United States");
m.put("JP", "Japan");
CODES = Map.copyOf(m); // store an unmodifiable copy
System.out.println("CountryCodes loaded");
}
public static String name(String code) {
return CODES.getOrDefault(code, "Unknown");
}
}
System.out.println(CountryCodes.name("IN"));
System.out.println(CountryCodes.name("JP"));
Output:
CountryCodes loaded
India
Japan
The message prints once, before the first use of the class. The class is initialized lazily: if a program never uses CountryCodes, its static block never runs. Static fields and blocks are processed in the order they appear in the source.
If a static initializer throws an exception, the class fails to initialize and every later attempt to use it throws NoClassDefFoundError (after an initial ExceptionInInitializerError). Keep static initialization simple and unlikely to fail.
Static Nested Classes
A class declared inside another with static is a static nested class. It does not hold a reference to an instance of the outer class, so it can be created on its own:
public class Order {
private final List<Line> lines = new ArrayList<>();
public static class Line {
private final String product;
private final int qty;
public Line(String product, int qty) {
this.product = product;
this.qty = qty;
}
}
public void add(Line line) { lines.add(line); }
}
Order.Line line = new Order.Line("Pen", 3); // no Order object needed
Use a static nested class when the nested type is closely related to the outer one but does not need access to a particular outer instance; builders and small helper types are typical. A non-static (inner) class keeps a hidden reference to its outer object, which uses memory and can prevent the outer object from being garbage collected. Make nested classes static unless they genuinely need the outer instance. Nested and inner classes have their own lessons.
Static Imports
import static lets you use static members without the class name:
import static java.lang.Math.PI;
import static java.lang.Math.pow;
double area = PI * pow(radius, 2);
It is most useful in tests (assertEquals, assertTrue) and for heavily used constants. Overuse makes it unclear where names come from.
When to Use static
Make a member static when:
- It represents data shared across all instances (a counter, a cache, a configuration constant).
- A method depends only on its parameters, not on any object's state.
- A method creates instances (factory method).
- A nested class does not need the outer instance.
Avoid static when:
- The data differs from object to object. A
static String namein aStudentclass would give every student the same name. - You need polymorphism. Static methods cannot be overridden, so behavior that varies by subtype must be in instance methods.
- The state is mutable and shared. Mutable static fields are effectively global variables: any code can change them, which makes behavior depend on hidden history, causes problems in multithreaded code, and makes tests interfere with each other.
// Problematic: hidden global state
public class Cart {
private static List<String> items = new ArrayList<>(); // every Cart shares one list!
}
// Correct: each cart owns its items
public class Cart {
private final List<String> items = new ArrayList<>();
}
The first version is a common beginner mistake: adding an item to one customer's cart adds it to everyone's.
Utility Classes
A class containing only static members should not be instantiated. Make it final and give it a private constructor:
public final class PriceUtils {
private PriceUtils() {} // prevents new PriceUtils()
public static double withGst(double amount) {
return amount * 1.18;
}
public static String format(double amount) {
return String.format("₹%,.2f", amount);
}
}
System.out.println(PriceUtils.format(PriceUtils.withGst(12500))); // ₹14,750.00
java.util.Collections, java.util.Arrays, java.util.Objects, and java.lang.Math follow this pattern.
Practical Example: Configuration and Instances
import java.util.ArrayList;
import java.util.List;
public class Student {
public static final int MAX_COURSES = 5; // constant
private static int enrolledCount = 0; // shared statistic
private final String name; // per-student data
private final List<String> courses = new ArrayList<>();
private Student(String name) {
this.name = name;
enrolledCount++;
}
public static Student register(String name) { // static factory
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("name required");
}
return new Student(name.strip());
}
public boolean join(String course) { // instance behavior
if (courses.size() >= MAX_COURSES || courses.contains(course)) {
return false;
}
return courses.add(course);
}
public static int getEnrolledCount() {
return enrolledCount;
}
@Override
public String toString() {
return name + " " + courses;
}
public static void main(String[] args) {
Student a = Student.register("Ada");
Student b = Student.register(" Linus ");
a.join("Java");
a.join("Docker");
b.join("C");
System.out.println(a);
System.out.println(b);
System.out.println("Students: " + Student.getEnrolledCount());
System.out.println("Course limit: " + Student.MAX_COURSES);
}
}
Output:
Ada [Java, Docker]
Linus [C]
Students: 2
Course limit: 5
MAX_COURSES is a rule that applies to every student, so it is a static constant. enrolledCount is a statistic about the class as a whole, so it is a static field updated in the constructor. name and courses differ for each student, so they are instance fields. register is a static factory because it creates students; join is an instance method because it changes one particular student's courses. Each member's placement follows directly from the question: does this belong to one student, or to students in general?