Il polimorfismo è legato solo e soltanto all'overriding dei metodi, esso consiste nella capacità degli oggetti di reagire in modo diverso a messaggi uguali in base alla loro posizione nella gerarchia di ereditarietà, pertanto deve essere sempre risolto a runtime ed il compilatore si occupa di generare codice che permetta appunto di "risolvere" il problema a runtime tramite il processo del "late binding" (pensa ad una variabile Supertype a cui viene associato un oggetto SubtypeA o SubtypeB a seconda dell'esito di una condizione if/else...il compilatore (la java virtual machine in questo caso) non potrà mai sapere tale esito a tempo di compilazione e creerà il codice adatto a risolvere il problema a runtime, ovviamente pagando qualcosina in tempo di esecuzione)...

Il polimorfismo legato all'overloading dei metodi è anche detto polimorfismo fittizio perchè la scelta del metodo da chiamare viene fatta a tempo di compilazione semplicemente guardando la firma del metodo (in JAVA in particolare la firma di un metodo non comprende il valore di ritorno ed una sottoclasse non può ridefinire un metodo di una superclasse con diverso valore di ritorno).

Ovviamente in JAVA dichiarando un metodo final ci assicuriamo che non possa essere sovrascritto nelle sottoclassi, ma non solo: siccome un metodo definito in una sottoclasse "nasconde" quelli delle classi antenate ci assicuriamo che sia proprio quel metodo ad essere eseguito, pertanto tale metodo non è più virtuale e viene "risolto" a tempo di compilazione, guadagnando qualcosina in efficienza (e avendo la sicurezza che tale metodo non può essere sovrascritto).In JAVA i metodi di default sono virtuali.

Saluti.