Java-Ausnahmebehandlung

Schauen wir uns die Java-Ausnahmebehandlung an. Es ist eines der nützlichsten Themen und kann manchmal verwirrend sein. Aber Java-Ausnahmen sind ziemlich einfach zu verstehen und hier werden wir versuchen, den Großteil der Java-Ausnahmebehandlung abzudecken.
Einige Fragen:

  • Warum brauchen wir eine Ausnahmebehandlung in Java?
  • Unterschiede zwischen aktivierten und nicht aktivierten Ausnahmen in Java?
  • Wie und wann werden die Schlüsselwörter „throws“ und „throw“ verwendet?
  • Durch das Verständnis der Java-Ausnahmebehandlung können wir dafür sorgen, dass unsere Programme ordnungsgemäßer beendet werden, wenn in unserem Programm Fehler auftreten, die sowohl auf Programmfehler als auch auf unvorhergesehene Fehler zurückzuführen sind. Die Ausnahmen können von einer einfachen NullPointerException, IndexOutOfBoundsException, IOException, FileNotFoundException, OutOfMemoryError usw. reichen.

    Beginnen wir mit den Grundlagen des Try-Catch-Blocks.

    import java.io.File;
      import java.util.Scanner;
    
      public class JavaException {
    
          /**
           * @param args
           */
         public static void main(String() args) {
            String filename = "/Users/test.txt";
    
            File textFile = new File(filename);
            Scanner in = new Scanner(textFile);
    
            while(in.hasNext()) {
               String line = in.nextLine();
               System.out.println(line);
             }
    
             in.close();
          }
    
      }
    

    Dieses Programm lässt sich ohne ordnungsgemäße Ausnahmebehandlung nicht in Java kompilieren. Gehen Sie jedoch zunächst davon aus, dass dieser Code kompiliert wird. Wenn wir genau hinsehen, kommen uns als Benutzer möglicherweise mehrere Fragen in den Sinn: Was passiert, wenn die Datei überhaupt nicht existiert? Das Programm stürzt ab und gibt einen Ausnahme-Trace aus. Ein Endbenutzer, der die technischen Details von Java nicht kennt, möchte nicht, dass ihm eine Ausnahme angezeigt wird. Ich wünsche mir aber eher eine elegantere Möglichkeit, über Fehler und Ausnahmen in der Anwendung informiert zu werden. Wir werden also sehen, ob wir durch das Hinzufügen von Try-Catch-Blöcken unsere Ziele erreichen können.

    String filename = "/Users/test.txt";
          try {
               File textFile = new File(filename);
               Scanner in = new Scanner(textFile);
    
               while(in.hasNext()) {
                     String line = in.nextLine();
                     System.out.println(line);
               }
    
               in.close();
           } catch (Exception e) {
                      System.out.println("Sorry! File Not Found");
           }
    

    Wir umgeben den Code mit einem Try-Catch-Block wie dem oben gezeigten, in dem die Ausnahmen wahrscheinlich auftreten. Dieses Programm ist besser als das vorherige, da der Benutzer nur die Meldung „Entschuldigung!“ sehen würde. Datei nicht gefunden!“ Fehler anstelle eines Stack-Trace. Wir können einen Stack-Trace intern in eine Datei oder auf einen Server drucken, damit die Java-Entwickler das Problem beheben können, anstatt den Stack-Trace dem Benutzer anzuzeigen.

    Sind wir fertig? Nein. Das oben genannte Programm weist einige Mängel auf. Denken Sie daran, dass eine Datei nicht gefunden wird und das Programm die Ausführung dann nicht über die Scanner-Zeile hinaus fortsetzt. Warum? Da Java die Datei nicht finden konnte und daher eine Ausnahme auslöste, springt die nächste Ausführung zum Catch-Block und gibt die Fehlermeldung aus. Wenn wir genau hinschauen, haben wir die Zeile in.close() nicht ausgeführt, die den Eingabestream schließt, was unerwünscht ist. Natürlich können wir dies im Catch-Block schließen, aber Java hat einen separaten Block namens „Endlich“ wird im Allgemeinen für die Codebereinigung von Ressourcen verwendet.

    String filename = "/Users/test.txt";
          File textFile = null;
          Scanner in = null;
    
          try {
                textFile = new File(filename);
                in = new Scanner(textFile);
                while(in.hasNext()) {
                      String line = in.nextLine();
                      System.out.println(line);
                }
    
           } catch (Exception e) {
                System.out.println("Sorry! File Not Found");
           }
           finally {
                in.close();
           }
    

    Ich habe gerade einen finalen Codeblock hinzugefügt, um sicherzustellen, dass der Eingabestream auftritt, wenn eine Ausnahme auftritt oder nicht. Denken Sie also daran, dass Code unter dem Block „finally“ immer ausgeführt wird, unabhängig davon, ob im Block „try“ eine Ausnahme auftritt oder nicht. Wenn jedoch eine Ausnahme auftritt, werden weitere Anweisungen im Try-Block nicht ausgeführt und sowohl der Catch- als auch der Final-Codeblock werden ausgeführt. Gehen wir noch einen Schritt weiter.

    Java-Ausnahmehierarchie

    So wie jedes Objekt in Java die Object-Klasse erweitert, erweitern alle Ausnahmeklassen standardmäßig Throwable, wie es im Paket java.lang der Fall ist. Es gibt eine Hierarchie der Java-Ausnahmen. Hier ist ein einfaches Diagramm zur Erläuterung der Ausnahmehierarchie.

    Ausnahmehierarchie

    Die Hierarchie ist ziemlich einfach.

    • Wenn Fehler auftreten, müssen diese nicht von Programmierern behoben werden, sondern die JVM erledigt das für uns. Warum? Weil sie aus Gründen auftreten können, die außerhalb der Kontrolle von Programmierern liegen. Fehler wie OutOfMemoryError fallen in diese Kategorie. Entwickler müssen sie nicht explizit behandeln, wohingegen einige Arten von Ausnahmen explizit von Entwicklern behandelt werden müssen.
    • Ungeprüfte Ausnahmen oder Laufzeitausnahmen treten im Allgemeinen aufgrund fehlerhafter Programmierung auf. Zum Beispiel der Zugriff auf Nullwerte, ein Fehler bei der Division durch Null oder der Zugriff auf Array-Werte, die über die Array-Länge hinausgehen. Dies sind einige Beispiele für fehlerhafte Programmierung. Entweder liegt es an den Java-Programmierern, sich darum zu kümmern, oder JVM erledigt das für uns. Daher erzwingt Java während der Kompilierungszeit keine RunTimeExceptions und es bleibt der Entscheidung des Entwicklers überlassen.
    • Aber geprüfte Ausnahmen sind solche, die von Java-Entwicklern in ihrem Code behandelt werden sollen. Weil diese geprüften Ausnahmen aufgrund ungültiger Eingaben/Bedingungen auftreten, die im Allgemeinen außerhalb der Kontrolle des Programms liegen. Beispiele dafür sind die Datei, die nicht im vom Benutzer angegebenen Pfad gefunden wurde oder der Benutzer, der falsche Parameter für eine SQL-Anweisung angibt. Java prüft zur Kompilierzeit, ob diese Ausnahmen vorliegen. Daher sollten Java-Entwickler sie in ihrem Code entsprechend behandeln. Im Folgenden sehen wir einige Beispiele, um die Unterschiede zu veranschaulichen.

    Wirft ein Schlüsselwort aus

    Wenn wir wissen, dass eine Checked-Ausnahme auftreten kann, prüft Java dies zur Kompilierungszeit, ob sie ordnungsgemäß behandelt wurde. Daher müssen Java-Entwickler geprüfte Ausnahmen entweder mit dem Try-Catch-Block behandeln oder das Schlüsselwort „throws“ verwenden, um diese geprüfte Ausnahme an das Programm zurückzuwerfen, das diese Methode aufgerufen hat.

    public static void main(String() args) {
              String filename = "/Users/test.txt";
              try {
                   handleFile(filename);
              } catch (FileNotFoundException e) {
                  System.out.println(“Error in file”);
              }
         }
    
         public static void handleFile(String fileName) throws FileNotFoundException {
            File textFile = null;
            Scanner in = null;
            textFile = new File(fileName);
            in = new Scanner(textFile);
            while(in.hasNext()) {
                String line = in.nextLine();
                 System.out.println(line);
            }
          }
    

    Ich habe mein früheres Programm geändert, um das Schlüsselwort „throws“ zu veranschaulichen. Da FileNotFoundException eine geprüfte Ausnahme ist, müssen wir sie entweder mit Try-Catch behandeln oder diese Ausnahme an das Programm main() werfen, das diese Methode aufgerufen hat. Ich habe beschlossen, die Ausnahme an den Aufrufer zurückzuwerfen, und habe daher das Schlüsselwort „throws“ verwendet, um diese geprüfte Ausnahme an main() zurückzuwerfen. Die aufrufende Methode muss wissen, dass diese handleFile()-Methode eine geprüfte Ausnahme auslöst und muss daher explizit als Teil der Methodendefinition verwendet werden. Java erzwingt, dass nur geprüfte Ausnahmen behandelt werden, und daher habe ich public static void handleFile(String fileName) throws FileNotFoundException verwendet. main() muss diese FileNotFoundException behandeln und daher ist sie von einem Try-Catch-Block umgeben, da main() in meinem Fall der Ausgangspunkt meines Programms ist und daher alle Ausnahmen behandelt.

    Schlüsselwort werfen

    public static void main(String() args) {
            String filename = "/Users/test.txt";
            try {
                 handleFile(filename);
            } catch (IOException e) {
                 e.printStackTrace();
            }
       }
    
       public static void handleFile(String fileName) throws IOException {
            // some logic for processing …..
            throw new IOException("File Not Found");
        }
    

    Angenommen, wir entscheiden, dass wir aufgrund bestimmter Fehler in unseren Anwendungen einige Ausnahmen auslösen müssen. In solchen Fällen verwenden wir das Schlüsselwort „throw“, um die Ausnahme unseres Falles auszulösen. Bei dieser Ausnahme kann es sich entweder um aktivierte oder nicht aktivierte Ausnahmen mit dem Schlüsselwort „throw“ handeln. Auch hier gilt: Wenn es sich um eine geprüfte Ausnahme handelt, kann diese Ausnahme auf die Methode main() zurückgeworfen werden oder der Try-Catch-Block verwendet werden, um diese ausgelöste Ausnahme zu behandeln.

    Catch Blocks Hierarchie

    String filename = "/Users/test.txt";
    
    try {
       handleFile(filename);
    } catch (IOException e) {
         System.out.println(“IOException”);
    } catch (FileNotFoundException e) {
         System.out.println(“File not found”);
    }
    

    Eine weitere Sache in Catch-Blöcken ist die hierarchische Natur von Ausnahmen. Im obigen Fall erweitert FileNotFoundException IOException, was bedeutet, dass alle FileNotFoundExceptions alle IOExceptions sind, aber nicht umgekehrt, da es mehrere andere Klassen geben könnte, die ebenfalls Java IOException erweitern. IOException ist die übergeordnete Klasse. Daher lässt sich der obige Code nicht kompilieren, da der erste Catch-Block alle IOExceptions einschließlich FileNotFoundException abfängt und die zweite Klasse daher von keinem Java-Code erreicht wird. Die richtige Form der Hierarchie ist:

    try {
              handleFile(filename);
         } catch (FileNotFoundException e) {
              e.printStackTrace();
         } catch (IOException e) {
              e.printStackTrace();
         }
    

    Wenn ein FileNotFoundException-Fehler auftritt, gleicht Java den ersten Catch-Block ab und führt diesen aus. Wenn jedoch eine andere IOException auftritt (außer FileNotFoundException), stimmt Java mit dem zweiten Catch-Block überein. Denken Sie also daran, dass die übergeordnete Exception-Klasse immer ganz unten stehen sollte, sodass die übergeordnete Exception-Klasse die Ausnahme behandelt, wenn keine der Ausnahmen zutrifft.

    Java-Ausnahmeverpackung

    Nachdem wir uns die Verwendung der Schlüsselwörter „throw“ und „wirft“ angesehen haben, schauen wir uns nun dieses Beispiel an.

    public static void handleFile(File file) throws FileNotFoundException,EOFException {
           if (!file.exists())
                throw new FileNotFoundException("Invalid file");
           if (file.canRead())
                throw new EOFException("End of file");
           }
      }
    

    Dieses Beispiel sieht zunächst gut aus, aber was ist, wenn eine andere Methode zehn verschiedene Ausnahmen auslöst? Verwenden wir bei der Methodendefinition 10 verschiedene „Würfe“? Natürlich könnten wir das, aber es sieht nicht gut aus, da es ungeschickt aussieht und der Code unleserlich wird, wenn dieser Methode mehr geprüfte Ausnahmen hinzugefügt werden. Es gibt eine viel sauberere Möglichkeit, dies zu tun, und zwar die Verwendung des Java-Ausnahmeumbruchs.

    Alles, was wir tun, ist, Ausnahmen in eine andere Ausnahme zu verpacken und dann nur diese eine Ausnahme auszulösen, anstatt die Anzahl der n Ausnahmen. Wir haben einen Catch-Block für alle geprüften Ausnahmen und verwenden dann eine benutzerdefinierte Ausnahme, um den Aufrufer über alle Ausnahmen zu informieren. Jetzt haben wir also nur noch eine „Throws“-Ausnahme, wodurch der Code viel sauberer und verständlicher aussieht. Zum Beispiel,

    public static void handleFile(File file) throws CustomException {
            try {
                 if (!file.exists())
                     throw new FileNotFoundException("Invalid file");
                 else if (!file.read())
                      throw new EOFException("File doesn't exist");
             } catch (FileNotFoundException e) {
                   throw new CustomException("File Not found");
             } catch (EOFException e) {
                   throw new CustomException("EOF found");
             } catch (IOException e){
                   throw new CustomException("IO Exception occured");
             }
    
         }
    

    Das ist natürlich kein Zwang. Je nach Bedarf können diese entsprechend angepasst werden.

    Benutzerdefinierte Ausnahmeklasse

    Nachdem wir uns bisher mit Java in Build-Ausnahmen befasst haben, erstellen wir nun eine beispielhafte benutzerdefinierte Ausnahmeklasse. Eine Ausnahmeklasse kann entweder RunTimeException erweitern, wenn es sich um eine Laufzeitausnahme handeln muss, oder Exception, wenn es sich um Ausnahmen handeln muss, bei denen die Groß-/Kleinschreibung geprüft wird. Geprüfte Ausnahmen werden im Compilerstadium geprüft. Daher muss eine Methode, die diese benutzerdefinierte geprüfte Ausnahme auslöst, entweder das Schlüsselwort „throws“ verwenden, um die Ausnahme an den Aufrufer zurückzuwerfen, oder die Ausnahme mit einem Try-Catch-Block behandeln. Denken Sie immer an diese goldene Regel. Wählen Sie daher basierend auf den Designanforderungen die übergeordnete Exception-Klasse aus.

    public class CustomException extends RuntimeException{
    
           private static final long serialVersionUID = 1L;
    
           /**
            * @param args
            */
           public CustomException() {
                  super();
           }
    
           public CustomException(String msg) {
                  System.out.println("Custom Exception called");
           }
    
           public String getMessage() {
                  return super.getMessage();
           }
    
       }
    

    Eine einfache Implementierung der CustomException-Klasse. Es erweitert RunTimeException und überschreibt einige der erforderlichen Methoden.

    Zusammenfassung

    Das war’s fürs Erste mit den Ausnahmen. Ich hoffe, Ihnen hat die Lektüre des Artikels gefallen und Sie haben mehr über Ausnahmen erfahren, bevor Sie mit der Lektüre dieses Artikels begonnen haben. Sie werden mehr lernen, wenn Sie anfangen, Programme in Java zu schreiben. Beginnen Sie also ab sofort mit der Integration der Ausnahmebehandlung in Ihren Java-Code und veröffentlichen Sie Ihre Kommentare.

    Dieser Artikel wurde ursprünglich unter veröffentlicht Java-Tutorials – Lasst uns ins Meer springenhier mit Genehmigung des Autors und als Teil des JBC-Programms erneut veröffentlicht.

    Kommentar verfassen

    Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

    Nach oben scrollen