JavaRush /Blogue Java /Random-PT /Exceções em Java: captura e tratamento

Exceções em Java: captura e tratamento

Publicado no grupo Random-PT
Olá! Odeio dizer isso a você, mas uma grande parte do trabalho de um programador é lidar com erros. E na maioria das vezes - com os seus próprios. Acontece que não existem pessoas que não cometam erros. E também não existem tais programas. Claro, o principal ao trabalhar com um erro é entender sua causa. E pode haver uma série de razões para isso no programa. A certa altura, os criadores do Java se depararam com uma pergunta: o que fazer com esses possíveis erros nos programas? Evitá-los completamente não é realista. Os programadores podem escrever algo que é impossível imaginar :) Isso significa que é necessário incorporar na linguagem um mecanismo para lidar com erros. Em outras palavras, se ocorreu algum erro no programa, um script é necessário para trabalhos futuros. O que exatamente o programa deve fazer quando ocorre um erro? Hoje conheceremos esse mecanismo. E é chamado de “Exceções .

O que é uma exceção em Java

Uma exceção é alguma situação excepcional e não planejada que ocorreu durante a operação do programa. Pode haver muitos exemplos de exceções em Java. Por exemplo, você escreveu um código que lê texto de um arquivo e exibe a primeira linha no console.
public class Main {

   public static void main(String[] args) throws IOException {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   }
}
Mas tal arquivo não existe! O resultado do programa será uma exceção - FileNotFoundException. Conclusão:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Cada exceção é representada por uma classe separada em Java. Todas as classes de exceção vêm de um “ancestral” comum – a classe pai Throwable. O nome da classe de exceção geralmente reflete brevemente o motivo de sua ocorrência:
  • FileNotFoundException(arquivo não encontrado)
  • ArithmeticException(exceção ao realizar uma operação matemática)
  • ArrayIndexOutOfBoundsException(o número da célula da matriz é especificado além do seu comprimento). Por exemplo, se você tentar exibir cell array[23] no console para um array array de comprimento 10.
Existem quase 400 dessas classes em Java! Por que tantos? Precisamente para tornar mais conveniente para os programadores trabalharem com eles. Imagine: você escreveu um programa e, quando ele é executado, lança uma exceção parecida com esta:
Exception in thread "main"
Uh-uh :/ Nada está claro. Que tipo de erro é e de onde veio não está claro. Não há informações úteis. Mas graças a essa variedade de classes, o programador obtém para si o principal - o tipo de erro e sua causa provável, que está contida no nome da classe. Afinal, é algo completamente diferente de se ver no console:
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Imediatamente fica claro qual pode ser o problema e “em que direção cavar” para resolvê-lo! As exceções, como quaisquer instâncias de classes, são objetos.

Capturando e tratando exceções

Para trabalhar com exceções em Java, existem blocos de código especiais: try, catche finally. Exceções: interceptação e processamento - 2O código no qual o programador espera que ocorram exceções é colocado em um bloco try. Isso não significa que necessariamente ocorrerá uma exceção neste local. Isso significa que isso pode acontecer ali e o programador está ciente disso. O tipo de erro que você espera receber é colocado em um bloco catch(“catch”). É aqui também que é colocado todo o código que precisa ser executado se ocorrer uma exceção. Aqui está um exemplo:
public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {

       System.out.println("Error! File not found!");
   }
}
Conclusão:

Ошибка! Файл не найден!
Colocamos nosso código em dois blocos. No primeiro bloco esperamos que possa ocorrer um erro “Arquivo não encontrado”. Isto é um bloco try. Na segunda, informamos ao programa o que fazer se ocorrer um erro. Além disso, existe um tipo específico de erro - FileNotFoundException. Se passarmos catchoutra classe de exceção entre colchetes de bloco, ela não será capturada.
public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (ArithmeticException e) {

       System.out.println("Error! File not found!");
   }
}
Conclusão:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
O código no bloco catchnão funcionou porque “configuramos” esse bloco para interceptar ArithmeticException, e o código no bloco trygerou outro tipo - FileNotFoundException. Não escrevemos um script para FileNotFoundException, então o programa exibiu no console as informações que são exibidas por padrão para FileNotFoundException. Aqui você precisa prestar atenção em 3 coisas. Primeiro. Assim que ocorrer uma exceção em qualquer linha de código em um bloco try, o código seguinte não será mais executado. A execução do programa “saltará” imediatamente para o bloco catch. Por exemplo:
public static void main(String[] args) {
   try {
       System.out.println("Divide a number by zero");
       System.out.println(366/0);//this line of code will throw an exception

       System.out.println("This");
       System.out.println("code");
       System.out.println("Not");
       System.out.println("will");
       System.out.println("done!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
Conclusão:

Делим число на ноль 
Программа перепрыгнула в блок catch! 
Ошибка! Нельзя делить на ноль! 
No bloco tryda segunda linha, tentamos dividir um número por 0, o que resultou em uma exceção ArithmeticException. Depois disso, as linhas 6 a 10 do bloco trynão serão mais executadas. Como dissemos, o programa começou imediatamente a executar o bloco catch. Segundo. Pode haver vários blocos catch. Se o código em um bloco trypuder lançar não um, mas vários tipos de exceções, você poderá escrever seu próprio bloco para cada um deles catch.
public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       System.out.println(366/0);
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {

       System.out.println("Error! File not found!");

   } catch (ArithmeticException e) {

       System.out.println("Error! Division by 0!");

   }
}
Neste exemplo escrevemos dois blocos catch. Se , tryocorrer no bloco FileNotFoundException, o primeiro bloco será executado catch. Se acontecer ArithmeticException, o segundo será executado. Você pode escrever pelo menos 50 blocos catch... Mas, claro, é melhor não escrever código que possa gerar 50 tipos diferentes de erros :) Terceiro. Como você sabe quais exceções seu código pode gerar? Bem, é claro que você pode adivinhar alguns, mas é impossível manter tudo na cabeça. Portanto, o compilador Java conhece as exceções mais comuns e sabe em quais situações elas podem ocorrer. Por exemplo, se você escreveu código e o compilador sabe que 2 tipos de exceções podem ocorrer durante sua operação, seu código não será compilado até que você os trate. Veremos exemplos disso a seguir. Agora, com relação ao tratamento de exceções. Existem 2 maneiras de processá-los. Já conhecemos o primeiro - o método pode tratar a exceção de forma independente no bloco catch(). Existe uma segunda opção - o método pode gerar uma exceção na pilha de chamadas. O que isso significa? Por exemplo, em nossa classe temos um método - o mesmo printFirstString()- que lê um arquivo e exibe sua primeira linha no console:
public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Atualmente nosso código não compila porque possui exceções não tratadas. Na linha 1 você indica o caminho para o arquivo. O compilador sabe que esse código pode facilmente levar a arquivos FileNotFoundException. Na linha 3 você lê o texto do arquivo. IOExceptionNeste processo , pode ocorrer facilmente um erro durante a entrada-saída (Entrada-Saída). Agora o compilador está lhe dizendo: “Cara, não vou aprovar este código nem compilá-lo até que você me diga o que devo fazer se uma dessas exceções ocorrer. E eles definitivamente podem acontecer com base no código que você escreveu!” . Não há para onde ir, você precisa processar os dois! A primeira opção de processamento já nos é familiar: precisamos colocar nosso código em um bloco trye adicionar dois blocos catch:
public static void printFirstString(String filePath) {

   try {
       BufferedReader reader = new BufferedReader(new FileReader(filePath));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error, file not found!");
       e.printStackTrace();
   } catch (IOException e) {
       System.out.println("Error while inputting/outputting data from file!");
       e.printStackTrace();
   }
}
Mas esta não é a única opção. Podemos evitar escrever um script para o erro dentro do método e simplesmente lançar a exceção para o topo. Isso é feito usando a palavra-chave throws, que está escrita na declaração do método:
public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Após a palavra throwslistamos, separados por vírgulas, todos os tipos de exceções que este método pode lançar durante a operação. Por que isso está sendo feito? Agora, se alguém no programa quiser chamar o método printFirstString(), ele mesmo terá que implementar o tratamento de exceções. Por exemplo, em outra parte do programa, um de seus colegas escreveu um método dentro do qual chama seu método printFirstString():
public static void yourColleagueMethod() {

   //...your colleague's method does something

   //...and at one moment calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
Erro, o código não compila! printFirstString()Não escrevemos um script de tratamento de erros no método . Portanto, a tarefa recai sobre quem utilizará esse método. Ou seja, o método yourColleagueMethod()agora enfrenta as mesmas 2 opções: ele deve processar ambas as exceções que “voaram” para ele usando try-catch, ou encaminhá-las posteriormente.
public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   //...the method does something

   //...and at one moment calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
No segundo caso, o processamento recairá sobre os ombros do próximo método na pilha - aquele que chamará yourColleagueMethod(). É por isso que tal mecanismo é chamado de “lançar uma exceção para cima” ou “passar para o topo”. Quando você lança exceções usando throws, o código é compilado. Neste momento, o compilador parece dizer: “Ok, ok. Seu código contém várias exceções em potencial, mas vou compilá-lo mesmo assim. Voltaremos a esta conversa!” E quando você chama um método em algum lugar do programa que não tratou suas exceções, o compilador cumpre sua promessa e o lembra delas novamente. Por fim, falaremos sobre o bloqueio finally(desculpem o trocadilho). Esta é a última parte do triunvirato de tratamento de exceções try-catch-finally. Sua peculiaridade é que pode ser executado em qualquer cenário de operação do programa.
public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error! File not found!");
       e.printStackTrace();
   } finally {
       System.out.println("And here is the finally block!");
   }
}
Neste exemplo, o código dentro do bloco finallyé executado em ambos os casos. Se o código do bloco tryfor executado inteiramente e não lançar uma exceção, o bloco será acionado no final finally. Se o código interno tryfor interrompido e o programa pular para o bloco catch, após a execução do código interno catch, o bloco ainda estará selecionado finally. Por que é necessário? Seu principal objetivo é executar a parte necessária do código; aquela parte que deve ser concluída independentemente das circunstâncias. Por exemplo, muitas vezes libera alguns recursos usados ​​pelo programa. Em nosso código, abrimos um stream para ler informações de um arquivo e passá-las para um arquivo BufferedReader. O nosso readerprecisa ser fechado e os recursos liberados. Isso deve ser feito em qualquer caso: não importa se o programa funciona conforme o esperado ou lança uma exceção. É conveniente fazer isso em um bloco finally:
public static void main(String[] args) throws IOException {

   BufferedReader reader = null;
   try {
       reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       e.printStackTrace();
   } finally {
       System.out.println("And here is the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
Agora temos certeza absoluta de que cuidamos dos recursos ocupados, independentemente do que aconteça durante a execução do programa :) Isso não é tudo que você precisa saber sobre exceções. O tratamento de erros é um tópico muito importante em programação: mais de um artigo é dedicado a ele. Na próxima lição aprenderemos quais tipos de exceções existem e como criar sua própria exceção :) Nos vemos lá!
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION