Exceções é um evento que acontece durante a execução de um programa, que acaba interrompendo o fluxo normal de um código. Uma exceção pode ocorrer por vários motivos.
Por exemplo, você pode estar tentando acessar o índice de uma String e acontecer isso.
publicclassMain{publicstaticvoidmain(String[] args) {String s ="Hello world";char c =s.charAt(20); // Impossível, pois "s" tem tamanho 11System.out.println(c); }}
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 20
at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:47)
at java.base/java.lang.String.charAt(String.java:693)
at Main.main(Main.java:14)
O código vai mandar esse erro para o terminal e terminar a execução do programa, sem chegar no println. Nesse caso o nosso problema é que não colocamos a chamada da função charAt em volta de um try catch.
Um bloco try catch é onde podemos colocar código que potencialmente pode gerar um ou mais erros.
Observe o exemplo abaixo:
publicclassMain{publicstaticvoidmain(String[] args) {String s ="Hello world";char c ='a';try { c =s.charAt(20); // Impossível, pois "s" tem tamanho 11 } catch (StringIndexOutOfBoundsException e) {e.printStackTrace(); }System.out.println(c); }}
Desta vez, nós declaramos um valor inicial para a variável c, e colocamos a lógica que chama a função dentro do bloco try, neste bloco as funções potencialmente perigosas são colocadas, e caso aconteça algum erro, o código vai cair no bloco catch onde podemos tratar casos onde aconteça um erro.
Perceba que o catch funciona como uma função, e recebe um parâmetro, neste caso ele recebe a nossa exceção, que pode ser imprimida com mais detalhes chamando a função printStackTrace().
Se rodarmos esse código, percebe que, por mais que o erro aconteça, a execução chega até o final.
java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 11
at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)
at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)
at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)
at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)
at java.base/java.lang.String.checkIndex(String.java:4557)
at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:46)
at java.base/java.lang.String.charAt(String.java:1515)
at Main.main(Main.java:7)
a
Uma coisa importante a notar, é que nem toda função que joga uma exceção irá exigir que você trate da mesma. Você pode utilizar a função charAt e nunca precisar colocar um bloco try catch para tratar possíveis erros.
Mas existe casos onde o tratamento de erro é obrigatório, então vamos olhar esse caso, já abordando também a outra problemática: "Como podemos tratar múltiplas exceções?"
Multiplas exceções
Considere o seguinte programa.
importjava.io.RandomAccessFile;importjava.util.ArrayList;publicclassMain {publicstaticInteger[] lerNumerosDeArquivo(String arqNome) {ArrayList<Integer> numeros =newArrayList<>();RandomAccessFile arq =newRandomAccessFile(arqNome,"r");String linha =newString();int numero =-1;while ((linha =arq.readLine()) !=null) { numero =Integer.parseInt(linha);numeros.add(numero); }arq.close();Integer[] array =newInteger[numeros.size()]; array =numeros.toArray(array);return array; }publicstaticvoidmain(String[] args) {Integer[] numeros =lerNumerosDeArquivo("arquivo.txt");for (Integer num : numeros) {System.out.println(num); } }}
Talvez nem tudo que você acabou de ler, mas o código chama a função lerNumerosDeArquivo para extrair das linhas de um arquivo.txt números válidos, que serão colocados dentro de um arranjo de inteiros. Por fim, a função main irá imprimir cada número um por um.
Se você tentar executar o código acima, você terá varios erros:
Unhandled Exception type FileNotFoundException java
Unhandled Exception type IOException java
Unhandled Exception type IOException java
Todos essas exceções o Java pede que você trate elas obrigatoriamente em código. Além disso, tem também o NumberFormatException que também pode acontecer no código acima se alguma linha for encontrada que contém um número inválido, porém o Java não obriga o programador a tratar esse problema.
Um jeito de lidar esse problema, seria rodear toda a lógica do programa que pode dar algum erro de exceção, com um try catch e adicionar um bloco catch para cada exceção:
Com isso, o nosso código agora está funcional e trata todos os possíveis erros, porém, agora a nossa função está muito bagunçada. Se quisermos podemos em vez de usar três blocos catch podemos colocar só um, e colocando como argumento o objeto Exception, que conseguirá encapsular todas as exceções ja que o mesmo é a classe pai de todas as exceções.
Agora sim, a nossa lógica foi simplificada, porém, em troca, perdemos a flexibilidade de poder manejar cada exceção individualmente. Uma outra alternativa, seria fazer com que a função não trate a exceção, mas sim apenas retornar para função que chamou ela, as exceções para serem tratadas.
Observe o exemplo abaixo
publicclassMain { public static Integer[] lerNumerosDeArquivo(String arqNome) throws FileNotFoundException, NumberFormatException, IOException {
ArrayList<Integer> numeros =newArrayList<>();RandomAccessFile arq =newRandomAccessFile(arqNome,"r");String linha =newString();int numero =-1;while ((linha =arq.readLine()) != null) { numero =Integer.parseInt(linha);numeros.add(numero); } arq.close();Integer[] array =newInteger[numeros.size()]; array =numeros.toArray(array);return array; }publicstaticvoidmain(String[] args) {Integer[] numeros =lerNumerosDeArquivo("arquivo.txt");for (Integer num : numeros) {System.out.println(num); } }}
Usando a palavra throws após a declaração da função, é possível jogar qualquer erro lançado pela função para outra. Desta forma, agora toda a lógica de tratamento de exceção foi colocada na função main que está chamado a função lerNumerosArquivos, aqui nós temos duas opções em como tratar esse problema na main, o primeiro seria fazer a função também usar o throws, porém desta forma não estaremos realmente tratando a exceção. A melhor forma aqui seria utilizar um try catch.
Pronto agora o nosso programa conseguiu tratar as exceções, e também caso dê algum erro, o nosso programa termina a execução, usando o System.exit(-1);.
Para finalizar vamos simplificar mais uma vez o código e permitir uma maior flexibidade no nosso programa. Supondo que eu não queira parar a execução do programa caso eu leia um número impossível, em vez da própria main tratar esse problema, podemos jogar a responsabilidade disso para a nossa função lerNumeroArquivos uma vez que fazendo isso, irá nos permitir ainda executar o código e imprimir todos os números válidos no arquivo.
importjava.io.FileNotFoundException;importjava.io.IOException;importjava.io.RandomAccessFile;importjava.util.ArrayList;publicclassMain {publicstaticInteger[] lerNumerosDeArquivo(String arqNome)throwsFileNotFoundException,IOException {ArrayList<Integer> numeros =newArrayList<>();RandomAccessFile arq =newRandomAccessFile(arqNome,"r");String linha =newString();int numero =-1;while ((linha =arq.readLine()) !=null) {try { numero =Integer.parseInt(linha); } catch(NumberFormatException ne) {System.err.println("[ERRO!]: "+ linha +" não é um número válido!");continue; }numeros.add(numero); }arq.close();Integer[] array =newInteger[numeros.size()]; array =numeros.toArray(array);return array; }publicstaticvoidmain(String[] args) {Integer[] numeros =null;String nomeArquivo ="arquivo.txt";try { numeros =lerNumerosDeArquivo(nomeArquivo); } catch (FileNotFoundException e) {System.err.println("[ERRO] "+ nomeArquivo +"não existe!");System.exit(-1); } catch(IOException ioe) {ioe.printStackTrace();System.exit(-1); }for (Integer num : numeros) {System.out.println(num); } }}
Pronto, acho que por agora está o suficiente ;-) . Agora tente rodar esse código, colocando os números abaixo em um "arquivo.txt"
15
32
66
323
Hello
431
6644
832
O seu resultado tem que ser algo parecido com isso:
[ERRO!]: Hello não é um número válido!
15
32
66
323
431
6644
832