Você pode encontrar-se na posição de pensar em finalizar um método estático, considerando o seguinte:
Tendo as seguintes classes:
class A {
static void ts() {
System.out.print("A");
}
}
class B extends A {
static void ts() {
System.out.print("B");
}
}
Agora, a maneira 'correta' de chamar esses métodos seria
A.ts();
B.ts();
o que resultaria, ABmas você também poderia chamar os métodos nas instâncias:
A a = new A();
a.ts();
B b = new B();
b.ts();
o que resultaria ABtambém.
Agora considere o seguinte:
A a = new B();
a.ts();
isso seria impresso A. Isso pode surpreendê-lo, pois você está realmente tendo um objeto de classe B. Mas como você está chamando a partir de uma referência do tipo A, ele chamará A.ts(). Você pode imprimir Bcom o seguinte código:
A a = new B();
((B)a).ts();
Nos dois casos, o objeto que você possui é realmente da classe B. Mas, dependendo do ponteiro que aponta para o objeto, você chamará o método de Aou deB .
Agora, digamos que você é o desenvolvedor da classe Ae deseja permitir a subclassificação. Mas você realmente deseja um método ts(), sempre que chamado, mesmo de uma subclasse, que é o que você deseja que ele faça e que não seja oculto por uma versão da subclasse. Então você pode fazer isso finale impedir que ele fique oculto na subclasse. E você pode ter certeza de que o código a seguir chamará o método da sua classe A:
B b = new B();
b.ts();
Ok, admitidamente, isso é de alguma forma construído, mas pode fazer sentido para alguns casos.
Você não deve chamar métodos estáticos nas instâncias, mas diretamente nas classes - então você não terá esse problema. Também o IntelliJ IDEA, por exemplo, mostrará um aviso, se você chamar um método estático em uma instância e também se você finalizar um método estático.