Ok, segue a explicação completa do livro.
O importante é entender que a seleção entre métodos sobrecarregados é estática, enquanto a seleção entre métodos sobrescritos é dinâmica. A versão correta de um método sobrescrito é escolha em tempo de execução, baseado no tipo Runtime do objeto no qual o método é chamado.
A versão que o compilador irá chamar, em tempo de execução, depende mais do tipo da variável no momento da compilação, e não do tipo de dados do seu conteúdo. O código abaixo demonstra o fenômeno (retirado do item 26, Core Java, Use overloading judiciously)
public class CollectionClassifier {
public static String classify(Set s) {
return "Set";
}
public static String classify(List l) {
return "List";
}
public static String classify(Collection c) {
return "Collection";
}
public static void main(String[] args) {
Collection[] testes = new Collection[] {
new HashSet(),
new ArrayList(),
new HashMap.values() //Neither Set nor List
};
for (int i = 0; i < testes.length; i++)
System.out.println(classify(tests[i]));
}
}
Quem esperar que esse programa retorne Set, List e Unknown Collection vai ter uma desagradável surpresa.
O java sempre irá utilizar o tipo Collection, pois é o que pode ser definido em tempo de compilação.
O problema de abordagens desse tipo é que, diferentes métodos podem conter diferentes referências e, por esse detalhe, você observa o programa (ou o depurador) se comportando de maneira aparentemente diferente durante a execução.
Para quem tiver mais interesse de ver o texto original, esse capítulo está como exemplo, no site oficial do livro e se trata do item 26.