Java
Coding Standards
Object-Oriented Programming
Variable Declaration
Programming Concepts

private final static attribute vs private final attribute

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

A private static final field in Java belongs to the class and is shared across every instance. A private final field belongs to each individual object and can hold a different value per instance. The static keyword is the only difference between the two declarations, but it changes where the value lives in memory, when it gets initialized, and what design intent the code communicates.

Choosing the wrong one does not usually cause a compiler error, but it leads to wasted memory, confusing APIs, or bugs when a value that should be constant per class accidentally varies per instance.

What Each Modifier Does

Before comparing the two combinations, it helps to be precise about each keyword individually.

ModifierEffect
privateRestricts access to the declaring class only
finalPrevents reassignment after initialization
staticBinds the field to the class, not to any instance

When final and static appear together, the field becomes a compile-time or class-load-time constant. When final appears without static, the field is an immutable property of each object.

Side-by-Side Comparison

java
1public class Connection {
2    // Class-level constant: one copy, shared by all instances
3    private static final int MAX_RETRIES = 3;
4
5    // Instance-level constant: unique per object, set at construction
6    private final String host;
7
8    public Connection(String host) {
9        this.host = host;
10    }
11}

MAX_RETRIES exists once in the JVM. Every Connection object reads the same value. host is created fresh for every new Connection(...) call and holds whatever string was passed to the constructor.

Initialization Rules

The initialization timing is different and matters for correctness.

java
1public class Example {
2    // static final: must be assigned in declaration or static initializer
3    private static final String CONFIG_PATH;
4    static {
5        CONFIG_PATH = System.getenv("APP_CONFIG");
6    }
7
8    // final: must be assigned in declaration, instance initializer, or constructor
9    private final long createdAt;
10
11    public Example() {
12        this.createdAt = System.currentTimeMillis();
13    }
14}

A static final field that is not assigned by the end of the static initializer block causes a compilation error. A final instance field that is not assigned by the end of every constructor also causes a compilation error. The compiler enforces both guarantees.

Memory and Lifecycle

Aspectprivate static finalprivate final
StorageOne copy in the class's metadata area (metaspace in modern JVMs)One copy per object on the heap
Created whenThe class is loaded by the classloaderThe object is constructed with new
Garbage collected whenThe classloader is garbage collected (rarely, for application classes)The object is garbage collected
Memory cost for N instancesConstant regardless of NProportional to N

If you declare a value as private final when it could be private static final, you allocate that value inside every object. For a simple int that wastes 4 bytes per instance, which is negligible. For a String or a large array, the waste multiplies quickly in applications that create millions of instances.

java
1// Wasteful: every instance carries its own identical copy
2public class BadExample {
3    private final String DEFAULT_REGION = "us-east-1";
4}
5
6// Correct: one shared constant
7public class GoodExample {
8    private static final String DEFAULT_REGION = "us-east-1";
9}

When to Use Each

Use private static final for True Constants

Values that are the same for every instance and do not depend on constructor arguments belong at the class level.

java
1public class MathUtils {
2    private static final double TAX_RATE = 0.075;
3    private static final int BUFFER_SIZE = 8192;
4    private static final String API_VERSION = "v2";
5}

By convention, Java constants use UPPER_SNAKE_CASE names. This naming convention only applies to static final fields, not to instance-level final fields.

Use private final for Per-Instance Immutable State

Values that are fixed after construction but vary between objects belong at the instance level.

java
1public class Order {
2    private final String orderId;
3    private final LocalDateTime placedAt;
4    private final BigDecimal total;
5
6    public Order(String orderId, BigDecimal total) {
7        this.orderId = orderId;
8        this.placedAt = LocalDateTime.now();
9        this.total = total;
10    }
11}

Each Order has its own orderId and total. Making these fields final signals that they will not change after construction, which simplifies reasoning about the object's state.

Decision Flowchart

  1. Is the value the same for every instance of the class? If yes, use static final.
  2. Does the value depend on constructor arguments or construction-time state? If yes, use final (no static).
  3. If you are unsure, ask whether creating two instances with different values for this field ever makes sense. If the answer is no, it is a class-level constant.

Behavior with Reference Types

final prevents reassignment of the reference, not mutation of the referenced object. This applies to both static final and instance final.

java
1public class Registry {
2    // The list reference cannot be reassigned, but the list contents can change
3    private static final List<String> ALLOWED_HOSTS = new ArrayList<>();
4
5    static {
6        ALLOWED_HOSTS.add("example.com");
7        ALLOWED_HOSTS.add("api.example.com");
8    }
9
10    public void addHost(String host) {
11        // This compiles and runs without error
12        ALLOWED_HOSTS.add(host);
13    }
14}

If you need the contents to be truly immutable, wrap the collection.

java
private static final List<String> ALLOWED_HOSTS =
    List.of("example.com", "api.example.com");

List.of() (Java 9+) returns an unmodifiable list. Calling add() on it throws UnsupportedOperationException.

Thread Safety Implications

static final fields initialized at declaration or in a static initializer are guaranteed by the JVM to be safely published to all threads. The class loading mechanism ensures this.

final instance fields are also safely published to other threads as long as the object reference does not escape during construction (the "safe publication" guarantee defined in the Java Memory Model, JSR-133). If you leak this in a constructor, another thread could see an uninitialized final field.

java
1public class Unsafe {
2    private final int value;
3
4    public Unsafe(Registry registry) {
5        registry.register(this); // leaks 'this' before construction finishes
6        this.value = 42;         // another thread reading via registry might see 0
7    }
8}

Avoid publishing this before the constructor completes. This is a subtle but well-documented pitfall.

Common Pitfalls

  • Declaring a shared constant as private final instead of private static final. Every object carries its own identical copy, wasting memory and obscuring the intent that this value is universal.
  • Assuming final makes an object immutable. final prevents reassignment of the reference. The object's internal state can still be mutated unless the type itself is immutable.
  • Using UPPER_SNAKE_CASE for instance-level final fields. The naming convention is reserved for static final constants. Instance fields should use camelCase even when they are final.
  • Leaking this in a constructor before all final fields are assigned. This breaks the safe publication guarantee and can cause other threads to see default (zero/null) values.
  • Forgetting that static final fields of non-primitive, non-String types are not compile-time constants. Only static final fields of primitive types or String initialized with a literal are true compile-time constants inlined by javac. Other types are initialized at class load time.

Summary

  • private static final declares a class-level constant: one copy, shared by all instances, initialized when the class loads.
  • private final declares an instance-level immutable field: one copy per object, initialized in the constructor.
  • Use static final when the value is the same for every instance. Use final alone when the value varies per object but should not change after construction.
  • final prevents reassignment of the reference, not mutation of the referenced object. Use unmodifiable collections or immutable types when full immutability is required.
  • Both kinds of final fields carry thread-safety guarantees from the Java Memory Model, but those guarantees depend on not leaking this during construction.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.