Static and Final in Java: Shared State, Constants and Override Rules with Runnable Examples
Learn what static and final control in Java by tracing a shared counter, class initialisation, final references, constants and inheritance errors.
KnowledgeGate Team
Exam prep & CS education

static and final often appear together, but they solve different problems. static decides that a member belongs to the class, while final restricts reassignment, overriding or inheritance according to where it appears. Static and final govern shared state, initialisation order and compiler errors.
Static and final in Java: build the modifier map first
Start by asking two different questions: who owns this member, and what change is forbidden?
Context |
|
|
|---|---|---|
Field | One class-level copy | Assigned once |
Method | Called without an implicit object or | Cannot be overridden |
Class | Allowed for a nested class, not a top-level class | Cannot be extended |
Local variable | Not allowed | Assigned once |
Parameter | Not allowed | Cannot be reassigned inside the method |
The modifiers are not opposites. static final int MAX_RETRIES = 3; declares one class-level value that cannot be reassigned after initialisation. A static initialiser also runs once when its class is initialised.
Static fields and methods: trace one shared counter
This program gives each support ticket its own final ticket number while one static field stores the next available number.
class Ticket {
static int nextNumber = 1001;
final int ticketNo;
final String topic;
Ticket(String topic) {
this.topic = topic;
this.ticketNo = nextNumber++;
}
static int issued() {
return nextNumber - 1001;
}
public static void main(String[] args) {
Ticket login = new Ticket("Login");
Ticket billing = new Ticket("Billing");
Ticket playback = new Ticket("Playback");
System.out.println(login.topic + " -> " + login.ticketNo);
System.out.println(billing.topic + " -> " + billing.ticketNo);
System.out.println(playback.topic + " -> " + playback.ticketNo);
System.out.println("Issued: " + Ticket.issued());
System.out.println("Next number: " + Ticket.nextNumber);
}
}The first constructor stores 1001 in the login ticket's ticketNo, then raises nextNumber to 1002. The billing ticket receives 1002, the playback ticket receives 1003, and the shared field finishes at 1004. The exact output is:
Login -> 1001
Billing -> 1002
Playback -> 1003
Issued: 3
Next number: 1004There are three ticketNo fields but only one nextNumber. issued() can read that shared field, but it cannot directly read topic or use this because no particular Ticket object is attached to a static method. Class-qualified access such as Ticket.issued() makes that ownership clear. Do not make every field static merely to silence a non-static access error.
Static initialisation order: prove what runs once
The following program separates class initialisation from work performed for each object. BASE is a blank static final field assigned exactly once.
class Config {
static final int BASE;
static int runs = 1;
static {
BASE = 40;
runs += 2;
System.out.println("static block: " + runs);
}
{
runs++;
System.out.println("instance block: " + runs);
}
Config() {
System.out.println("constructor: " + runs);
}
public static void main(String[] args) {
new Config();
new Config();
System.out.println("sum: " + (BASE + runs));
}
}Static field initialisers and static blocks execute in source order when this class is initialised. Here, runs becomes 1 + 2 = 3 before main. The first instance block changes it to 4, then the first constructor prints 4. The second pair prints 5, and the final calculation is BASE + runs = 40 + 5 = 45.
static block: 3
instance block: 4
constructor: 4
instance block: 5
constructor: 5
sum: 45The static block runs once. For each new object here, the instance initialiser runs before the constructor body, so both run twice.

Final variables, references and static final constants
final prevents another assignment to a variable. It does not automatically make the referenced object immutable.
final int[] marks = {70, 80};
marks[0] = 95;
System.out.println(java.util.Arrays.toString(marks)); // [95, 80]
// marks = new int[]{60, 70}; // compile-time errorThe array object can change, but marks cannot point to another array. The same distinction explains why String Handling in Java: Pool, Immutability, Interview Questions treats immutability as an object property, not a benefit automatically granted by a final reference.
An instance blank final such as ticketNo must be assigned once on every constructor path. A static blank final such as BASE may be assigned at declaration or in a static initialiser. A final parameter cannot be reassigned inside its method, although a mutable object passed through it may still change.
class CircleMath {
static final double PI = 3.14159;
static double area(double radius) {
return PI * radius * radius;
}
public static void main(String[] args) {
System.out.printf("%.2f%n", area(4));
}
}The calculation is area(4) = PI * 4 * 4 = 3.14159 * 16 = 50.26544, which %.2f prints as 50.27. Uppercase names are the convention for constants. A compile-time constant is narrower than every static final field: it must be a primitive or String initialised with a constant expression.
Final methods and final classes: control inheritance
A final method blocks overriding only that method. A final class blocks subclassing altogether.
class Report {
final int score() { return 80; }
}
class AdvancedReport extends Report {
// int score() { return 95; } // error: cannot override final method
}
final class AuditLog {}
// class ExtendedAuditLog extends AuditLog {} // error: cannot inheritnew AdvancedReport().score() still calls the inherited method and returns 80. An abstract final class or method is contradictory and illegal because an abstract declaration requires subclass implementation. If that inheritance vocabulary needs revision, use OOP for Teaching CS Exams: Classes and Inheritance before returning to these modifier rules.
Static and final mistakes: diagnose the compiler message
Consider static int x = 7; int y = 3; static int total() { return x + y; }. It fails because y belongs to an object. Make total() an instance method if it represents one object's state, or pass the required object explicitly. Changing y to static is correct only if the class should truly share it.
Next, final StringBuilder text = new StringBuilder("A"); text.append("B"); is legal and produces AB. Assigning a new StringBuilder to text is illegal. Again, mutation is not reassignment.
A blank final must be definitely assigned exactly once on every path. final int mode; if (args.length > 0) mode = 1; else mode = 0; is legal, but a later unconditional mode = 2; is not. Also avoid exposing a mutable collection as public static final: callers cannot replace its reference, but they can still change the shared contents.
How coding tests and interviews check static and final
Typical questions ask you to predict a shared counter, identify access from a static context, or separate final-reference mutation from reassignment. Justify each answer using ownership and assignment count.
Snippet | Result | Exact value or rule |
|---|---|---|
| Compiles |
|
| Compiles |
|
| Compiles | Prints |
| Fails | It attempts to reassign a final field |
These patterns test the rule behind the keyword. Ask which object owns the data, how many assignments occur, and whether inheritance is attempting an override.
Static and final in Java: the short version and next practice step
A static field has one shared class-level copy.
A static method has no implicit object.
A final variable or reference is assigned once.
A final method cannot be overridden.
A final class cannot be extended.
static final is the usual shape for a class constant, but it does not freeze a mutable object. As a quick exercise, change Ticket.nextNumber to 5001 and issued() to return nextNumber - 5001, then create four tickets. The sequence is 5001, 5002, 5003, 5004, so the fourth ticketNo is 5004, issued() returns 5005 - 5001 = 4, and the next shared value is 5005.
For guided concept and question practice, continue with Java Course: Concepts, MCQs and Coding Questions. Use the Complete Java Course when you want a basics-to-advanced route that includes OOP and application work. If you only need to compare Java with the wider programming and DSA catalogue, browse Coding & DSA Courses for Placements.
Keep learning

Java Multithreading Basics: Threads, Runnable and Safe Counters with Examples
Build two Java thread examples, trace a lost counter update exactly, and learn when to use synchronized, AtomicInteger, volatile or private state.

Polymorphism in Java: Overloading, Overriding and Runnable Examples
Learn how Java chooses overloaded methods at compile time and overridden methods at runtime. Trace two complete programs, common traps and practice targets.

Packages and Access Modifiers in Java: Visibility Rules with Runnable Examples
Learn how Java package boundaries change member visibility. Compile a complete example covering public, protected, package-private and private access.

OOP Basics in Java: Classes, Objects and the Four Pillars with Runnable Examples
Connect Java's OOP syntax to design decisions with a complete Account example. Trace two objects through validation, overriding and runtime dispatch to exact output.