Constants in Java
static final, enums, and immutable collections
static final: Java's Constants
Java defines constants using the static final modifier combination. A static final field belongs to the class (not to instances), and its value cannot be changed after initialization. By convention, constant names use SCREAMING_SNAKE_CASE.
public class AppConfig {
public static final int MAX_RETRIES = 3;
public static final String API_BASE_URL = "https://api.example.com/v1";
public static final long TIMEOUT_MS = 30_000L;
public static final double TAX_RATE = 0.0825;
}The final keyword prevents reassignment but, like JavaScript's const, does not make objects immutable. A static final List can still be modified through add() and remove(). To make collections truly constant, use immutable factory methods or unmodifiable wrappers.
Compile-Time Constants
Java distinguishes between compile-time constants and regular final fields. A compile-time constant is a static final field of a primitive type or String initialized with a constant expression. The compiler inlines these values at every use site, which means changing the value requires recompiling all dependent classes.
// Compile-time constant — inlined by javac
public static final int MAX_SIZE = 1024;
public static final String VERSION = "2.0";
// NOT a compile-time constant — computed at class load
public static final long STARTUP_TIME = System.currentTimeMillis();
public static final String HOME = System.getenv("HOME");This inlining behavior has an important consequence: if you change a compile-time constant in a library, all consuming code must be recompiled to pick up the new value. This is why some developers prefer static methods or enum values for constants that might change across library versions.
Enum Constants
Java enums are full-featured classes that can have fields, methods, constructors, and implement interfaces. They are the idiomatic way to define a fixed set of related constants with behavior. Every enum instance is a singleton, guaranteed to be created once during class loading.
public enum HttpStatus {
OK(200, "OK"),
CREATED(201, "Created"),
BAD_REQUEST(400, "Bad Request"),
NOT_FOUND(404, "Not Found"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error");
private final int code;
private final String reason;
HttpStatus(int code, String reason) {
this.code = code;
this.reason = reason;
}
public int code() { return code; }
public String reason() { return reason; }
public boolean isSuccess() { return code >= 200 && code < 300; }
public boolean isError() { return code >= 400; }
}
// Usage
HttpStatus status = HttpStatus.OK;
System.out.println(status.code()); // 200
System.out.println(status.isSuccess()); // trueJava 14+ introduced switch expressions with exhaustiveness checking for enums. The compiler verifies that every enum value is handled, eliminating an entire class of bugs where a new enum variant is added but not handled in existing switch statements.
Immutable Collections
Java 9 introduced factory methods for creating immutable collections: List.of(), Set.of(), and Map.of(). These are the preferred way to define constant collections because they are truly unmodifiable — any attempt to modify them throws UnsupportedOperationException.
// Java 9+ immutable collections
public static final List<String> SUPPORTED_FORMATS = List.of("png", "jpg", "webp", "gif");
public static final Set<String> RESERVED_WORDS = Set.of("class", "interface", "enum", "record");
public static final Map<String, Integer> STATUS_CODES = Map.of(
"OK", 200,
"NOT_FOUND", 404,
"ERROR", 500
);
// For pre-Java 9, use Collections.unmodifiable*
public static final List<String> LEGACY_LIST =
Collections.unmodifiableList(Arrays.asList("a", "b", "c"));Built-in Constants
Java provides constants through wrapper classes, the Math class, and various standard library classes:
// Math constants
Math.PI // 3.141592653589793
Math.E // 2.718281828459045
// Integer limits
Integer.MAX_VALUE // 2_147_483_647
Integer.MIN_VALUE // -2_147_483_648
Long.MAX_VALUE // 9_223_372_036_854_775_807L
// Float/Double limits
Double.MAX_VALUE // ~1.8e308
Double.MIN_VALUE // ~4.9e-324 (smallest positive)
Double.POSITIVE_INFINITY
Double.NEGATIVE_INFINITY
Double.NaN
// Boolean
Boolean.TRUE
Boolean.FALSE
// Collections
Collections.EMPTY_LIST
Collections.EMPTY_MAP
Collections.EMPTY_SETCommon Patterns
- Use
static finalfor individual constants and enums for related groups of constants. - Define constants in an interface or a final utility class with a private constructor to prevent instantiation.
- Prefer
List.of(),Set.of(), andMap.of()over mutable collections for constant data. - Use Java records (Java 16+) for constant value objects:
record Point(int x, int y) {}creates an immutable data class. - Be aware of compile-time constant inlining — changing a
static finalprimitive or String requires recompiling all dependent code.