Static and Utility Classes
What belongs to the class itself rather than to any instance, and when a stateless utility class is a reasonable choice
So far, every field and every method we've written has belonged to
individual objects. Each Dog has its own name. Each
BankAccount has its own balance. We send messages to specific
instances.
But sometimes a piece of data or a piece of behavior doesn't belong
to any one instance — it belongs to the class itself. Java models
that with the keyword static.
static: belongs to the class, not to instances
A static member exists once per class, no matter how many
instances you create. Every instance shares the same static field
and the same static method.
Notice the difference in how you call the two methods. a.id() is
sent to a specific widget. Widget.totalCreated() is sent to the
class. There is no specific widget involved.
Static state is global state in disguise
A static field is essentially a global variable scoped to a class.
It's convenient, but it has the same downside the Software Crisis
chapter warned about: anyone with access to the class can change it.
Prefer instance state. Use static state for things that are genuinely
class-wide (a counter of instances, a cached singleton resource, a
constant).
Constants: public static final
The combination public static final is the standard idiom for a
constant: a single value that belongs to the class and can never
change. By convention, constants are written in UPPER_SNAKE_CASE.
Java's standard library is full of these: Integer.MAX_VALUE,
Math.PI, Boolean.TRUE, String.CASE_INSENSITIVE_ORDER. They are
values that have always been there and that you never want anyone
to change.
Utility classes
A utility class is a class that holds only static members —
no instance state, no constructor anyone calls. It is a namespace for
related helper functions. Java's own Math class is the canonical
example.
The convention is to give a utility class a private constructor so
nobody can mistakenly write new StringTools().
The class is also marked final (no class can extend it) — another
convention for utility classes.
When not to use a utility class
It is tempting, especially when coming from procedural languages, to solve every problem with a utility class. Resist. A pile of static methods on a bag of fields is a procedural program in disguise.
Rule of thumb: if a method's behavior depends on the state of some object, the method belongs to that object as an instance method. Static is for genuinely stateless helpers.
Factory methods: a friendly use of static
A static method on a class that creates and returns instances of that class is called a factory method. It can be more readable than a constructor and can return existing instances rather than always allocating.
The constructor is private, so the only way to construct a Color
is through one of the factory methods. They have intent-revealing
names (rgb, named) that no overloaded constructor could match.
Practice
Implement MyMath as a pure utility class:
- A
privateno-arg constructor (sonew MyMath()is impossible). - Static methods:
max(int a, int b)returns the larger ofaandb.clamp(int x, int lo, int hi)returnsxconstrained to[lo, hi]. ThrowsIllegalArgumentExceptioniflo > hi.sum(int[] xs)returns the sum of all elements (returns 0 for empty ornull).
Main.java will exercise the API and is expected to print:
max=10
clamp1=5
clamp2=0
clamp3=10
sum=15
Test your understanding
A field declared static belongs to...
Each instance, with one independent copy per instance
The class itself; there is one copy shared by all instances
The first instance created
The package that contains the class
What is the convention used in Java to define a constant?
const keyword in front of the field
@Constant annotation
public static final field, written in UPPER_SNAKE_CASE
private field with a getter
When is a utility class (only static members, private constructor) a reasonable choice?
Whenever you want to avoid writing constructors
When the methods are genuinely stateless and don't naturally belong to any particular object
For all classes — it makes them easier to test
For any class that has more than ten methods