The final keyword | CodeGuru

The final keyword

Bruce Eckel’s Thinking in Java Contents | Prev | Next The final keyword has slightly different meanings depending on the context, but in general it says “This cannot be changed.” You might want to prevent changes for two reasons: design or efficiency. Because these two reasons are quite different, it’s possible to misuse the final […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
7 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

The


final
keyword has slightly different meanings depending on the context, but in
general it says “This cannot be changed.” You might want to prevent
changes for two reasons: design or efficiency. Because these two reasons are
quite different, it’s possible to misuse the
final
keyword.

The


following sections discuss the three places where


final

can be used: for data, methods and for a class.


Final
data

Many


programming languages have a way to tell the compiler that a piece of data is


“constant.” A constant is useful for two reasons:

  1. It
    can be a compile-time
    constant

    that won’t ever change.
  2. It
    can be a value initialized at run-time
    that you don’t want changed.

In


the case of a compile-time constant the compiler is allowed to


“fold” the

constant
value into any calculations in which it’s used; that is, the calculation
can be performed at compile time, eliminating some run-time overhead. In Java,
these sorts of constants must be
primitives
and are expressed using the
final
keyword. A value must be given at the time of definition of such a constant.

A


field that is both

static
and
final
has only one piece of storage that cannot be changed.

When


using

final
with object handles rather than primitives the meaning gets a bit confusing.
With a primitive,
final
makes the
value
a constant, but with an object handle,
final
makes the handle a constant. The handle must be initialized to an object at the
point of declaration, and the handle can never be changed to point to another
object. However, the object can be modified; Java does not provide a way to
make any arbitrary object a constant. (You can, however, write your class so
that objects have the effect of being constant.) This restriction includes
arrays, which are also objects.

Here’s


an example that demonstrates


final

fields:

//: FinalData.java
// The effect of final on fields
 
class Value {
  int i = 1;
}
 
public class FinalData {
  // Can be compile-time constants
  final int i1 = 9;
  static final int I2 = 99;
  // Typical public constant:
  public static final int I3 = 39;
  // Cannot be compile-time constants:
  final int i4 = (int)(Math.random()*20);
  static final int i5 = (int)(Math.random()*20);
 
  Value v1 = new Value();
  final Value v2 = new Value();
  static final Value v3 = new Value();
  //! final Value v4; // Pre-Java 1.1 Error: 
                      // no initializer
  // Arrays:
  final int[] a = { 1, 2, 3, 4, 5, 6 };
 
  public void print(String id) {
    System.out.println(
      id + ": " + "i4 = " + i4 +
      ", i5 = " + i5);
  }
  public static void main(String[] args) {
    FinalData fd1 = new FinalData();
    //! fd1.i1++; // Error: can't change value
    fd1.v2.i++; // Object isn't constant!
    fd1.v1 = new Value(); // OK -- not final
    for(int i = 0; i < fd1.a.length; i++)
      fd1.a[i]++; // Object isn't constant!
    //! fd1.v2 = new Value(); // Error: Can't 
    //! fd1.v3 = new Value(); // change handle
    //! fd1.a = new int[3];
 
    fd1.print("fd1");
    System.out.println("Creating new FinalData");
    FinalData fd2 = new FinalData();
    fd1.print("fd1");
    fd2.print("fd2");
  }
} ///:~ 

Since


i1

and


I2

are


final

primitives with compile-time values, they can both be used as compile-time


constants and are not different in any important way.


I3

is the more typical way you’ll see such constants defined:


public

so they’re usable outside the package,


static

to emphasize that there’s only one, and


final

to say that it’s a constant. Note that

final
static

primitives with constant initial values (that is, compile-time constants) are
named with all capitals by convention. Also note that
i5
cannot be known at compile time, so it is not capitalized.

Just


because something is


final

doesn’t mean that its value is known at compile-time. This is


demonstrated by initializing


i4

and


i5

at run-time using randomly generated numbers. This portion of the example also


shows the difference between making a


final

value


static

or non-


static

.


This difference shows up only when the values are initialized at run-time,


since the compile-time values are treated the same by the compiler. (And


presumably optimized out of existence.) The difference is shown in the output


from one run:

fd1: i4 = 15, i5 = 9
Creating new FinalData
fd1: i4 = 15, i5 = 9
fd2: i4 = 10, i5 = 9

Note


that the values of


i4

for


fd1

and


fd2

are unique, but the value for


i5

is not changed by creating the second


FinalData

object. That’s because it’s


static

and is initialized once upon loading and not each time a new object is created.

The


variables


v1

through


v4

demonstrate the meaning of a


final

handle. As you can see in


main( )

,


just because


v2

is


final

doesn’t mean that you can’t change its value. However, you cannot


re-bind


v2

to a new object, precisely because it’s


final

.


That’s what


final

means for a handle. You can also see the same meaning holds true for an array,


which is just another kind of handle. (There is know way that I know of to make


the array handles themselves


final

.)


Making handles


final

seems less useful than making primitives


final

.


Blank
finals

Java


1.1


allows the creation of
blank
finals
,
which are fields that are declared as
final
but are not given an initialization value. In all cases, the blank final
must
be initialized before it is used, and the compiler ensures this. However, blank
finals provide much more flexibility in the use of the
final
keyword since, for example, a
final
field inside a class can now be different for each object and yet it retains
its immutable quality. Here’s an example:
//: BlankFinal.java
// "Blank" final data members
 
class Poppet { }
 
class BlankFinal {
  final int i = 0; // Initialized final
  final int j; // Blank final
  final Poppet p; // Blank final handle
  // Blank finals MUST be initialized
  // in the constructor:
  BlankFinal() {
    j = 1; // Initialize blank final
    p = new Poppet();
  }
  BlankFinal(int x) {
    j = x; // Initialize blank final
    p = new Poppet();
  }
  public static void main(String[] args) {
    BlankFinal bf = new BlankFinal();
  }
} ///:~ 

You’re


forced to perform assignments to finals either with an expression at the point


of definition of the field or in every constructor. This way it’s


guaranteed that the final field is always initialized before use.


Final
arguments

Java


1.1


allows you to make
arguments
final
by declaring them as such in the argument list. This means that inside the
method you cannot change what the argument handle points to:
//: FinalArguments.java
// Using "final" with method arguments
 
class Gizmo {
  public void spin() {}
}
 
public class FinalArguments {
  void with(final Gizmo g) {
    //! g = new Gizmo(); // Illegal -- g is final
    g.spin();
  }
  void without(Gizmo g) {
    g = new Gizmo(); // OK -- g not final
    g.spin();
  }
  // void f(final int i) { i++; } // Can't change
  // You can only read from a final primitive:
  int g(final int i) { return i + 1; }
  public static void main(String[] args) {
    FinalArguments bf = new FinalArguments();
    bf.without(null);
    bf.with(null);
  }
} ///:~ 

Note


that you can still assign a


null

handle to an argument that’s final without the compiler catching it, just


like you can with a non-final argument.

The


methods


f( )

and


g( )

show what happens when primitive arguments are


final

:


you can only read the argument, but you can

t change it.


Final
methods

There


are two reasons for

final
methods. The first is to put a “lock” on the method to prevent any
inheriting class from changing its meaning. This is done for design reasons
when you want to make sure that a method’s behavior is retained during
inheritance and cannot be overridden.

The


second reason for


final

methods is efficiency. If you make a method


final

,


you are allowing the compiler to turn any calls to that method into

inline
calls. When the compiler sees a
final
method call it can (at its discretion) skip the normal approach of inserting
code to perform the method call mechanism (push arguments on the stack, hop
over to the method code and execute it, hop back and clean off the stack
arguments, and deal with the return value) and instead replace the method call
with a copy of the actual code in the method body. This eliminates the overhead
of the method call. Of course, if a method is big, then your code begins to
bloat and you probably won’t see any performance gains from inlining
since any improvements will be dwarfed by the amount of time spent inside the
method. It is implied that the Java compiler is able to detect these situations
and choose wisely whether to inline a
final
method. However, it’s better to not trust that the compiler is able to do
this and make a method
final
only if it’s quite small or if you want to explicitly prevent overriding.

Any


private
methods in a class are implicitly
final.
Because you can’t access a
private
method,
you can’t override it (the compiler gives an error message if you try).
You can add the
final
specifier to a
private
method but it doesn’t give that method any extra meaning.

Advertisement

Final
classes

When


you say that an entire class is


final

(by preceding its definition with the


final

keyword), you state that you don’t want to inherit from this class or


allow anyone else to do so. In other words, for some reason the design of your


class is such that there is never a need to make any changes, or for safety or


security reasons you don’t want subclassing. Alternatively, you might be


dealing with an efficiency issue and you want to make sure that any activity


involved with objects of this class is as efficient as possible.

//: Jurassic.java
// Making an entire class final
 
class SmallBrain {}
 
final class Dinosaur {
  int i = 7;
  int j = 1;
  SmallBrain x = new SmallBrain();
  void f() {}
}
 
//! class Further extends Dinosaur {}
// error: Cannot extend final class 'Dinosaur'
 
public class Jurassic {
  public static void main(String[] args) {
    Dinosaur n = new Dinosaur();
    n.f();
    n.i = 40;
    n.j++;
  }
} ///:~ 

Note


that the data members can be


final

or not, as you choose. The same rules apply to


final

for data members regardless of whether the class is defined as


final

.


Defining the class as


final

simply prevents inheritance – nothing more. However, because it prevents

inheritance
all methods in a
final
class are implicitly
final,
since there’s no way to override them. So the compiler has the same
efficiency options as it does if you explicitly declare a method
final.

You


can add the


final

specifier to a method in a


final

class, but it doesn’t add any meaning.


Final
caution

It


can seem to be sensible to make a method


final

while you’re designing a class. You might feel that

efficiency
is very important when using your class and that no one could possibly want to
override your methods anyway. Sometimes this is true.

But


be careful with your assumptions. In general, it’s difficult to


anticipate how a class can be reused, especially a general-purpose class. If


you define a method as


final

you might prevent the possibility of reusing your class through inheritance in


some other programmer’s project simply because you couldn’t imagine


it being used that way.

The


standard Java library is a good example of this. In particular, the


Vector

class is commonly used and might be even more useful if, in the name of


efficiency, all the methods hadn’t been made


final

.


It’s easily conceivable that you might want to inherit and override with


such a fundamentally useful class, but the designers somehow decided this


wasn’t appropriate. This is ironic for two reasons. First,


Stack

is


inherited from


Vector

,


which says that a


Stack
is

a


Vector

,


which isn’t really true. Second, many of the most important methods of


Vector

,


such as


addElement( )

and


elementAt( )

are


synchronized

,


which as you will see in Chapter 14 incurs a significant performance overhead


that probably wipes out any gains provided by


final

.


This lends credence to the theory that programmers are consistently bad at


guessing where optimizations should occur. It’s just too bad that such a


clumsy design made it into the standard library where we must all cope with it.

It’s


also interesting to note that


Hashtable

,


another important standard library class, does


not

have any


final

methods. As mentioned elsewhere in this book, it’s quite obvious that


some classes were designed by completely different people than others. (Notice


the brevity of the method names in


Hashtable

compared to those in


Vector

.)


This is precisely the sort of thing that should


not

be obvious to consumers of a class library. When things are inconsistent it


just makes more work for the user. Yet another paean to the value of design and


code walkthroughs.


Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.