Em relação a sobrescrita de métodos e exceções devemos lembrar que as exceções fazem parte da assinatura do método.
Logo um método sobrescrito deve ter o mesmo tipo de retorno, mesma lista de parâmetros e exceções do método que ele sobrescreveu, com uma implementação diferente.
Se mudar a lista de parâmetros então será uma sobrecarga e aí será permitido mudar o tipo de retorno e fugir às regras de exceções para os sobrescritos.
1-) o método que sobrescreve pode lançar qualquer exceção não-verificada (unchecked exception), ou seja, qualquer sub-classe de RuntimeException
2-) o método que sobrescreve não deve lançar exceções verificadas novas ou mais abrangentes que as declaradas pelo método que está sendo sobrescrito
3-) o método que sobrescreve poderá lançar exceções mais restritivas ou menos abrangentes
É assim por causa do polimorfismo. Juntemos a herança com a sobrescrita. O que teremos? Teremos a implementação de comportamento especializados nas classes filhas. Porém, alguém tem que garantir que as classes filhas implementem os métodos de acordo com algumas regras (tipo de retorno, mesma lista de parâmetros e exceções).
Então a superclasse entra em ação e diz para a subclasse: “ok, você quer sobrescrever um método meu? Então o seu método deve retornar um boolean, deve receber uma String e lançar um IOException. Caso tente lançar uma exceção mais abrangente que IOException (uma classe hierarquicamente superior, ou de outra hierarquia), eu não o reconhecerei como um método que sobrescreve meu método.” E nesse caso também não será uma sobrecarga (não mudou parâmetro).
Assim nos beneficiaremos do polimorfismo. Utilizando o seu código de exemplo, imagine que em algum ponto da sua aplicação é aguardo um objeto do tipo Th para a execução do método A. Mas por algum motivo tinha que ser executado um comportamento diferente. Então você enviará um objeto do tipo Th1 (perfeito! É permitido), e o método executado continua sendo o método A. Porém ele vai executar o desejado comportamento diferente.
E você criou outras classes filhas de Th (Th2, Th3…) e cada uma tem a sua implementação de A.
Agora, em outros pontos da sua aplicação, você tem que executar o método A, às vezes da classe Th1, outras vezes da classe Th2 e assim vai. Então nesses pontos basta você aguardar um objeto do tipo Th e executar o método A. Porém se você que executar o método A de um objeto Th2, então basta enviar uma referência ao objeto Th2 que seu respectivo método A será executado.
Retornando às exceções. Como é que um método A de uma classe filha poderá ser executado se ele quer lançar uma exceção que não está no contrato com a superclasse? Como é que ele quer lançar um SQLException se o método da superclasse (aquele que foi sobrescrito) nem sabe quem é SQLException e só permitiu alguém que se enquadre em IOException?
Parece complicado, mas não é. Requer alguns leituras e alguns exemplos. :shock:
Qualquer coisa peça ajuda.
Boa sorte.