Learn Java

Lesson 26Phase 3: Abstraction and design

The static and final keywords

Members that belong to the class not the object, values that never change, and constants done right.

By Updated 55 min

  • static fields
  • static methods
  • final variables
  • final methods
  • final classes
  • constants

Almost everything you have built so far belongs to one object. Each customer has its own balance, each box its own width. Some facts are not about any single object, though. How many customers exist? What tax rate does everyone pay? This lesson is about two small keywords for questions like these: static, for what belongs to the class itself, and final, for what must never change.

By the end of this lesson
  • Explain what it means for a field or method to belong to the class instead of to one object.
  • Use a static field to share one value across every object, such as a count of how many exist.
  • Write a static method, call it on the class, and explain why it cannot use this or the instance fields.
  • Say why main has been marked static all along.
  • Put final on a local variable, a field, a method and a class, and say what each one forbids.
  • Write a class constant in its standard form, public static final.
From before
You can write a class with fields and methods, build objects with a constructor, and you know that this points at the object a method runs on. You have also met inheritance with extends and overriding. This lesson adds two modifiers, words you put in front of a declaration the way you already put public or private there. One changes who owns a member; the other says whether it can change.

A question no single object can answer#

Picture a small game. Every time the player spawns a goblin, a new Goblin object is born. The game wants to show how many goblins have been spawned so far. Each goblin knows its own id, but no goblin knows the total. The total is not a fact about any one goblin; it is a fact about goblins as a whole.

With only the tools you have, you might add a field count to Goblin. That gives every goblin its own count, and each one would hold the number 1. What you need is a value that exists once, next to the class itself, that every goblin reads and writes.

Key idea

Some data belongs to the class, not to any object. A count of how many objects exist is the classic example: one number, shared by all of them, owned by none of them.

A static field: one box, shared by everyone#

Marking a field static says: this field belongs to the class, and there is exactly one of it, however many objects you make. Every object shares that one box. So Goblin gets a static counter, raised once inside the constructor. The constructor runs once for each new, so the count goes up by one for every goblin born.

Main.java
1public class Main {
2 public static void main(String[] args) {
3 Goblin g1 = new Goblin();
4 Goblin g2 = new Goblin();
5 Goblin g3 = new Goblin();
6
7 System.out.println("Goblins spawned: " + Goblin.count);
8 System.out.println("g2 is goblin number " + g2.id);
9 }
10}
11
12class Goblin {
13 static int count = 0;
14 int id;
15
16 Goblin() {
17 count = count + 1;
18 this.id = count;
19 }
20}
Prints
›Goblins spawned: 3
›g2 is goblin number 2

Step through it and read the narration. The first new Goblin() runs the constructor: count goes from 0 to 1, and that goblin saves 1 as its own id. The second new Goblin() finds the same shared count, now 1, raises it to 2 and saves 2. The third raises it to 3. So the program prints Goblins spawned: 3 and g2 is goblin number 2.

Now look at the memory view. Each goblin object shows an id, and none of them shows a count. The count is not stored inside any goblin. It belongs to the class, so the memory view gives it a box of its own under Static fields, labeled Goblin: one count, shared by every goblin.

Read it on the class, not on an object
The program writes Goblin.count, with the class name, not g2.count. Because the field belongs to the class, the natural way to reach it is through the class. Java does accept g2.count, but it misleads the reader: it looks as if g2 had its own count, and it does not. Use the class name, so the sharing is plain to see.

Compare that with id, an ordinary field with no static. Each object made from a class is called an instance of it, so a field that every object gets its own copy of is an instance field. Here g1 holds 1, g2 holds 2 and g3 holds 3: three separate boxes, against one shared count.

Quick check
Goblin has static int count and int id. After three goblins have been created, how many int boxes do these two fields give you in memory?
Instance member (no static)
Static member (static)
Belongs to each object. One per object.
Belongs to the class. Exactly one.
Made by new, along with its object.
Made once, when Java sets up the class.
Reached through an object: g2.id.
Reached through the class: Goblin.count.
Different objects can hold different values.
Every object sees the same value.
Inside a method, this is the object it belongs to.
There is no this: it is not about any object.

Here two clickers count their own clicks, and the class keeps a total of every click. Decide what the last line prints before you run it.

Predict the outputClicks.java
1public class Main {
2 public static void main(String[] args) {
3 Clicker a = new Clicker();
4 Clicker b = new Clicker();
5
6 a.click();
7 a.click();
8 b.click();
9
10 System.out.println(a.mine + " " + b.mine + " " + Clicker.total);
11 }
12}
13
14class Clicker {
15 static int total = 0;
16 int mine = 0;
17
18 void click() {
19 mine = mine + 1;
20 total = total + 1;
21 }
22}

a clicks twice and b clicks once. What does the last line print?

It prints 2 1 3. Each click raises that clicker's own mine and the one shared total. a's mine reaches 2 and b's reaches 1, while total counts every click from both and reaches 3.

A static method: a job the class does, with no object in hand#

Methods can be static too. A static method belongs to the class, so you call it on the class, not on an object. You have used static methods already: Math.max(7, 3) calls the static method max on the Math class. You never wrote new Math(), because max needs no object. It takes two numbers and gives one back.

Here is a small utility class of your own. Picking the bigger of two numbers is a job, not a property of any object, so bigger is static, and you call it as MathUtil.bigger(7, 3).

Main.java
1public class Main {
2 public static void main(String[] args) {
3 int b = MathUtil.bigger(7, 3);
4 System.out.println("Bigger of 7 and 3: " + b);
5 System.out.println("Bigger of 2 and 9: " + MathUtil.bigger(2, 9));
6 }
7}
8
9class MathUtil {
10 static int bigger(int a, int b) {
11 if (a > b) {
12 return a;
13 }
14 return b;
15 }
16}
Prints
›Bigger of 7 and 3: 7
›Bigger of 2 and 9: 9

It prints Bigger of 7 and 3: 7 and Bigger of 2 and 9: 9, and no object was ever made: there is no new MathUtil() anywhere. The class name and the method name are enough, because the answer depends only on the two arguments. That is the mark of a good static method: inputs in, an answer out, no object needed.

Static helpers in Main#

A static method can also live in Main, next to main. Inside the same class you call it by its bare name, without the class name in front. That is how you split a long main into small helper methods:

Helpers.java
1public class Main {
2 public static void main(String[] args) {
3 System.out.println(square(4));
4 System.out.println(square(square(3)));
5 System.out.println(Math.max(7, 3) + " " + Math.abs(-5));
6 }
7
8 static int square(int n) {
9 return n * n;
10 }
11}
Prints
›16
›81
›7 5

It prints 16, 81 and 7 5. Inside Main, square(4) means Main.square(4). The second line calls squaretwice: the inner call gives 9, and the outer call squares that to 81. The last line uses two static methods of Java's Math class, max and abs.

Put the lines in orderAverage.java
Put the lines in order so that main hands the scores to the static helper average, which adds them up and divides by how many there are. The program should print Average: 81.66666666666667.

Tap a line, then tap where it should go. Or use the arrows.

The rule that surprises people#

A static method runs without an object, so inside it there is no this. That leads straight to the rule: a static method cannot use an instance field, or call an instance method, by its bare name. Those belong to some object, and the static method does not have one. Both mistakes below are common, and javac rejects both:

Broken.java (does not compile)
read along
1public class Main {
2 public static void main(String[] args) {
3 greet(); // error: non-static method greet() cannot be referenced from a static context
4 System.out.println(Thing.report());
5 }
6
7 void greet() {
8 System.out.println("Hello");
9 }
10}
11
12class Thing {
13 int size = 5;
14
15 static String report() {
16 return "size is " + size; // error: non-static variable size cannot be referenced from a static context
17 }
18}

Whose size should report read? There could be ten Thingobjects, or none at all. The fix depends on what you mean. If the method needs one object's data, make it an instance method and call it on an object, as in new Thing().report(). If it does not, pass the data in as a parameter and keep the method static. For greet, the usual fix is to declare it static void greet().

The error you will meet most often
non-static method ... cannot be referenced from a static context nearly always means you wrote a helper method in Main and forgot the word static. main is static, so every method it calls by bare name in Main must be static too.
Quick check
Which of these methods is the best fit for static?

At last: why main is static#

You have typed public static void main(String[] args) at the top of every program since the first lesson, as a fixed phrase. Now you can read the static in it.

When a program starts, no objects exist yet. Making them is your program's job, and your program has not run. The java launcher needs a method it can call at that moment. A static main belongs to the class, so the launcher can call Main.main(...) as soon as the class is set up, with no object at all. From inside main, your code then makes its first objects.

It is also why main could not call the instance method greet above: main is static, so it has no this to lend.

Key idea

main is the door the program comes in through, and no objects exist when it is first called. A static main can be called on the class alone, before anything else exists.

final: a variable that is set once#

The second keyword is final. On a variable, final means it can be given a value once, and after that never another. Assign it a second time and the compiler stops you.

Why bother? If a value should never change, saying so lets the compiler guard it: a wrong reassignment becomes a compile error instead of a quiet bug. It also tells anyone reading the code that this name stands for one settled value.

Lanes.java (does not compile)
read along
1public class Main {
2 public static void main(String[] args) {
3 final int lanes = 4;
4 System.out.println("Lanes: " + lanes);
5
6 lanes = 5; // error: cannot assign a value to final variable lanes
7 }
8}

Notice what this means: the program never runs, so not even the first println happens. javac checks the whole file before anything runs, and it refuses the file because of line 6. A final variable is protected before the program even starts.

Set once, not necessarily straight away#

A final variable does not need its value on the line where it is declared. Java only insists that it gets exactly one value before anything reads it. That lets you pick the value with an if/else. Step through this and watch result in the memory view: it shows no value yet until one of the branches stores a value in it.

Grade.java
1public class Main {
2 public static void main(String[] args) {
3 int score = 72;
4
5 final String result;
6 if (score >= 50) {
7 result = "pass";
8 } else {
9 result = "fail";
10 }
11
12 System.out.println(score + " is a " + result);
13 }
14}
Prints
›72 is a pass

It prints 72 is a pass. A final variable declared without a value is called a blank final. javac checks every path through the code: here each branch assigns result exactly once, so all is well. Without the else branch, the println line would not compile (variable result might not have been initialized), and a second assignment after the if/else would not compile either (variable result might already have been assigned).

final fields: set in the constructor, then fixed#

Fields can be final too. A final field must get its value by the time the constructor finishes, and it never changes after that. That suits facts about an object that are settled once it exists, like the number printed on a ticket.

Ticket.java
1public class Main {
2 public static void main(String[] args) {
3 Ticket first = new Ticket("Ana");
4 Ticket second = new Ticket("Ben");
5
6 System.out.println(first.holder + " has ticket " + first.number);
7 System.out.println(second.holder + " has ticket " + second.number);
8 System.out.println("Tickets issued: " + Ticket.issued);
9 }
10}
11
12class Ticket {
13 static int issued = 0;
14
15 final String holder;
16 final int number;
17
18 Ticket(String holder) {
19 issued = issued + 1;
20 this.holder = holder;
21 this.number = issued;
22 }
23}
Prints
›Ana has ticket 1
›Ben has ticket 2
›Tickets issued: 2

It prints Ana has ticket 1, Ben has ticket 2 and Tickets issued: 2. issued is static: one counter for the whole ticket machine. holder and number are final instance fields: each ticket has its own, set once in the constructor. A line such as first.number = 7; anywhere else would not compile. If the constructor forgot to set number, javac would point at the end of the constructor with variable number might not have been initialized.

ExerciseMembers.java
A library hands out membership numbers starting at 1001. Finish the constructor so that each new member takes the next number and the shared nextNumber then moves on by one. Leave main as it is.

final locks the variable, not the object#

Be precise about what final locks. It locks the variable: a final variable can never point at a different object. It does not freeze the object the variable points at. If that object can change, it still can. Decide what this prints.

Predict the outputGuests.java
1import java.util.ArrayList;
2
3public class Main {
4 public static void main(String[] args) {
5 final ArrayList<String> guests = new ArrayList<>();
6 guests.add("Ana");
7 guests.add("Ben");
8 guests.remove("Ana");
9
10 System.out.println(guests);
11 }
12}

guests is final. What happens?

It prints [Ben]. The list changed three times through a final variable, and Java allowed all three: add and remove change the list object, not the variable. What final forbids is pointing guests at a different list: guests = new ArrayList<>(); would not compile, with cannot assign a value to final variable guests. Arrays work the same way: the elements of a final int[] can change, but the variable cannot be pointed at another array.

Key idea

A final variable gets one value and keeps it. When the variable refers to an object, that value is the reference: the variable always points at the same object, while the object itself may still change.

final on methods and classes#

The same word has two more uses, and they read the same way: this cannot change. On a method, final means no subclass can override it. On a class, final means no class can extend it. Both say: how this works is decided, and nobody gets to redefine it underneath me.

  • A final methodcannot be overridden. You use it when a subclass must not be able to swap out a method's behavior, for example the checks inside a bank account's deposit. Subclasses can still add methods and override the others.
  • A final class cannot be extended at all. The best-known example is String: it is a final class, so nobody can write class MyString extends String.
Locked.java (does not compile)
read along
1class Account {
2 final void deposit(int amount) {
3 // checks the amount, then adds it to the balance
4 }
5}
6
7class SneakyAccount extends Account {
8 void deposit(int amount) { // error: deposit(int) in SneakyAccount cannot override deposit(int) in Account
9 } // overridden method is final
10}
11
12final class Receipt { }
13
14class FancyReceipt extends Receipt { } // error: cannot inherit from final Receipt
15
16class MyString extends String { } // error: cannot inherit from final String
Use final where a guarantee matters
final on a method or class is a message to future readers, including future you: this is settled, build on it, do not redefine it. Reach for it when that guarantee matters. A class that must never be extended is a different thing from a class that nobody has extended yet.
Quick check
A class is declared final class Receipt { void print() { ... } }. Which of these can you still do?

static final together: the class constant#

Now combine the two. A value that belongs to the class (so static) and never changes (so final) is a class constant. It is one of the most common patterns in Java: you declare a shared, named, unchanging value once, and the whole program reads it through the class. The usual form is public static final, with a name in UPPER_CASE, words joined by underscores. That naming is only for constants like these; an ordinary final local variable keeps a normal name, like lanes above.

You have used one already: Math.PI is a public static final double on the Math class. Here is one of your own. TimeUtil holds the number of seconds in a day, and a static method uses it.

Main.java
1public class Main {
2 public static void main(String[] args) {
3 System.out.println("Seconds in a day: " + TimeUtil.SECONDS_PER_DAY);
4 System.out.println("Two days in seconds: " + TimeUtil.daysToSeconds(2));
5 }
6}
7
8class TimeUtil {
9 public static final int SECONDS_PER_DAY = 86400;
10
11 static int daysToSeconds(int days) {
12 return days * SECONDS_PER_DAY;
13 }
14}
Prints
›Seconds in a day: 86400
›Two days in seconds: 172800

It prints Seconds in a day: 86400 and Two days in seconds: 172800. SECONDS_PER_DAY is declared once and used both from outside the class, in main, and inside it, in daysToSeconds. If a stray line ever tried TimeUtil.SECONDS_PER_DAY = 99;, javac would refuse it with cannot assign a value to static final variable SECONDS_PER_DAY.

Constants work just as well inside ordinary instance methods. A shopping cart keeps its own subtotal as an instance field, while the tax rate, the same for every cart, lives once as a static final constant.

Main.java
1public class Main {
2 public static void main(String[] args) {
3 Cart c = new Cart();
4 c.subtotal = 100.0;
5
6 System.out.println("Tax rate (shared): " + Cart.TAX_RATE);
7 System.out.println("Total with tax: " + c.total());
8 }
9}
10
11class Cart {
12 static final double TAX_RATE = 0.1;
13 double subtotal;
14
15 double total() {
16 return subtotal + subtotal * TAX_RATE;
17 }
18}
Prints
›Tax rate (shared): 0.1
›Total with tax: 110.0

Step through it and notice the two kinds of data side by side. subtotalis an instance field: this cart's own number, 100.0. TAX_RATE is a class constant: one value, 0.1, shared by every cart there will ever be. total() reads both and returns 110.0. Give the cart a different subtotal and the total changes; the tax rate does not, because it does not belong to this cart.

Key idea

public static final NAME is the standard shape of a constant: static so there is one, shared by the whole class, and final so it can never be reassigned. The capital letters let everyone see at a glance that it never changes.

ExerciseUnits.java
Finish the Units class. Add a class constant CM_PER_INCH with the value 2.54, then make toCentimetres and toInches use it. main already calls both methods on the class.
Where these keywords turn up next
You will see both keywords often from here on. Next, inner classes puts static in front of a class declared inside another class, with the same meaning: it belongs to the outer class, not to one of its objects. In enums and lambdas, every constant of an enum turns out to be a public static final object. And records make every field final for you.

Try it

Answer each one in your head or on paper first, then open the answer.

1
In one sentence each, say what static does to a field, and what an ordinary instance field is by contrast. Use the goblin count if it helps.
2
In the goblin program, after g1, g2 and g3 are created, what does Goblin.count print, and what does g3.id print? Why are they not the same kind of number?
3
A teammate writes a static method report() in a class that has an instance field size, and the method tries to return "size is " + size. The compiler refuses. Why, in plain words?
4
Explain in one or two sentences why main is marked static.
5
For each, say what final forbids: (a) final int speed = 5; inside a method, (b) a method marked final, (c) a class marked final.
6
Why is public static final double TAX_RATE = 0.1; the right shape for a shared tax rate, and what does each of the three words add?

Practice

Work through these on your own. Each one builds on this page.

1
Build a BankAccount class with an instance field balance and a static field accountsOpened that starts at 0. Raise accountsOpened by one in the constructor. In main, open three accounts with different balances, print each balance, and finally print BankAccount.accountsOpened. Check that the shared count reads 3 while each balance stays its own.
2
Write a utility class Geometry with a constant static final double PI = 3.14159; and a static method circleArea(double r) that returns PI * r * r. Print the area for a radius of 2 and a radius of 5 by calling Geometry.circleArea(...), and notice that you never write new Geometry(). Then switch to Java's own Math.PI and see how the answers change.
3
Write a static helper in Main, static boolean isLeapYear(int year). A year is a leap year when it divides by 4, except for years that divide by 100, which are leap years only if they also divide by 400. Call it from main for 2024, 1900, 2000 and 2025; you should get true false true false.
4
On paper, decide for each of these whether it should be an instance member or a static member, and write one sentence saying why: (a) a Dog's name, (b) the total number of dogs created, (c) a method that converts inches to centimetres, (d) the number of legs every dog has (always 4).
5
In two or three sentences, explain why making String a final class was a deliberate safety decision. What could go wrong if anyone were allowed to extend String and override its methods?

Further reading#

Short guides on this topic

Quick answers to what people search for about it, with code to run.

Spotted a mistake, or a part that did not make sense? Report it by email