JavaRush /Java Blog /Random EN /Exceptions in Java: catching and handling

Exceptions in Java: catching and handling

Published in the Random EN group
Hello! I hate to break it to you, but a huge part of a programmer’s job is dealing with errors. And most often - with their own. It just so happens that there are no people who do not make mistakes. And there are no such programs either. Of course, the main thing when working on an error is to understand its cause. And there can be a whole bunch of reasons for such in the program. At one point, the creators of Java were faced with a question: what to do with these very potential errors in programs? Avoiding them completely is unrealistic. Programmers can write something that is impossible to even imagine :) This means that it is necessary to build into the language a mechanism for dealing with errors. In other words, if some error has occurred in the program, a script is needed for further work. What exactly should the program do when an error occurs? Today we will get acquainted with this mechanism. And it is called “Exceptions .

What is an exception in Java

An exception is some exceptional, unplanned situation that occurred during the operation of the program. There can be many examples of exceptions in Java. For example, you wrote a code that reads text from a file and displays the first line to the 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);
   }
}
But such a file does not exist! The result of the program will be an exception - FileNotFoundException. Conclusion:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
Each exception is represented by a separate class in Java. All exception classes come from a common “ancestor” - the parent class Throwable. The name of the exception class usually briefly reflects the reason for its occurrence:
  • FileNotFoundException(file not found)
  • ArithmeticException(exception when performing a math operation)
  • ArrayIndexOutOfBoundsException(the number of the array cell is specified beyond its length). For example, if you try to output cell array[23] to the console for an array array of length 10.
There are almost 400 such classes in Java! Why so many? Precisely to make it more convenient for programmers to work with them. Imagine: you wrote a program, and when it runs, it throws an exception that looks like this:
Exception in thread "main"
Uh-uh :/ Nothing is clear. What kind of error it is and where it came from is unclear. There is no useful information. But thanks to such a variety of classes, the programmer gets the main thing for himself - the type of error and its probable cause, which is contained in the name of the class. After all, it’s a completely different thing to see in the console:
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
It immediately becomes clear what the problem might be and “in which direction to dig” to solve the problem! Exceptions, like any instances of classes, are objects.

Catching and Handling Exceptions

To work with exceptions in Java, there are special code blocks: try, catchand finally. Exceptions: interception and processing - 2The code in which the programmer expects exceptions to occur is placed in a block try. This does not mean that an exception will necessarily occur at this location. This means that it can happen there, and the programmer is aware of it. The type of error you expect to receive is placed in a block catch(“catch”). This is also where all the code that needs to be executed if an exception occurs is placed. Here's an example:
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!");
   }
}
Conclusion:

Ошибка! Файл не найден!
We put our code in two blocks. In the first block we expect that a “File not found” error may occur. This is a block try. In the second, we tell the program what to do if an error occurs. Moreover, there is a specific type of error - FileNotFoundException. If we pass catchanother exception class into the block brackets, it will not be caught.
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!");
   }
}
Conclusion:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
The code in the block catchdid not work because we “configured” this block to intercept ArithmeticException, and the code in the block trythrew out another type - FileNotFoundException. We did not write a script for FileNotFoundException, so the program displayed in the console the information that is displayed by default for FileNotFoundException. Here you need to pay attention to 3 things. First. As soon as an exception occurs in any line of code in a try block, the code after it will no longer be executed. The execution of the program will immediately “jump” to the block catch. For example:
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!");
   }
}
Conclusion:

Делим число на ноль 
Программа перепрыгнула в блок catch! 
Ошибка! Нельзя делить на ноль! 
In the block tryin the second line, we tried to divide a number by 0, which resulted in an exception ArithmeticException. After this, lines 6-10 of the block trywill no longer be executed. As we said, the program immediately began executing the block catch. Second. There can be several blocks catch. If the code in a block trycan throw not one, but several types of exceptions, you can write your own block for each of them 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!");

   }
}
In this example we wrote two blocks catch. If , tryoccurs in the block FileNotFoundException, the first block will be executed catch. If happens ArithmeticException, the second one will be executed. You can write at least 50 blocks catch. But, of course, it’s better not to write code that can throw 50 different types of errors :) Third. How do you know what exceptions your code might throw? Well, you can, of course, guess about some, but it’s impossible to keep everything in your head. Therefore, the Java compiler knows about the most common exceptions and knows in what situations they can occur. For example, if you wrote code and the compiler knows that 2 types of exceptions may occur during its operation, your code will not compile until you handle them. We will see examples of this below. Now regarding exception handling. There are 2 ways to process them. We have already met the first one - the method can handle the exception independently in the block catch(). There is a second option - the method can throw an exception up the call stack. What does it mean? For example, in our class we have a method - the same one printFirstString()- that reads a file and displays its first line to the console:
public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Currently our code does not compile because it has unhandled exceptions. On line 1 you indicate the path to the file. The compiler knows that such code can easily lead to FileNotFoundException. On line 3 you read text from the file. IOExceptionIn this process , an error during input-output (Input-Output) can easily occur . Now the compiler is telling you, “Dude, I won't approve this code or compile it until you tell me what I should do if one of these exceptions occurs. And they can definitely happen based on the code you wrote!” . There is nowhere to go, you need to process both! The first processing option is already familiar to us: we need to place our code in a block tryand add two blocks 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();
   }
}
But this is not the only option. We can avoid writing a script for the error inside the method, and simply throw the exception to the top. This is done using the keyword throws, which is written in the method declaration:
public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
After the word, throwswe list, separated by commas, all types of exceptions that this method can throw during operation. Why is this being done? Now, if someone in the program wants to call the method printFirstString(), he will have to implement exception handling himself. For example, in another part of the program, one of your colleagues wrote a method within which it calls your method 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");
}
Error, the code does not compile! printFirstString()We did not write an error handling script in the method . Therefore, the task falls on the shoulders of those who will use this method. That is, the method yourColleagueMethod()now faces the same 2 options: it must either process both exceptions that “flew” to it using try-catch, or forward them further.
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");
}
In the second case, processing will fall on the shoulders of the next method on the stack - the one that will call yourColleagueMethod(). That is why such a mechanism is called “throwing an exception upward”, or “passing to the top”. When you throw exceptions up using throws, the code compiles. At this moment, the compiler seems to say: “Okay, okay. Your code contains a bunch of potential exceptions, but I'll compile it anyway. We’ll come back to this conversation!” And when you call a method somewhere in the program that has not handled its exceptions, the compiler fulfills its promise and reminds you about them again. Finally, we'll talk about the block finally(pardon the pun). This is the last part of the exception handling triumvirate try-catch-finally. Its peculiarity is that it is executed under any program operation scenario.
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!");
   }
}
In this example, the code inside the block finallyis executed in both cases. If the code in the block tryis executed entirely and does not throw an exception, the block will fire at the end finally. If the code inside tryis interrupted and the program jumps to the block catch, after the code inside is executed catch, the block will still be selected finally. Why is it needed? Its main purpose is to execute the required part of the code; that part that must be completed regardless of the circumstances. For example, it often frees up some resources used by the program. In our code, we open a stream to read information from a file and pass it to a BufferedReader. Ours readerneeds to be closed and resources freed up. This must be done in any case: it doesn’t matter whether the program works as expected or throws an exception. It is convenient to do this in a block 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();
       }
   }
}
Now we are absolutely sure that we have taken care of occupied resources, regardless of what happens while the program is running :) That's not all you need to know about exceptions. Error handling is a very important topic in programming: more than one article is devoted to it. In the next lesson we will learn what types of exceptions there are and how to create your own exception :) See you there!
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION