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',trueor"hello". - final
- A modifier meaning 'can be assigned only once'. A
finalvariable can't be changed after it gets its value. - Constant
- A named value that never changes, usually a
static finalfield such asMAX_SIZE. - Magic number
- A bare number in code with no name explaining what it means, like
0.18or86400. - Blank final
- A
finalvariable declared without a value and assigned exactly once later. - Hexadecimal
- Base 16, written with
0x. Digits are 0–9 then A–F, so0xFFis 255. - Octal
- Base 8, written in Java with a leading
0.010means eight. - Binary
- Base 2, written with
0b, using only the digits 0 and 1.0b101is 5. - Compile-time constant
- A
finalprimitive orStringwhose 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.
final int lives = 3;
System.out.println(lives); // reading is fine
// lives = 4; // error: cannot assign a value to final variable lives02Blank 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.
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).
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
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 finalconstantsITEM_PRICE,GST_RATEandDELIVERY_FEEand check that the output is the same. Which version would you rather change next year? - 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
See the constant in the bytecode
Compile the last example and run
javap -c -constants Main. Look for86400: the field holds the folded number, and inmainthe compiler went further and joined the whole message into one constant String. There is no multiplication and no field read left.terminal$ javac Main.javajavap -c -constants Main | grep 86400── expected output ──static final int SECONDS_PER_DAY = 86400;3: ldc #15 // String seconds per day: 86400
Code & diagrams
Expected output
Ludo: 3 of 4 seats taken, 1 left
price with GST: 590.0
bonus: 50Expected output
255 four ways: 255 255 255 255
million: 1000000, people: 1428627663
0xFFFF_FFFF = -1
010 = 8
42 in binary: 101010
255 in hex: ffExpected output
1000.0 0.0015 0.5 2.5 3.141592
A A equal? true
ready: true, nobody: nullExpected output
[99, 20, 30]
Hi thereThis shows what the compiler does; it is not a reason to compare Strings with `==`. Use `equals` (Topic 1.10).
Expected output
seconds per day: 86400
constant + literal pooled? true
variable + literal pooled? falseBreak 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.
public class Main {
static final int MAX_PLAYERS = 4;
public static void main(String[] args) {
MAX_PLAYERS = 5;
System.out.println(MAX_PLAYERS);
}
}Break #2
Assign a blank final twice
Declare final int total;, then assign it on two lines.
public class Main {
public static void main(String[] args) {
final int total;
total = 10;
total = 20;
}
}Break #3
A leading zero on 09
Write a day number with a leading zero: int day = 09;.
public class Main {
public static void main(String[] args) {
int pinCode = 0412;
int day = 09;
System.out.println(pinCode + " " + day);
}
}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
BusesA.LIMITand you changeLIMITand recompile onlyA,Bkeeps the old value until it is recompiled too. Library authors avoid exposing values that may change aspublic static finalconstants, or initialise them through a method call to stop the inlining. - ▸
In the class file a constant field carries a
ConstantValueattribute, and the JVM sets it when the class is prepared, before any static initialiser runs. Non-constantstatic finalfields, such asstatic final List<String> NAMES = List.of(...), are set in<clinit>, the class's static initialiser (Topic 14 covers class loading). - ▸
The JIT treats
static finalfields as truly constant and folds them into compiled code; it does not trust ordinary instancefinalfields 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
finalfor lambdas and inner classes even without the keyword (Java 8). Writingfinalmakes the intent explicit and lets the compiler enforce it.
Remember this
- 1
finalon a variable means assign exactly once.final int lives = 3;can be read as often as you like, butlives = 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
A constant is a
static finalfield of a primitive type orStringset to a fixed value:static final int MAX_PLAYERS = 4;. By convention its name isUPPER_SNAKE_CASE. Constants replace magic numbers: a bare0.18in a formula tells the reader nothing,GST_RATEtells them everything, and changing the rate later means editing one line. (staticis explained in Topic 4.6; for now read it as 'belongs to the class, one copy'.) - 3
Integer literals are
intby default; addLforlong(3_000_000_000L). Write them in four bases: decimal255, hexadecimal0xFF, binary0b1111_1111(Java 7) and octal with a leading zero,0377. That last one is a trap:010is 8, not 10, and09doesn'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
Floating-point literals are
doubleby default:2.5,.5,5., and scientific1e3(1000.0) or1.5e-3(0.0015). Addfforfloat(2.5f);float f = 2.5;doesn't compile because adoublecan't be narrowed silently (Topic 1.4). The suffixdis allowed but redundant. - 5
Other literals:
charin single quotes ('A','\n','\u0041', Topic 1.5),boolean(true,false),Stringin double quotes (Topic 1.10) and text blocks in triple quotes (Java 15, Topic 6.5), andnull, the literal for 'no object'. Note thattrue,falseandnullare literals, not keywords, but you still can't use them as names. - 6
finalfreezes the variable, not the object.final int[] scores = {10, 20};can't be pointed at another array, butscores[0] = 99;is allowed. For truly unchangeable data you need immutable objects (Topic 4.11). When afinalprimitive orStringis 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
What does final mean for a local variable, a static field and a reference to an object?
Why do teams replace magic numbers with named constants? Give an example.
What are the types of 42, 42L, 4.2, 4.2f, '4' and "4", and why does 010 print 8?
What is a compile-time constant, and what surprising effect can it have when another class uses it?
Practice
Define constants SECONDS_PER_MINUTE, MINUTES_PER_HOUR and HOURS_PER_DAY, compute SECONDS_PER_WEEK from them as another constant, and print it.
Print the number 100 written as a decimal, hex (0x64), octal (0144) and binary (0b110_0100) literal, and check all four are equal with ==.
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
finaldocuments 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 finalconstants 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
intconstants is compact, but anyintcan be passed where one was expected; anenumgives type safety and names at a small memory cost.
Done when you can
I can use
finalfor locals, blank finals andstatic finalconstants, and fix the related compile errors.I name constants in
UPPER_SNAKE_CASEand replace magic numbers with them.I can write and read integer literals in decimal, hex, octal and binary, with underscores and the
Lsuffix.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,Lare needed.I can explain that
finalfreezes the variable, not the object, and what a compile-time constant is.