Dúvidas em Exceções

21 respostas
jPscoralicK

Fala pessoal.

Fiz aqui um "projeto" bem simples que tem duas classes: Ponto e Circulo. No construtor da classe Circulo, são passados como parâmetro o raio (int) e um objeto p da classe Ponto.

Como tratamento de exceções, incluí na assinatura do construtor: throws [color=blue]NullPointerException[/color], [color=red]IllegalArgumentException[/color], para evitar que o ponto passado como argumento tenha referência nula e que o raio passado seja negativo.

Até aí tudo bem. Só que, quando criei a aplicação com o main e criei um objeto Circulo com parâmetro null para o ponto e, por exemplo, o valor negativo -5 para o raio, só era impresso a exceção para o raio, isto é, não era identificado a referênia null do ponto.

Para tratar esse erro, fui aconselhado a acrescentar o seguinte código ao construtor:

if (raio &lt 0) {
			throw new IllegalArgumentException(
					"Não é permitido valor negativo ao raio");
		}

		if (p == null) {
			throw new NullPointerException(
					"Não é permitido ponto com referência null");
		}

Contudo, o erro persistiu. Alguém sabe o que está acontecendo? O código acima é necessário?

Seguem abaixo o construtor e parte do main:

CONSTRUTOR

public Circulo(Ponto p, int raio) throws NullPointerException,
			IllegalArgumentException {

		if (raio &lt 0) {
			throw new IllegalArgumentException(
					"Não é permitido valor negativo ao raio");
		}

		if (p == null) {
			throw new NullPointerException(
					"Não é permitido ponto com referência null");
		}

		centrox = p.getX();
		centroy = p.getY();
		this.raio = raio;
	}

PARTE DO MAIN

public static void main (String[] args){
try {
				Circulo c2 = new Circulo(null, -3);
				}
				catch (NullPointerException e) {
					System.out.println("Não é permitido um ponto com referência null");
				}
				catch (IllegalArgumentException e) {
					System.out.println("Não é permitido raio negativo");
				}

Obrigado! :D

21 Respostas

Y

Está correto, cara.

Quando você usa vários catch’s, o primeiro que capturar a excessão “trava” a execução dos blocos catch’s adjacentes, mesmo que hajam mais catch’s elegíveis à frente. O que ocorre é que, mesmo que você tenha passado 2 parâmetros incorretos, somente a primeira excessão é lançada.

Quando seu programa chega nessa linha:

throw new IllegalArgumentException("Não é permitido um ponto com referência null");

A execução pula para os blocos catch’s, então, o catch (IllegalArgumentException) não é executado.

Veja o try… catch: caso uma excessão seja lançada dentro do bloco try, a execução é “pulada” para o primeiro catch que capture a excessão corretamente. Seguindo essa dedução da execução, basta você imaginar o seu método como se estivesse totalmente dentro de um bloco try, no momento que a excessão ser lançada, ele pára de executar e o tratamento da excessão começa (ou não) em blocos catch.

Espero não ter sido demasiado prolixo oO.

T

Se você sabe que um determinado método pode ter muitos erros, em vez de fazê-lo lançar exceções, você pode coletar as exceções em uma lista, e depois tratar o que for necessário. Exemplo:

private void copiaUmArquivo (File arqOrigem, File dirDestino) throws IOException {
...
}

public void copiaArquivos (File dirOrigem, File dirDestino, List &lt IOException &gt erros) {
    File[] arqs = dirOrigem.listFiles();
    for (int i = 0; i &lt arqs.length; ++i) {
        if (arqs[i].isFile()) {
            try {
                copiaUmArquivo (arqs[i], dirDestino);
            } catch (IOException ex) {
                erros.add (ex);
            }
        }
    }
}
Marky.Vasconcelos

Por favor quando postar códigos coloque os entre as tags [ code] e [ /code]

jPscoralicK

Valeu pessoal, entendi.

Mas, só mais um esclarecimento:

mesmo colocando o código abaixo na assinatura do método

throws NullPointerException, IllegalArgumentException

ainda assim preciso colocar os “ifs” do raio e do ponto dentro do método?

[]s

jPscoralicK

jPscoralicK:
Valeu pessoal, entendi.

Mas, só mais um esclarecimento:

mesmo colocando o código abaixo na assinatura do método

throws NullPointerException, IllegalArgumentException

ainda assim preciso colocar os “ifs” do raio e do ponto dentro do método?

[]s

?

jPscoralicK

alguém poderia me responder?

LPJava

assim sua pergunta nao entendi poe ai a sintaxe o que quer fazer… mais respondendo de modo geral, vc tem ai exceções nao verificadas onde nao vai ter obrigação nenhuma de declarar ou tratar… to falando de modo geral… mais poe sua duvida o que quer fazer… :smiley:

Y

jPscoralicK:
Valeu pessoal, entendi.

Mas, só mais um esclarecimento:

mesmo colocando o código abaixo na assinatura do método

throws NullPointerException, IllegalArgumentException

ainda assim preciso colocar os “ifs” do raio e do ponto dentro do método?

[]s

Para o IllegalArgumentException: Sim, você deve manter o seu if.

Entretanto, o if do NullPointerException você pode retirar pois, quando essa linha for executada:

centrox = p.getX();

Caso p (o parâmetro do construtor) seja nulo, o Java mesmo irá lançar uma NullPointerException.

jPscoralicK

Segue o código:

public Circulo(Ponto p, int raio) throws NullPointerException,
IllegalArgumentException {

if (raio &lt 0) {
throw new IllegalArgumentException(
"Não é permitido valor negativo ao raio";
}

if (p == null) {
throw new NullPointerException(
"Não é permitido ponto com referência null";
}

centrox = p.getX();
centroy = p.getY();
this.raio = raio;
}

A minha dúvida é a seguinte: mesmo colocando na assinatura do método o throws NullPointerException,
IllegalArgumentException
, eu preciso fazer os dois "ifs" dentro do método?

Porque, pelo o que eu li, quando você coloca o "throws" na assinatura, não é preciso fazer "mais nada" relativo a exceções dentro do método.

Y

Cara, é que tipo: declarar através do throws obriga ao código que chamará seu método/construtor a capturar e/ou repassar a excessão. Ou seja, a excessão não poderá ser “simplesmente ignorada”.

Já respondi em relação aos if’s cara. Você só precisa criar o if para a IllegalArgumentException, caso contrário, uma IllegalArgumentException nunca será lançada pelo seu contrutor (não neste caso).

jPscoralicK

Ok, Yky, obrigado.

Mas porque só a IllegalArgumentException tem que ser lançada?

LPJava

assim eu nao entendi ate agora pq vc ta tentando declarar exeções nao verificadas… implicitamente elas acontece… segundo a kathy é valido declaracao e tratar exceções verificadas…

jPscoralicK

Ok, camilo.

Então, sempre que eu declarar uma exceção, eu tenho que obrigatoriamente lançá-la dentro do método?

Y

jPscoralicK:
Ok, camilo.

Então, sempre que eu declarar uma exceção, eu tenho que obrigatoriamente lançá-la dentro do método?

Não. Ocorre que no seu caso nada dentro do seu construtor irá lançar uma IllegalArgumentException. Você declarou o throws IllegalArgumentException apenas para evitar que seja informado um valor errado, correto? Então, você é que tem que lançar a excessão. É complicado de explicar isso. Talvez alguém possa explanar isso de melhor forma.

jPscoralicK

Acho que depois de toda a complicação que eu arrumei estou começando a entender.

Valeu pela ajuda!

LPJava

a respeito de sua pergunta… nesse caso seu a resposta nao… nao é pq vc declara uma exceção no metodo que ele tem q lançar… vc declarou dizendo que ali pode ocorrer uma exceção… e nesse caso de exceções nao-verificadas ela pode ocorrer em todo o seu programa mesmo se declare ou nao… mais vc nao é obrigado a lançar uma exceção para o metodo que declara…
a declaração diz o seguinte: “olha se o corpo abaixo de {} lançar uma exceção passa a bola para quem me chamou veja”

class Az{
static void metodo() throws NullPointerException{
//se lançar uma exceção do tipo declarado esse metodo
//vai passar a bola para quem o chamou..
}
public static void main(String...arg){
//o metodo main que chamou o metodo() 
//se ele nao tratar a exceção aparece os codigos esquisito para o
//usuario na tela, mais como se trata de uma nao-verificada
//você nao tem obrigacao de tratar


metodo(){}
}

para tratar colocaria o metodo() do main dentro de :

try{} catch{}

bom espero ter ajudado, qualquer duvida so gritar :smiley:

jPscoralicK

Acho que entendi cara. Mas eu estou complicando muito minha cabeça! :x

Não sei se foi a maneria como aprendi que foi errada, pois, aprendi que, uma vez que é colocado na assinatura do método o tipo de exceção “throws QualquerException”, no corpo do método não é preciso fazer mais nada.

Mas, após alguns códigos que tive que fazer, era necessário colocar “ifs” para lançar a exceção, ou seja, pra que eu usuaria os “ifs” se já estou realizando exceção!?!?! Essa foi minha estúpida dúvida e com a qual estou até agora!

Li as respostas de todos, mas ainda não consegui captar tudo perfeitamente.

Me desculpem pela ignorância, mas quando eu fico com uma coisa confusa na cabeça, é difícil de retirar! :roll:

Valeu a todos!

ViniGodoy

Onde você leu essa besteira?

Colocar throws apenas indica que seu método lança certas exceções. Não tem sentido fazer isso para exceções não-verificadas (como a NullPointerException e o IllegalArgumentException).

Mas, ainda sim, você deve garantir que o código lance as exceções que você indicou, seja de maneira direta (laçando através do throw) ou de maneira indireta (fazendo acesso a um ponteiro nulo, ou chamando um método que gere essa exceção para você).

Mágica e advinhação o java ainda não faz. Mas do jeito que anda, não duvido que fará em alguma versão no futuro… :slight_smile:

jPscoralicK

Onde você leu essa besteira?

Colocar throws apenas indica que seu método lança certas exceções. Não tem sentido fazer isso para exceções não-verificadas (como a NullPointerException e o IllegalArgumentException).

Mas, ainda sim, você deve garantir que o código lance as exceções que você indicou, seja de maneira direta (laçando através do throw) ou de maneira indireta (fazendo acesso a um ponteiro nulo, ou chamando um método que gere essa exceção para você).

Mágica e advinhação o java ainda não faz. Mas do jeito que anda, não duvido que fará em alguma versão no futuro… :)

Valeu kra! A droga do slide da minha professora tava falando besteira então!

ViniGodoy

Agora, acho que ela quis te explicar a seguinte situação: imagine que você tem o seguinte método, que insere um nome numa tabela de nomes do banco de dados:

public insereNome(String nome) { Statatement stmt = conn.createStatement(); stmt.executeUpdate("INSERT INTO nomes(nome) VALUES('" + nome + "')"); stmt.close(); }

Da forma que está, esse método aí em cima não compila. Por quê? Simplesmente porque os métodos executeQuery e close da classe Statement lançam a exceção verificada SQLException. Veja, o problema aqui é tratar exceções lançadas, e não gerar nossas próprias exceções (como no caso que estava sendo discutido no tópico).

Para isso, temos duas alternativas. A primeira, é colocar um bloco try, catch e realmente tratar a exceção. Algo do tipo:

public insereNome(String nome) { try { Statatement stmt = conn.createStatement(); stmt.executeUpdate("INSERT INTO nomes(nome) VALUES('" + nome + "')"); stmt.close(); } catch (SQLException e) { log.getInstance().severe("Exceção inexperada!", e); System.exit(0); //Aborta a aplicação } }

O tratamento acima é um tanto drástico. Ele loga a exceção em algum arquivo, e depois aborta o programa. No nível em que essa classe está, não teria como fazer um tratamento muito mais sofisticado do que isso. O mais adequado seria partir para outra alternativa.

A outra alternativa, seria deixar para a classe que está usando nosso método tratar a exceção. Nesse caso, não nos preocuparíamos com a SQLException nesse método, mas nas classes que fazem uso desse método. Acho que era essa situação que estava no slide de sua professora.

Fazemos isso com a cláusula throws:

public insereNome(String nome) throws SQLException { Statatement stmt = conn.createStatement(); stmt.executeUpdate("INSERT INTO Nomes(nome) VALUES('" + nome + "')"); stmt.close(); }

Muito similar ao código original, não? Mas a cláusula throws, na assinatura do método, agora forcará a quem usar o método inserirNome a também usar um try catch, ou continuar lançando a exceção para cima, com outro throws. Embora nesse método não tenhamos mais que nos preocupar com a SQLException (conforme disse sua professora), ainda teremos que nos preocupar com ela no futuro, quando usarmos o método inserirNome.

Agora, nem sempre repassar diretamente a exceção é uma boa idéia. É uma boa prática de programação lançar exceções adequadas a camada que você estiver trabalhando. No caso do exemplo acima, nosso método ficaria:

public insereNome(String nome) throws FalhaAoInserirNomeException { try { Statatement stmt = conn.createStatement(); stmt.executeUpdate("INSERT INTO Nomes(nome) VALUES('" + nome + " ')"); stmt.close(); } catch (SQLException e) { throw new FalhaAoInserirNomeException("Não foi possível inserir o nome " + nome, e); //Note que o segundo parâmetro é a exceção lançada. //É uma boa prática anexar a causa na exceção reescrita. } }

Parece um mix dos dois, não? Mas, você poderia decidir que FalhaAoInserirNomeException não é uma exceção comum e que, portanto, seria uma RuntimeException. As classes que usam sua classe não precisariam mais captura-la, embora ela ainda ocorresse em caso de problemas e seria logada em algum ponto da aplicação, provavelmente por um UncaughtExceptionHandler.

Qual dos três casos é o mais adequado? Depende. O ideal é que você pare e pense em qual das três alternativas mais se encaixa no momento do tratamento da sua exceção.

Agora, quando o assunto é lançar suas próprias exceções, o throws não vai te ajudar em muita coisa. A não ser ao documentar que sua classe lança essa ou aquela exceção.

Agora uma coisa que você nunca deve fazer é o seguinte código:

try { //Código que lança exceção } catch (Exception e) { e.printStackTrace(); }

Isso aí ignorará todas as exceções, sejam elas runtime ou não. Uma péssima idéia. Seu código poderá executar algo inválido e continuará prosseguindo, simplesmente imprimindo o erro, mas ignorando que ele ocorreu. Isso certamente gerará problemas maiores.

Na pior das hipóteses, faça:

try { //Código que lança exceção } catch (Exception e) { throw new RuntimeException(e); }

Pelo menos assim, você deixa de se preocupar com a exceção, mas não deixa de lança-la, ainda que sem a necessidade de captura-la. Quando possível, parta para uma das três primeiras alternativas.

E note que, de novo, colocamos como parâmetro da RuntimeException a exceção original. Novamente para indicar a causa. Isso é muito importante no log que será gerado, especialmente se o problema ocorrer em campo.

jPscoralicK

Grande ViniGodoy! Agora sim cara, finalmente, depois de toda minha burrice saquei tudo! Valeu mesmo kra!

Viva o GUJ! \o/

Criado 8 de maio de 2007
Ultima resposta 15 de mai. de 2007
Respostas 21
Participantes 6