skip to content

How do you declare a constant in Java with static final, and what does static add over plain final?

level: middleimportance: must knowfreq 68%

answer

  1. static final = class constant idiom
  2. static = one shared copy via class name
  3. UPPER_SNAKE_CASE naming
  4. primitive/String + constant expr = inlined compile-time constant
  5. object constants need immutable types (List.of)

basics

~20 s

A Java constant is usually a static final field, e.g. public static final int MAX = 100;. static means one shared copy for the whole class instead of one per object, and final means it cannot be reassigned. The naming convention is ALL_CAPS_WITH_UNDERSCORES.

solid answer

~50 s

static final is the standard idiom for a class constant. final makes the field assign-once; static makes it belong to the class rather than each instance, so there is a single shared copy and you access it via the class name (MathUtil.PI), not an object. Constants are named in UPPER_SNAKE_CASE by convention. A static final field of a primitive or String type initialized with a compile-time constant expression becomes a true compile-time constant: the compiler inlines its value into call sites and it is eligible for use in switch labels, annotation values, and case constants. That inlining has a subtle binary-compatibility cost: if a constant's value changes, dependents compiled against the old value keep the old inlined value until recompiled. For object constants, prefer immutable types (e.g. List.of(...)) because static final does not make a mutable object immutable.

code

java · 9 lines
java
public final class HttpConfig {
    public static final int DEFAULT_TIMEOUT_MS = 5_000; // compile-time constant
    public static final String USER_AGENT = "katajob/1.0";
    public static final List<String> SAFE_METHODS = List.of("GET", "HEAD"); // immutable

    private HttpConfig() {} // no instances needed
}

// usage: HttpConfig.DEFAULT_TIMEOUT_MS

go deeper

for a junior

Can declare public static final int X = 1; and knows it is the constant idiom with ALL_CAPS naming.

for a middle

Explains what static adds (one shared class-level copy) and that object constants must be immutable types.

for a senior

Knows compile-time constant inlining, where constant expressions are required (case labels, annotations), and the binary-compatibility pitfall.

for a principal

Weighs constant exposure in public APIs (inlining/versioning), favors enums or config over scattered constants, and reasons about static-init ordering and class-loading effects.

## What a constant is in Java Java has no top-level `const` keyword. The idiom for a **constant** — a named value fixed for the whole program — is a field declared: ```java public static final int MAX_RETRIES = 3; ``` This combines two modifiers. ### `final` — assign once `final` on a field means it can be assigned exactly once (in the declaration or, for a blank final, in every constructor / static initializer path) and then never reassigned. ### `static` — one per class, not per object A normal (instance) field gets its own copy in every object you create. A **`static`** field belongs to the **class itself**: there is exactly one copy, shared by all instances, and it exists even if you never create an instance. You access it through the class name: ```java Math.PI // good — static access via the class new Math().someInstanceField // (not applicable; Math is utility) ``` So for a constant you want **both**: `static` (share one copy, no need for an object) and `final` (never changes). That is why the idiom is `static final`. ### Naming convention Constants use **UPPER_SNAKE_CASE**: `MAX_VALUE`, `DEFAULT_TIMEOUT_MS`. This is a strong, universal convention, not a language rule. ## Compile-time constants and inlining If a `static final` field is of a **primitive** type or `String` **and** is initialized with a **constant expression** (literals and other compile-time constants combined with operators), it becomes a **compile-time constant**. Two consequences: 1. **Inlining:** the compiler copies the literal value directly into every place that uses it, rather than reading the field at runtime. ```java public static final int SIZE = 10; // compile-time constant int[] a = new int[SIZE]; // compiled as new int[10] ``` 2. **Usable where constants are required:** `switch`/`case` labels, annotation arguments, and array sizes for initializers all require compile-time constants. A field initialized from a method call is `final` but **not** a compile-time constant: ```java public static final long START = System.currentTimeMillis(); // runtime value ``` ### The binary-compatibility gotcha Because compile-time constants are **inlined**, if you change `public static final int SIZE = 10;` to `20` and recompile only that class, any **other** already-compiled class that referenced `SIZE` still carries the old inlined `10` until it too is recompiled. This is a well-known pitfall for public API constants across module/jar boundaries. ## Object constants are not automatically immutable `static final` freezes the reference, not the object: ```java public static final List<String> ROLES = new ArrayList<>(); // dangerous ROLES.add("admin"); // anyone can mutate the shared constant! ``` Prefer an immutable object: ```java public static final List<String> ROLES = List.of("admin", "user"); // safe ``` ## Static initialization A `static final` field can be assigned in a **static initializer block** if a single expression is not enough; the compiler still enforces assign-once across all static-init paths.

  • Why is changing a public static final int risky across jars?
    Because the compiler inlines compile-time constants into callers. If you change the value and recompile only the defining class, callers compiled earlier keep the old inlined value until they are recompiled, causing silent mismatches.
  • Is public static final List<String> X = new ArrayList<>(); a safe constant?
    No. The reference is fixed but the ArrayList is mutable, so any code can add or remove elements from the shared list. Use List.of(...) or Collections.unmodifiableList to make the contents immutable.

A static final constant is like a value printed in the company handbook (one official copy everyone shares, never edited), whereas an instance final is a value handwritten once on each employee's badge.

saying these in an interview costs you the question

  • Thinking static alone makes a value constant (it does not — it just shares one copy).
  • Assuming static final makes a referenced collection immutable.
  • Not knowing primitive/String compile-time constants get inlined.
  • Forgetting the UPPER_SNAKE_CASE convention or accessing static constants through an instance.

context