L'usage d'un même mot-clé, final, permet de limiter les possibilités d'extension (d'une classe), de redéfinition (d'une méthode) et d'affectation (à une variable). Une classe, méthode ou variable, dont la définition est précédée de ce mot-clé, est dite finale.
Si une classe est finale, aucune classe ne peut en être dérivée :
final class A { ... } class B extends A { ... } // INTERDIT
Une méthode finale ne peut pas être redéfinie (ou, si elle est statique, elle ne peut pas être masquée) dans une classe dérivée :
class A { final void f() { ... } } class B extends A { void f() { ... } // INTERDIT }
Méthodes et classes finales sont utilisées dans un but de sécurité (un utilisateur de la classe ne peut pas modifier son comportement) et dans un but d'optimisation, puisque la détermination de la méthode invoquée peut être faite dès la compilation, sans que le mécanisme de liaison tardive soit mis en 3#3uvre.
Enfin, une variable (un champ, une variable locale ou un paramètre d'une méthode) finale ne peut pas être modifiée une fois qu'elle a été initialisée :
final double n = 3; n = 4; // INTERDIT
Une constante de classe est déclarée comme un champ static final :
static final double PI = 3.14;
Par exemple, l'expression System.out.println("Hello") est l'invocation de la méthode println de la constante (membre statique final) out (de type PrintStream) de la classe System. Les constantes sont souvent désignées par un nom en lettres majuscules (comme PI) :
static final int EST = 0; static final int NORD = 1; static final int OUEST = 2; static final int SUD = 3;
Ces constantes de type entier sont particulièrement utiles dans une instruction d'aiguillage (switch), mais ne modélisent pas correctement un type d'énumération, puisqu'on peut aussi bien écrire :
int direction = NORD; // correct direction = 59; // c'est où ?