Topic 20 of 119 2. Fundamentals of Java Programming Interactive Tutorial

Creating Constants in Java: Symbolic Values and the final Keyword

17 mins read Last updated: 22 Aug 2026 18,460 views

Direct Answer & Core Concept

A constant in Java is a value that is set once and can never change. You create one by adding the final keyword to a variable declaration, and the standard form for a shared constant is static final, written in UPPER_SNAKE_CASE. The final keyword can also be applied to methods, classes and parameters. One point confuses almost every beginner: final freezes the reference, not the object, so a final List can still be added to, removed from and emptied. Only a final variable with a primitive type or String, initialised with a constant expression, is a true compile-time constant that javac inlines directly into the calling code.

Detailed Explanation & Mechanics

1. What Is a Constant in Java?

A constant is a value that is set once and then never changes. Java has no single constants keyword, because the same idea applies to variables, methods and classes. Instead you mark the thing you want to protect with the final keyword.

Main.java
Run Online
final int MAX_USERS = 500;
// set once, can never change

The word final is a promise made to the compiler. The compiler then enforces it for you, so a final variable can never be assigned a second time anywhere in your program.

Architecture Diagram
A CONSTANT IN ONE PICTURE

   final int MAX_USERS = 500;
   |     |   |              |
   |     |   |              +--> assigned exactly once
   |     |   +-----------------> name, used everywhere
   |     +---------------------> type
   +---------------------------> final, this can never be reassigned

   MAX_USERS = 500;    OK, this is the one and only assignment
   MAX_USERS = 1000;   ERROR, a final variable cannot change

Because the compiler checks this, final is far safer than a naming convention. A class called ALL_CAPS might still be reassigned by mistake, but a final constant simply will not compile.

2. Where the final Keyword Can Appear

The final keyword is not limited to variables. It can be attached to four different things, and each one means something slightly different.

PlacementExampleMeaning
Fieldstatic final int MAX = 10;Assigned once, one shared copy
Parametervoid run(final int x)The method cannot change x
Methodfinal void run()Subclasses may not override it
Classfinal class MoneyNobody may extend it

Note what is missing. A constructor can never be final, and a method can never be both final and abstract, because a method that must be implemented cannot also be forbidden from being overridden.

3. Creating Your First Constant

The simplest constant is a final local variable or field. The most common and most useful form is static final on a field.

Main.java
Run Online
public class AppConfig {
    public static final String APP_NAME = "OperateTools";
    public static final int MAX_RETRIES = 3;
    public static final long TIMEOUT_MS = 5_000L;
    public static void main(String[] args) {
        System.out.println(APP_NAME);
        System.out.println("Retries: " + MAX_RETRIES);
        MAX_RETRIES = 10;
        // ERROR, this will not compile
    }
}
  • The value is supplied at the point of declaration, so the variable is final and initialised in one step.
  • A constant that belongs to the class rather than to a single object is declared static as well, so every instance sees the same value.
  • Naming is a strong convention rather than a rule. Shared constants are written in UPPER_SNAKE_CASE, exactly like the class MAX_RETRIES.
  • Referencing a static final field does not require an object, so AppConfig.MAX_RETRIES works without ever creating an AppConfig.

4. static final: The Universal Java Constant

The order of the two keywords does not matter, so static final and final static are identical. What matters is that you understand what each half does.

Architecture Diagram
static          +-->  ONE copy, shared by every object,
   |                    lives as long as the class itself
   v
   static final    +-->  and it can never be reassigned
   |
   v
   public static final int MAX_RETRIES = 3;
Main.java
Run Online
public class Counter {
    public static final int SHARED_MAX = 100;
    // one copy for the whole class
    private final int instanceMax;
    // one copy per object
    public Counter(int instanceMax) {
        this.instanceMax = instanceMax;
    } public static void main(String[] args) {
        Counter a = new Counter(10);
        Counter b = new Counter(20);
        System.out.println(Counter.SHARED_MAX);
        // 100
        System.out.println(a.instanceMax);
        // 10
        System.out.println(b.instanceMax);
        // 20
    }
}
  • A static final field is created once, when the class is first loaded, and is then shared by every object of that class.
  • A final instance field gives each object its own value, which is why it is normally set through a constructor.
  • If the value is genuinely the same for every object, declare it static, because a per-object copy of a constant is wasted memory.

5. Interface Fields Are Always public static final

This one catches nearly everybody out. Any field you declare inside an interface is automatically public, static and final, whether you write those keywords or not.

Main.java
Run Online
public interface PaymentGateway {
    int MAX_RETRIES = 3;
    // implicitly public static final
    String DEFAULT_CURRENCY = "INR";
    // implicitly public static final
    void charge(int amount);
}public class GatewayUtil {
    public static void main(String[] args) {
        System.out.println(PaymentGateway.MAX_RETRIES);
        // 3
        System.out.println(PaymentGateway.DEFAULT_CURRENCY);
        // INR
        // PaymentGateway.MAX_RETRIES = 5;   ERROR, it is implicitly final
    }
}

The reason is that an interface describes a contract, and a field that could change would be meaningless in a contract. The compiler also rejects private interface fields, because every interface field is public by definition.

Main.java
Run Online
public interface Settings {
    int TIMEOUT = 30;
    // private int hidden = 1;   ERROR, modifier private not allowed here
    // TIMEOUT = 60;             ERROR, cannot assign a value to final variable
}

One thing that does work is a non-constant initialiser. The field simply stops being a compile-time constant, which is a performance detail rather than a restriction.

6. Compile-Time Constants and Bytecode Inlining

Not every final field is a compile-time constant, and this distinction has a real effect on your compiled code. The Java Language Specification calls something a constant variable only when it is final, of a primitive type or String, and initialised with a constant expression.

Main.java
Run Online
IS THIS A COMPILE-TIME CONSTANT? final double PI = 3.14159;
YES inlined by javac final String NAME = "Java";
YES inlined by javac final char NL = '\n';
YES inlined by javac final int N = 5 + 5;
YES a constant expression final double P = Math.PI;
YES Math.PI is itself constant final String S = new String("x");
NO new runs at runtime final double D = Math.sin(1.0);
NO a method call runs at runtime final int M = someVariable;
NO a plain variable is not constant
Main.java
Run Online
public class Inlining {
    public static final int SIZE = 10;
    // inlined
    public static final String TAG = "paid";
    // inlined
    public static final double COST = 9.99;
    // inlined
    public static double discount() {
        // a real method, not inlined
        return 5.00;
    } public static void main(String[] args) {
        int a = SIZE;
        // javac copies the number 10 in here
        int b = SIZE;
        // and 10 in here again
        double d = discount();
        // a genuine call, made at runtime
        System.out.println(a + " " + b + " " + d + " " + TAG + " " + COST);
    }
}

Run javap on that class and you will see bipush 10 and ldc "paid" sitting in the bytecode, with no getstatic instruction for SIZE, TAG or COST at all.

When a field is a compile-time constant, javac substitutes the literal value into every class that uses it, rather than storing a reference to the field. That is why SIZE costs nothing at runtime, and it is also why recompiling only the caller keeps the old value if you change SIZE later.

7. final Objects: The Reference Is Frozen, Not the Data

This is the single most misunderstood feature of final in Java. When you mark a variable of a reference type as final, you freeze the reference. The object it points to is completely untouched.

Architecture Diagram
final List tags = new ArrayList();
   |
   |   +--> the REFERENCE, frozen by final
   v
   [ tag1, tag2, tag3 ]  +--> the OBJECT, nobody froze this
   ^                     |
   |                     +--> add, remove, clear and set all work fine
   |
   +--> the only thing that is illegal: pointing at a different list
Main.java
Run Online
import java.util.ArrayList;
import java.util.List;
public class FinalList {
    public static void main(String[] args) {
        final List tags = new ArrayList();
        tags.add("paid");
        // fine
        tags.add("urgent");
        // fine
        tags.set(0, "settled");
        // fine
        tags.clear();
        // fine
        System.out.println("Size now: " + tags.size());
        // tags = new ArrayList();   ERROR, the reference is final
    }
}

If you need a list that genuinely cannot change, ask for an unmodifiable view. It is a different promise, and the compiler and the class both enforce it for you.

Main.java
Run Online
import java.util.ArrayList;
import java.util.List;
public class Unmodifiable {
    public static void main(String[] args) {
        List raw = new ArrayList();
        raw.add("a");
        List safe = List.copyOf(raw);
        // safe.add("b");   ERROR, this List rejects every change
        System.out.println(safe);
    }
}

8. final with Arrays

Arrays are objects, so the same rule applies exactly. A final array reference stops you from pointing at a different array, but it does nothing at all to the elements inside.

Main.java
Run Online
public class FinalArray {
    public static void main(String[] args) {
        final int[] scores = {
            90, 80, 70
        };
        scores[0] = 95;
        // perfectly legal
        System.out.println(scores[0]);
        // 95
        // scores = new int[]{1, 2};   ERROR, the reference is final
        // scores = null;              ERROR, same reason
        for (int i = 0;
        i < scores.length;
        i++) {
            System.out.println(scores[i]);
        }
    }
}

To stop the contents changing as well, either copy the array in and never keep the original reference, or use a java.util.List created with List.of or List.copyOf, which refuses every modification.

9. Blank final Fields and Constructor Assignment

A final field does not have to be given its value on the same line. You may declare it without a value, and the compiler then forces you to assign it exactly once later. This is called a blank final field.

Main.java
Run Online
public class Order {
    private final int orderId;
    // declared, not yet assigned
    private final double amount;
    // declared, not yet assigned
    private static final double TAX;
    // static blank final
    static {
        TAX = 0.18;
        // static initialiser assigns it once
    } public Order(int orderId, double amount) {
        this.orderId = orderId;
        this.amount = amount;
    } public double total() {
        return amount +(amount * TAX);
    } public static void main(String[] args) {
        Order o = new Order(7, 1000.0);
        System.out.println("Order " + o.orderId + " total: " + o.total());
    }
}
  • Every constructor must assign every blank final instance field, on every path, or the code will not compile.
  • The same field cannot be assigned twice, even inside two different constructors, because the compiler treats it as one single assignment.
  • A static blank final is assigned inside a static initialiser block, which runs once when the class is loaded.
  • You can also initialise an instance final field in an instance initialiser block instead of a constructor, which is handy when the value is computed.

10. final Methods

A final method cannot be overridden by a subclass. You use it when behaviour must stay identical for every subclass, usually because that behaviour protects something important.

Main.java
Run Online
public class Payment {
    public void pay() {
        System.out.println("Processing generic payment");
    } public final void audit() {
        // no subclass may change this
        System.out.println("Audit entry written");
    }
}public class UpiPayment extends Payment {
    @Override public void pay() {
        // fine, pay is not final
        System.out.println("Processing UPI payment");
    } @Override public final void audit() {
        // ERROR, cannot override a final method
        System.out.println("changed");
    }
}
  • final on a method stops overriding, not calling. You can call a final method as often as you like.
  • A final method combined with private is common, because a private method is already un-overridable, but the explicit final documents the intent.
  • A static method cannot be overridden at all, it is only hidden, so final static is legal but the final is largely decorative.
  • Marking a method final also helps performance, because the compiler knows no subclass can provide a different implementation.
  • A final method can never be abstract. The two keywords contradict each other and javac will reject the combination.

11. final Classes

A final class cannot be extended by anybody. It is the strongest seal Java offers, and it is how the standard library protects types your code depends on.

Main.java
Run Online
public final class Money {
    private final long paise;
    public Money(long paise) {
        this.paise = paise;
    } public long getPaise() {
        return paise;
    }
}public class Rupee extends Money {
    // ERROR, Money is final
    public Rupee(long paise) {
        super(paise);
    }
}
  • String, Integer, Math and LocalDate are all final, so nobody can subclass them and quietly change behaviour your program depends on.
  • Because nothing can extend a final class, its methods are effectively final too, so you rarely need to mark them individually.
  • final and abstract cannot be combined, for exactly the same reason as methods. A class that must be extended and a class that must not be extended are opposites.
  • final is not the same as private. A private constructor prevents outsiders creating instances but still allows subclassing, so a class that must never be extended needs the final keyword.

12. final Parameters and Effectively Final Locals

A final parameter cannot be reassigned inside the method. The caller still has to supply a value, so this is about protecting your own code, not the caller.

Main.java
Run Online
public class ParameterDemo {
    public static void main(String[] args) {
        final int base = 100;
        // base = 200;    ERROR, base is final
        int bonus = 50;
        // bonus = 60;    ERROR under Java 7 and earlier
        Runnable task =() -> System.out.println(base + bonus);
        task.run();
    }
}

Since Java 8 you no longer need the final keyword to use a local variable inside a lambda or an anonymous class. Any local that is never reassigned is called effectively final and can be captured. The word final is still useful, because it makes the intention obvious and because the compiler checks it even if the variable is only reassigned on a branch you did not notice.

Main.java
Run Online
final int x = 5;
final, and captured by a lambda int y = 5;
effectively final, captured just the same int z = 5;
z = 6;
not effectively final, cannot be captured

13. Common Mistakes with final

What You WroteWhat HappensCorrect Version
final List<String> l = new ArrayList<>(); l.add("x");Works, the object is still mutableUse List.of("x") if it must not change
final int[] a = {1}; a[0] = 2;Works, arrays are objectsUse List.of(1) for a fixed list
final int n; (never assigned)Blank final never assignedAssign it in a constructor or initialiser
static final String S = new String("x");Not a constant variable, not inlinedstatic final String S = "x";
interface I { int X = 1; } X = 2;Implicitly final, so it failsNothing, interface fields cannot change
final void run() in an abstract classfinal and abstract cannot meetRemove one of the two keywords
class A extends StringString is finalWrap or compose instead
public static final File F = new File("x");Shared mutable objectPrefer a String path and a getter
  • Expecting final to make a collection immutable is mistake number one, so reach for List.of or List.copyOf when that is what you need.
  • Forgetting to assign a blank final in one of several constructors is a compile error, which is the compiler doing you a favour.
  • A public static final mutable object such as a List or a File is a global variable wearing a constant's name, and it will be mutated by someone eventually.

14. Java Constants Cheat Sheet

QuestionAnswer
Keyword that creates a constantfinal
Usual constant formpublic static final
Naming conventionUPPER_SNAKE_CASE
Can static and final be swapped?Yes, order does not matter
Interface fieldsImplicitly public static final
Does final make an object immutable?No, it freezes only the reference
Fixed immutable collectionList.of or List.copyOf
Blank final fieldAssigned exactly once in a constructor or initialiser
static blank finalAssigned in a static initialiser block
final methodCannot be overridden
final classCannot be extended
final parameterCannot be reassigned by the method
Capture a local in a lambdafinal or effectively final, since Java 8
Really inlined by javacfinal primitive or String with a constant expression, no new and no method call
Best alternative for named valuesAn enum, for a fixed set of choices

Constants are the foundation of the next topic, because a constant is simply a value you have promised never to change, and every naming convention and immutability rule in Java builds on that one idea.

Runnable Code Example: Creating Constants in Java

Java 17+
Main.java
Run Online
public class ConstantsDemo {
    // ---------- shared constants, the usual form ----------
    public static final double PI = 3.141592653589793;
    public static final String APP_NAME = "OperateTools";
    public static final int MAX_RETRIES = 3;
    public static final long TIMEOUT_MS = 5_000L;
    // ---------- a per-object final value ----------
    private final int orderId;
    private final String createdBy;
    public ConstantsDemo(int orderId, String createdBy) {
        this.orderId = orderId;
        // assigned exactly once
        this.createdBy = createdBy;
    } public int getOrderId() {
        return orderId;
    } public String getCreatedBy() {
        return createdBy;
    } public static void main(String[] args) {
        System.out.println(APP_NAME + " is running");
        System.out.println("Area of r=2 : " + areaOfCircle(2));
        System.out.println("Max retries: " + MAX_RETRIES);
        System.out.println("Timeout    : " + TIMEOUT_MS + " ms");
        ConstantsDemo demo = new ConstantsDemo(42, "tanmoy");
        System.out.println("Order " + demo.getOrderId() + " by " + demo.getCreatedBy());
        // final freezes the REFERENCE, not the contents
        final java.util.List<String> tags = new java.util.ArrayList<>();
        tags.add("paid");
        tags.add("urgent");
        tags.remove("paid");
        System.out.println("Tags now  : " + tags);
        // tags = new java.util.ArrayList<>();   ERROR, the reference is final
        // tags.add("x") is perfectly legal, and so is tags.clear();
        // a final parameter cannot be reassigned
        describe(MAX_RETRIES);
        // MAX_RETRIES = 10;   ERROR, a final variable can only be set once
    } static double areaOfCircle(double radius) {
        return PI * radius * radius;
    } static void describe(final int retries) {
        // retries = 5;   ERROR, a final parameter cannot be reassigned
        System.out.println("Retries allowed: " + retries);
    }
}

Common Java Technical Interview Questions & Answers

Interview Prep
Q1

How do you create a constant in Java?

Add the final keyword to the variable declaration, and for a value shared by the whole class also add static. The normal form is public static final, for example public static final int MAX_RETRIES = 3, and it is written in UPPER_SNAKE_CASE by convention.
Q2

What is the difference between final, finally and finalize?

final is a modifier you attach to a variable, method or class. finally is a block in a try statement that almost always runs, used for cleanup. finalize is a method on Object that has been deprecated for removal since Java 9, and you should never write one; use try-with-resources instead.
Q3

Does final make an object immutable?

No, and this is the single biggest misunderstanding. final freezes the reference, so you cannot point the variable at a different object, but the object itself is untouched. A final List can still be added to, removed from and cleared. For real immutability you need an immutable type such as String, or a defensive copy.
Q4

Is final enough to make something a compile-time constant?

No. javac only inlines a final variable of primitive type or String whose initialiser is a constant expression. final double PI = 3.14159; is inlined, but final String S = new String("x"); is not, because new runs at runtime. A detail that trips people up: final double P = Math.PI; IS inlined, because Math.PI is itself a constant variable.
Q5

What is a blank final variable?

A final variable declared without a value, such as private final int orderId;. The compiler forces you to assign it exactly once, either in the constructor or in an instance initialiser block. Forgetting to do so in every constructor is a compile error.
Q6

Can a final variable be initialised inside a constructor?

Yes. A blank final instance variable must be assigned exactly once before the constructor finishes, and this happens on every path through the constructor. A static blank final is assigned in a static initialiser block instead.
Q7

Are interface fields automatically constants?

Yes, and this surprises people every time. Every field declared in an interface is implicitly public static final, so you can write int MAX = 10; and it behaves exactly like public static final int MAX = 10;. Changing it later is a compile error, and declaring it private is also an error.
Q8

What does a final method do?

It cannot be overridden by a subclass. You use it when a method must not change behaviour for security reasons or to document a guarantee, and it also helps the optimiser because the call target is known. A final method is often combined with private, though private methods are already un-overridable.
Q9

What is a final class?

A class that cannot be extended, which is how Java closes a class for modification. String, Integer and Math are all final for exactly this reason, so nobody can subclass them and change behaviour your code depends on. A final class cannot also be abstract, because that combination makes no sense.
Q10

Can a final variable be used in a switch or a lambda?

A final or effectively final local variable can always be captured by a lambda or an anonymous class, which is the whole reason the effectively final rule exists. Once captured, it can never be reassigned, not even after the lambda has been created.
Q11

What is the difference between static final and a final instance field?

static final gives you exactly one copy shared by every instance and callable without creating an object. A final instance field gives every object its own copy, which is why it is normally set in the constructor. If the value is the same for everyone, make it static.
Q12

Why should I use an enum instead of String constants?

Because an enum gives you a fixed, type-safe set of named values that the compiler checks, and it can carry behaviour and fields. A group of public static final String values will happily accept any other String at a call site, and you only find out at runtime that you made a typo.

Key Takeaways, Best Practices & Clean Code Rules

Java Best Practice & Production Rules

Give a shared constant exactly one home. Put it in one clearly named class, make it public static final, write it in UPPER_SNAKE_CASE, and let the rest of the codebase import it instead of copying the number around. Reach for a primitive or String constant when the value is a genuine fixed fact such as a timeout or a buffer size, but do not force it: a public static final File is a shared mutable object pretending to be a constant. Prefer an enum over a family of String constants, because the compiler can then catch a typo that would otherwise surface at runtime. Treat final objects carefully, since a final reference to a mutable collection is a promise about the variable, not about the data, so return unmodifiable views when you want a real guarantee. Use a final method when behaviour must not be overridden and a final class when a type must not be extended. Finally, remember that final on a compile-time constant is inlined into the calling bytecode, so if you ever need a value that can genuinely change at runtime, do not declare it constant.
We value your privacy & transparency

OperateTools uses cookies and local storage to ensure basic tool functionality, preserve preferences, and analyze aggregated traffic. We never sell your personal data or index your document files. Learn more in our Privacy Policy.