Command Palette

Search for a command to run...

PHASE 1Beginner ~31 min· topic 14 of 14

Topic 1.14

Constants, final and Literals

In one line

A literal is a value written directly in code, such as 42, 0xFF, 3.5f, 'A', true, "hi" or null, and its exact spelling decides its type. Marking a variable final means it can be assigned only once; a static final field with a fixed value is a constant, named in UPPER_SNAKE_CASE.

Think of it like this

The numbers painted on a football shirt versus the score on the board. The score keeps changing during the match, but the shirt number is printed once and stays for the whole game. A normal variable is the scoreboard; a final variable is the printed shirt number; the digits you write in code, like 7, are literals: the paint itself.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Literal
A value written directly in source code, like 42, 'x', true or "hello".
final
A modifier meaning 'can be assigned only once'. A final variable can't be changed after it gets its value.
Constant
A named value that never changes, usually a static final field such as MAX_SIZE.
Magic number
A bare number in code with no name explaining what it means, like 0.18 or 86400.
Blank final
A final variable declared without a value and assigned exactly once later.
Hexadecimal
Base 16, written with 0x. Digits are 0–9 then A–F, so 0xFF is 255.
Octal
Base 8, written in Java with a leading 0. 010 means eight.
Binary
Base 2, written with 0b, using only the digits 0 and 1. 0b101 is 5.
Compile-time constant
A final primitive or String whose value the compiler can work out from literals alone, so it can paste the value straight into the code.

Step by step

01final: one assignment, then read-only

Put final in front of a declaration and the compiler allows exactly one assignment. Every later read is fine; any second assignment is an error at compile time, so it costs nothing when the program runs.

Use final for local values that shouldn't change after they're computed. Many teams do this by default because a reader then knows the value is the same on every line below.

Main.javawhole filejava
final int lives = 3;
System.out.println(lives);   // reading is fine
// lives = 4;                // error: cannot assign a value to final variable lives

02Blank finals and definite assignment

A final local doesn't need its value on the declaration line. The compiler checks two things: it is assigned before every read (definitely assigned), and never assigned twice on any path (definitely unassigned before each assignment).

This pairs well with if/else: each branch assigns once, and after the if the variable is fixed. If you add a third assignment by mistake, you get *variable ... might already have been assigned*.

03Constants: static final and UPPER_SNAKE_CASE

Declare constants at the top of the class, outside main, with static final. Then every method in the class can use them. A static final field must be given a value where it's declared (or in a static initialiser block, Topic 4.6); otherwise you get *variable ... might not have been initialized*.

The naming convention, MAX_PLAYERS, GST_RATE, SECONDS_PER_DAY, tells every reader 'this never changes'. Group related constants together. For a fixed set of options such as days or colours, an enum (Topic 4.10) is better than a list of int constants.

Main.javawhole filejava
public class Main {
    static final int SECONDS_PER_DAY = 24 * 60 * 60;   // computed once by the compiler
    static final double GST_RATE = 0.18;
    static final String APP_NAME = "Shop";
    ...
}

04Integer literals: four bases and underscores

The same number can be written four ways: 255, 0xFF, 0377, 0b1111_1111. The compiler converts all of them to the same 32-bit int. Hex is natural for colours, masks and bytes (Topic 1.9), binary for flags, and decimal for everything else.

Without a suffix, an integer literal is an int and must fit in one: 3000000000 is a compile error (*integer number too large*) even when assigned to a long. Write 3_000_000_000L. Use capital L; a lower-case l looks like the digit 1.

A hex or binary literal may describe all 32 bits, so 0xFFFF_FFFF is legal and equals -1 (two's complement, Topic 1.3).

Integer literals: four bases and underscoresdiagram
Rendering diagram…

05The leading-zero trap

In Java a leading 0 means octal. int pin = 0412; stores 266, not 412. int day = 09; doesn't even compile: 9 isn't an octal digit, so the compiler reads 0 as a complete literal and then complains about the stray 9.

Codes with leading zeros, such as PIN codes, phone numbers and postcodes, aren't numbers you calculate with. Store them as String.

06Floating-point, char, boolean and null literals

2.5, .5, 5. and 1e3 are all double. The e means 'times ten to the power': 6.02e23, 1.5e-3. Add f to get a float. Underscores work here too: 3.141_592.

'A' is a char, written in single quotes; '\u0041' is the same character by its Unicode number. true and false are the only boolean literals. null can be assigned to any reference type (String, arrays, objects) but not to a primitive: int n = null; doesn't compile.

07What the compiler does with constants

When a final primitive or String is initialised with a constant expression (literals, operators, other constants), javac computes the value itself and writes it into the class file. 24 * 60 * 60 becomes 86400 before the program ever runs, and every use of the constant is replaced by 86400.

Two visible effects: constant Strings are pooled, so a final String joined with a literal gives the pooled object (the == puzzle from Topic 1.10), and constants can be used in switch case labels (Topic 2.3), which ordinary variables can't.

Try it yourself

  1. 1

    Replace the magic numbers

    Write a program that prints the price of 3 items at 250 each with 18% tax and a 40 delivery fee, first with bare numbers. Then introduce static final constants ITEM_PRICE, GST_RATE and DELIVERY_FEE and check that the output is the same. Which version would you rather change next year?

  2. 2

    Decode the literals

    Predict, then print: 0x10, 010, 0b10, 10L, 1e1, '1' + 1. Expected: 16, 8, 2, 10, 10.0, 50 (the code of '1' is 49).

  3. 3

    See the constant in the bytecode

    Compile the last example and run javap -c -constants Main. Look for 86400: the field holds the folded number, and in main the compiler went further and joined the whole message into one constant String. There is no multiplication and no field read left.

    terminal
    $ javac Main.java
    javap -c -constants Main | grep 86400
    ── expected output ──
    static final int SECONDS_PER_DAY = 86400;
    3: ldc #15 // String seconds per day: 86400

Code & diagrams

final locals and class constants New tab
Sign in to run this example in your browser.

Expected output

Ludo: 3 of 4 seats taken, 1 left
price with GST: 590.0
bonus: 50
Integer literals in every base New tab
Sign in to run this example in your browser.

Expected output

255 four ways: 255 255 255 255
million: 1000000, people: 1428627663
0xFFFF_FFFF = -1
010 = 8
42 in binary: 101010
255 in hex:   ff
Decimal, char, boolean and null literals New tab
Sign in to run this example in your browser.

Expected output

1000.0 0.0015 0.5 2.5 3.141592
A A equal? true
ready: true, nobody: null
final freezes the variable, not the object New tab
Sign in to run this example in your browser.

Expected output

[99, 20, 30]
Hi there
Compile-time constants are folded and pooled New tab

This shows what the compiler does; it is not a reason to compare Strings with `==`. Use `equals` (Topic 1.10).

Sign in to run this example in your browser.

Expected output

seconds per day: 86400
constant + literal pooled? true
variable + literal pooled? false

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Change a constant

Assign a new value to a static final field.

Main.javawhole filejava
public class Main {
    static final int MAX_PLAYERS = 4;

    public static void main(String[] args) {
        MAX_PLAYERS = 5;
        System.out.println(MAX_PLAYERS);
    }
}
terminal
$ javac Main.java
── what you'll see ──
Main.java:5: error: cannot assign a value to static final variable MAX_PLAYERS
MAX_PLAYERS = 5;
^
1 error

Break #2

Assign a blank final twice

Declare final int total;, then assign it on two lines.

Main.javawhole filejava
public class Main {
    public static void main(String[] args) {
        final int total;
        total = 10;
        total = 20;
    }
}
terminal
$ javac Main.java
── what you'll see ──
Main.java:5: error: variable total might already have been assigned
total = 20;
^
1 error

Break #3

A leading zero on 09

Write a day number with a leading zero: int day = 09;.

Main.javawhole filejava
public class Main {
    public static void main(String[] args) {
        int pinCode = 0412;
        int day = 09;
        System.out.println(pinCode + " " + day);
    }
}
terminal
$ javac Main.java
── what you'll see ──
Main.java:4: error: ';' expected
int day = 09;
^
1 error
Output from JDK 21. Notice that line 3 compiles without complaint, yet `pinCode` would hold 266.

Myth vs fact

Myth

A final array or object can't be changed.

Fact

Only the variable is frozen; it can't point at something else. The object's contents can still change unless the object itself is immutable.

Myth

010 is ten with a decorative zero.

Fact

A leading zero makes it octal, so 010 is 8. 08 and 09 don't even compile.

Myth

long big = 3000000000; works because long is big enough.

Fact

The literal itself is an int and is checked before assignment; it's too large. Write 3000000000L.

Myth

final makes code faster.

Fact

For locals, final is a compile-time check only and the bytecode is the same. The real performance effect comes from compile-time constants, which are folded and inlined.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Compile-time constants (JLS 4.12.4 and 15.29) are inlined into other classes at compile time. If class B uses A.LIMIT and you change LIMIT and recompile only A, B keeps the old value until it is recompiled too. Library authors avoid exposing values that may change as public static final constants, or initialise them through a method call to stop the inlining.

  • ▸

    In the class file a constant field carries a ConstantValue attribute, and the JVM sets it when the class is prepared, before any static initialiser runs. Non-constant static final fields, such as static final List<String> NAMES = List.of(...), are set in <clinit>, the class's static initialiser (Topic 14 covers class loading).

  • ▸

    The JIT treats static final fields as truly constant and folds them into compiled code; it does not trust ordinary instance final fields the same way, because reflection and deserialization can change them (records and hidden classes are exceptions in recent JDKs).

  • ▸

    Effectively final: a local that is never reassigned is treated as final for lambdas and inner classes even without the keyword (Java 8). Writing final makes the intent explicit and lets the compiler enforce it.

Remember this

  1. 1

    final on a variable means assign exactly once. final int lives = 3; can be read as often as you like, but lives = 4; is a compile error. A blank final is declared without a value and assigned later, exactly once on every path; the same definite-assignment rules from Topic 1.1 check it.

  2. 2

    A constant is a static final field of a primitive type or String set to a fixed value: static final int MAX_PLAYERS = 4;. By convention its name is UPPER_SNAKE_CASE. Constants replace magic numbers: a bare 0.18 in a formula tells the reader nothing, GST_RATE tells them everything, and changing the rate later means editing one line. (static is explained in Topic 4.6; for now read it as 'belongs to the class, one copy'.)

  3. 3

    Integer literals are int by default; add L for long (3_000_000_000L). Write them in four bases: decimal 255, hexadecimal 0xFF, binary 0b1111_1111 (Java 7) and octal with a leading zero, 0377. That last one is a trap: 010 is 8, not 10, and 09 doesn't compile. Underscores (Java 7) may separate digits for readability, 1_000_000, but not at the start or end of a number or next to the decimal point.

  4. 4

    Floating-point literals are double by default: 2.5, .5, 5., and scientific 1e3 (1000.0) or 1.5e-3 (0.0015). Add f for float (2.5f); float f = 2.5; doesn't compile because a double can't be narrowed silently (Topic 1.4). The suffix d is allowed but redundant.

  5. 5

    Other literals: char in single quotes ('A', '\n', '\u0041', Topic 1.5), boolean (true, false), String in double quotes (Topic 1.10) and text blocks in triple quotes (Java 15, Topic 6.5), and null, the literal for 'no object'. Note that true, false and null are literals, not keywords, but you still can't use them as names.

  6. 6

    final freezes the variable, not the object. final int[] scores = {10, 20}; can't be pointed at another array, but scores[0] = 99; is allowed. For truly unchangeable data you need immutable objects (Topic 4.11). When a final primitive or String is initialised with a constant expression, the compiler treats it as a compile-time constant and pastes its value directly wherever it's used.

Explain it without notes

01

What does final mean for a local variable, a static field and a reference to an object?

02

Why do teams replace magic numbers with named constants? Give an example.

03

What are the types of 42, 42L, 4.2, 4.2f, '4' and "4", and why does 010 print 8?

04

What is a compile-time constant, and what surprising effect can it have when another class uses it?

Practice

01

Define constants SECONDS_PER_MINUTE, MINUTES_PER_HOUR and HOURS_PER_DAY, compute SECONDS_PER_WEEK from them as another constant, and print it.

02

Print the number 100 written as a decimal, hex (0x64), octal (0144) and binary (0b110_0100) literal, and check all four are equal with ==.

03

Using a final blank variable grade, assign 'A' if marks >= 90, 'B' if marks >= 75, otherwise 'C', for marks = 82, and print it.

Trade-offs

  • ↔

    Marking every local final documents intent and prevents accidental reassignment, but adds noise; many teams use it for fields and important values and rely on 'effectively final' elsewhere.

  • ↔

    public static final constants are fast and simple, but they are inlined into other classes; values that may change between releases are safer behind a method or in configuration.

  • ↔

    A group of int constants is compact, but any int can be passed where one was expected; an enum gives type safety and names at a small memory cost.

Done when you can

  • I can use final for locals, blank finals and static final constants, and fix the related compile errors.

  • I name constants in UPPER_SNAKE_CASE and replace magic numbers with them.

  • I can write and read integer literals in decimal, hex, octal and binary, with underscores and the L suffix.

  • I know the leading-zero octal trap and store codes like PINs as Strings.

  • I know the default types of number literals and when f, L are needed.

  • I can explain that final freezes the variable, not the object, and what a compile-time constant is.