7.5 Static Members and Class-Level State
Prerequisites: Instance fields, instance methods, encapsulation, and constructors
Estimated time: 25 minutes
You are ready to continue when: You can distinguish between class-level and instance-level members, and you can identify the lifecycle and concurrency risks of shared mutable state
"Has this book been borrowed" belongs to some object; "How many instances of this Book class have been successfully created" belongs to the entire class. The latter can be stored in a static field.
1. static Fields Are Not Reinitialized for Each Instance
final class Book {
private static int totalCreated;
private final String title;
Book(String title) {
if (title == null || title.isBlank()) {
throw new IllegalArgumentException("blank title");
}
this.title = title;
totalCreated++;
}
String title() {
return title;
}
static int totalCreated() {
return totalCreated;
}
}new Book("Java Core technology");
new Book("Understand computer systems deeply");
System.out.println(Book.totalCreated()); // 2Accessing static members via the class name clearly indicates their ownership. Java also allows access to static members through an object expression, but this can lead to the misconception that each object has its own copy, so it should be avoided.
This counts successful creations, not currently live objects. The class is final so it cannot count a subclass whose constructor later fails. The example is for small, single-threaded counts; the int counter can also overflow. totalCreated++ is a combination of read, increment, and write operations, and is not an atomic operation; concurrent object creation by multiple threads can result in lost counts. Production code can use AtomicInteger, LongAdder, locks, or external metrics systems as needed.
Static state is not "only one instance in the entire universe." It is tied to the Class object that defines the class; different class loaders can load the same class name and have independent static states. This boundary is especially important to consider in testing, application servers, and plugin systems.
2. Static Methods Do Not Have a Current Object
class TextRules {
static boolean hasText(String value) {
return value != null && !value.isBlank();
}
}Calling TextRules.hasText(title) does not require first creating TextRules. Static methods do not have this, and cannot directly access instance fields:
class Book {
String title;
static int titleLength(Book book) {
return book.title.length(); // Access via explicit object parameter
}
}Instance methods can access static members because they have a current object and know their class. The reverse is not true unless a static method first obtains an object reference.
3. Constants are Typically static final
class LendingRules {
static final int MAX_LOAN_DAYS = 30;
}Named constants are usually written in uppercase with underscores. final prevents reassignment of the variable; it does not automatically make the referenced object immutable:
static final java.util.List<String> TAGS = new java.util.ArrayList<>();
// TAGS Cannot change to point to another list, but still can TAGS.add(...)Public constants should prioritize basic types, String, enums, or truly immutable types. Do not use public static final as an excuse for sharing mutable containers.
4. Tools Should Only Contain Operations That Do Not Require an Instance
public final class PriceMath {
private PriceMath() {
throw new AssertionError("no instances");
}
public static long addCents(long left, long right) {
return Math.addExact(left, right);
}
}Final prevents inheritance, private constructor prevents external instantiation. If a method requires a large number of replaceable dependencies, configurations, or shared caches, putting them all into a static utility class can make testing and lifecycle management more difficult. In such cases, a regular object is more appropriate.
5. Classic Entry Point for Java 17
The Java 17 launcher looks for the following entry point signature:
public static void main(String[] args) {
// ...
}Since the entry point is static, the launcher does not need to know how to construct the application class in advance. String... args can also be used as an equivalent declaration. Other Java versions or specialized runtime environments may provide additional entry point mechanisms, but this text is based on the Java 17 command-line launcher.
6. Static Initialization Has Lifecycle Costs
Static field initializers and static initialization blocks execute when the class is initialized, running in the order of the source code. Active uses include creating an instance, calling a static method, and reading a static field that is not a compile-time constant. Reading a compile-time constant generally does not initialize its class: the value may already be embedded in the caller’s bytecode. When a published library changes such a constant, recompile callers to ensure they see the new value.
Avoid performing slow network requests, reading potentially failing external configurations, or starting background threads within static initializers. A thrown value that is not an Error is wrapped in ExceptionInInitializerError; an Error propagates directly. The class is marked erroneous, and subsequent active use generally produces NoClassDefFoundError.
Professional Quick Reference
| Member | Requires Receiver | State Ownership | Typical Use |
|---|---|---|---|
| Instance Field | Yes | Each Object | Book Title, Borrow Status |
| Instance Method | Yes | Operable on Current Object | Borrow Book, Return Book |
| static Field | No | Class and Class Loader | Constants, Controlled Shared State |
| static Method | No | No Current Object | Pure Tools, Factory, Entry Point |
Before Moving On
Place the book name and loan status on the instance side, and place the creation count and pure tool operations on the class side. Explain why static methods do not have this. Then check if the static final reference still points to a mutable object, and whether count++ might lose updates in a concurrent environment. Also, explain why different class loaders might each have their own static state.
→ Next: OOP Exercises and Common Pitfalls
Specification: JLS §12.4.2: Steps for Class Initialization.