Dieser Artikel konzentriert sich auf einige Missverständnisse in der Auswahl und Verwendung von Java -Ausnahmen. Ich hoffe, dass die Leser einige der Punkte und Prinzipien der Ausnahmebehandlung beherrschen und auf Zusammenfassung und Induktion achten können. Nur durch den Umgang mit Ausnahmen können wir die Grundqualitäten von Entwicklern verbessern, die Robustheit des Systems verbessern, die Benutzererfahrung verbessern und den Wert des Produkts verbessern.
Missverständnis 1. Abnormale Wahl
Abbildung 1. Anomalieklassifizierung
Abbildung 1 beschreibt die Struktur der Ausnahme. Tatsächlich wissen wir alle, dass Anomalien in die Nachweis von Anomalien und nicht erfassenden Anomalien unterteilt sind, aber in der Praxis ist die Anwendung dieser beiden Anomalien verwirrt. Da nicht erfasste Ausnahmen einfach zu bedienen sind, sind viele Entwickler der Meinung, dass die Erkennung von Ausnahmen nutzlos ist. Tatsächlich können die abnormalen Anwendungsszenarien wie folgt zusammengefasst werden:
1. Der Anrufcode kann nicht weiter ausgeführt werden und muss sofort beendet werden. Es gibt zu viele Möglichkeiten für diese Situation, wie z. B. der Server nicht angeschlossen ist, die Parameter falsch sind usw. Nicht erfasste Ausnahmen sind zu diesen Zeiten anwendbar, und es müssen keine explizite Erfassung und Verarbeitung von Code aufgerufen werden, und der Code ist präzise und klare.
2. Der aufrufende Code muss eine weitere Verarbeitung und Wiederherstellung benötigen. Wenn SQLEXception als nicht erfasste Ausnahmen definiert ist, werden Entwickler natürlich glauben, dass SQLEXception keine explizite Erfassung und Verarbeitung von Code erfordert, was zu schwerwiegenden Situationen wie dem Schließen der Verbindung, nicht der Rückführung der Transaktion und schmutzigen Daten in der DB führt. Gerade weil SQLEXception als Erkennung von Ausnahmen definiert ist, werden Entwickler dazu veranlasst, nach dem Code eine Ausnahme explizit zu erfassen und zu reinigen. Natürlich können Sie nach der Reinigung von Ressourcen weiterhin nicht erfasste Ausnahmen werfen, um die Ausführung des Programms zu verhindern. Nach Beobachtung und Verständnis kann die Erkennung von Ausnahmen hauptsächlich auf Werkzeugklassen angewendet werden. Java Learning Group 669823128
Missverständnis 2: Zeigen Sie Ausnahmen direkt auf der Seite oder dem Client an.
Es ist üblich, Ausnahmen direkt auf der Client -Seite zu drucken. Wenn der Code als Beispiel JSP nimmt, druckt der Container die Ausnahmestapelinformationen standardmäßig direkt auf der Seite. Aus der Sicht des Kunden hat jede Ausnahme keine praktische Bedeutung, und die meisten Kunden können die Ausnahmeinformationen überhaupt nicht verstehen. Die Softwareentwicklung sollte auch versuchen, die Ausnahme direkt den Benutzern zu vermeiden.
Listing 1
<strong> Paket </strong> com.ibm.dw.sample.exception;/*** benutzerdefinierte RunTimeException* Fehlercode -Attribut hinzufügen*/<strong> public </strong> <strong> Klasse </strong> <strong> RunTimeException </strong> <strong> erweitert </strong> <strong> java </strong>. <strong> public </strong> <strong> statisch </strong> <strong> Final </strong> Ganzzahl generic = 1000000; // Fehlercode <strong> privat </strong> Integer ERRORCODE; <strong> public </strong> <strong> RunTimeException </strong> (Integer ERRORCODE, Throwable Ursache) {<strong> this </strong> (Fehlercode, <strong> null </strong>, Ursache); } <strong> public </strong> <strong> RunTimeException </strong> (String -Nachricht, Throwable Ursache) {// Verwenden Sie den allgemeinen Fehlercode <strong> dieses </strong> (generische, meldung, Ursache); } <strong> public </strong> <strong> RunTimeException </strong> (Integer ERRORCODE, STRING -MODULE, TUPTABLE COUR) {<strong> Super </strong> (Nachricht, Ursache); <strong> this </strong> .ErrorCode = ERRORCODE; } <strong> public </strong> Integer <strong> getRrorCode </strong> () {<strong> return </strong> Fehlercode; }}Wie der Beispielcode zeigt, führen Sie Fehlercodes in Ausnahmen ein. Sobald eine Ausnahme eingetreten ist, präsentieren wir nur den Fehlercode der Ausnahme an den Benutzer oder konvertieren den Fehlercode in eine verständlichere Eingabeaufforderung. Tatsächlich enthält der Fehlercode hier auch eine andere Funktion, und Entwickler können auch genau wissen, welche Art von Ausnahme basierend auf dem Fehlercode aufgetreten ist.
Missverständnis 3: Verschmutzung der Codehierarchie
Wir teilen den Code häufig in verschiedene Hierarchien wie Service, Geschäftslogik, DAO usw. auf. Die DAO -Schicht enthält Methoden zum Werfen von Ausnahmen, wie in Listing 2 gezeigt:
Listing 2
<strong> public </strong> Kunde <strong> AbrufpustomerbyId </strong> (Long ID) <strong> Wurf </strong> SQLEXception {// Abfragen Sie die Datenbank basierend auf ID}Auf den ersten Blick gibt es kein Problem mit dem obigen Code, aber wenn Sie aus der Perspektive der Designkupplung sorgfältig nachdenken, verschmutzt die SQLEXception hier den oberen Anrufcode. Die aufrufende Ebene muss explizit Try-Catch verwenden, um sie auf die höhere Ebene zu erfassen oder weiter zu werfen. Nach dem Prinzip der Designisolierung können wir es angemessen ändern, an:
Listing 3
<strong> public </strong> Kunde <strong> AbrufsecustomerById </strong> (Long ID) {<strong> Versuchen Sie </strong> {// Die Datenbank basierend auf ID} <strong> catca </strong> (SQLEXception E) {// Verwenden Sie nicht nachgewiesene Ausnahmeberätigung, um die Ausnahme von Hierarchical zu erfassen, die Hierarchical-Kokupling </Strong-Wurfs-Wurfverluste </Strong </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> </strong> -Recon-usw. RunTimeException (SQLERRORCODE, E); } <strong> schließlich </strong> {// Schließen Sie die Verbindung und säubern Sie die Ressourcen}}Missverständnis 4: Ausnahmen ignorieren
Die folgende Ausnahmebehandlung gibt nur die Ausnahme von der Konsole aus und macht keinen Sinn. Darüber hinaus erscheint hier eine Ausnahme und das Programm unterbricht nicht, und der Anrufcode wird weiterhin ausgeführt, was zu mehr Ausnahmen führt.
Listing 4
<strong> public </strong> <strong> void </strong> <strong> retrievelobjectById </strong> (Long ID) {<strong> Versuchen Sie </strong> {//..esome Code, der SQLEXception auswirft} <strong> catch </strong> (SQLEXECTE ex) {/** ***Jemand, der weiß, dass es sich aus der Ausnahme von Ausnahme, dass das Printing -Aufgabe ist, nivalisiert, was der Eindruck hat. * In der Produktionsumgebung muss der Fehlerstapel an das Protokoll ausgegeben werden. * Und das Programm wird nach dem Fangprozess weiter ausgeführt, was zu weiteren Problemen führen wird*/ ex.printstacktrace (); }}Kann rekonstruiert werden:
Listing 5
<strong> public </strong> <strong> void </strong> <strong> retrieveObjectById </strong> (Long ID) {<strong> Try </strong> {//..esome Code, der SQLEXception} <strong> catch </strong> (SQLEXTECECECTE, ex) {Strong> </strong> <strong> new> new> runyiDexception ("Ausnahme </strong> avew> </strong> runy. ex); } <strong> schließlich </strong> {// räumen Ergebnissenset, Anweisung, Verbindung usw.}}Dieses Missverständnis ist relativ einfach und unter normalen Umständen werden Sie diesen Fehler auf niedriger Ebene nicht machen.
Missverständnis 5: Ausnahmen in Schleifenanweisungsblöcke einbeziehen
Wie im folgenden Code gezeigt, ist die Ausnahme im Block for Loop -Anweisungen enthalten.
Listing 6
<strong> für </strong> (<strong> int </strong> i = 0; i <100; i ++) {<strong> Versuchen Sie </strong> {} <strong> catch </strong> (xxxxception e) {//…. }}Wir alle wissen, dass die Ausnahmeverarbeitung Systemressourcen einnimmt. Auf den ersten Blick dachten alle, dass sie keinen solchen Fehler machen würden. Aus einer anderen Perspektive wird in der Klasse A eine Schleife ausgeführt, und die Methode der Klasse B wird in der Schleife aufgerufen, aber die in Klasse B genannte Methode enthält Anweisungsblöcke wie Try-Catch. Die Hierarchie der Klasse ist verblasst und der Code ist genau der gleiche wie oben.
Missverständnis 6: Verwenden Sie eine Ausnahme, um alle potenziellen Ausnahmen zu erfassen
Während der Ausführung einer Methode werden verschiedene Arten von Ausnahmen geworfen. Um der Einfachheit des Codes willen wird die Ausnahme der Basisklasse verwendet, um alle potenziellen Ausnahmen zu erfassen, wie im folgenden Beispiel gezeigt:
Listing 7
<strong> public </strong> <strong> void </strong> <strong> retrieveObjectById </strong> (Long ID) {<strong> Versuchen Sie </strong> {//… Code Call, der IOException //… Code -Aufruf, die SQLEXception} <strong> Fang </strong> (Ausnahme E) {// hier alle potenziellen Ausrüstungen erfasst, aus dem Basis -Klassen ausgelöst. Wenn mehrere Ebenen dies fangen, gehen die gültigen Informationen der ursprünglichen Ausnahme verloren <strong> Wurf </strong> <strong> neu </strong> RunTimeException ("Ausnahme <strong> in </strong> retrieveBejectById", e); }}Kann rekonstruiert werden
Listing 8
<strong> public </strong> <strong> void </strong> <strong> retrieveObjectById </strong> (Long ID) {<strong> Try </strong> {//..esome Code, der RunTimeException auswirkt, iOException, sqlexception} </strong </strong> </starke {//. RunTimeException (/*Geben Sie hier den Fehlercode an, der hier entsprechend IOException*/Code, "Ausnahme <strong> in </strong> retrieveObjectById", e); } <strong> catch </strong> (SQLEXception e) {// Fang SQLEXception <strong> Wurf </strong> <strong> Neu </strong> runTimeException (/*Geben Sie den Fehlercode an, der SQLEXception hier entspricht*/Code, "Ausnahme <strong> in </strong> retrieveByiDId", e); }}Missverständnis 7: Mehrstufe Einkapselung wirft nicht erfasste Ausnahmen aus
Wenn wir immer darauf bestehen, dass verschiedene Arten von Ausnahmen unterschiedliche Erfassungsanweisungen verwenden müssen, können die meisten Beispiele diesen Abschnitt umgehen. Wenn jedoch nur ein Stück Code -Anrufe mehr als eine Ausnahme auswirft, ist es häufig nicht erforderlich, eine Catch -Anweisung für jede verschiedene Art von Ausnahme zu schreiben. Für die Entwicklung reicht jede Ausnahme aus, um das spezifische Problem des Programms zu erklären.
Listing 9
<strong> Versuchen Sie </strong> {// Die RunTimeException, Ioexception oder andere können geworfen werden; // Beachten Sie den Unterschied zwischen hier und Missverständnis sechs. Hier ist ein Code, der mehrere Ausnahmen ausführt. Die oben genannten sind mehrere Codesegmente, die jeweils unterschiedliche Ausnahmen werfen} <strong> catch </strong> (<strong> Exception </strong> e) {// Wie immer konvertieren Sie die Ausnahme in RunTimeException, aber das E hier ist tatsächlich eine Instanz der RunTimeException und wurde im vorherigen Code eingekapselt. RunTimeException (/**/Code,/**/, e);}Wenn wir alle Ausnahmen in RunTimeException konvertieren, wie im obigen Beispiel gezeigt, machen wir, wenn der Ausnahmetyp bereits RunTimeException ist, eine andere Kapselung. Die RunTimeException wurde erneut umkapsuliert, und die gültigen Informationen, die von der ursprünglichen RunTimeException übernommen wurden, gingen verloren.
Die Lösung ist, dass wir der RunTimeException -Klasse relevante Überprüfungen hinzufügen können, um zu bestätigen, dass der Parameter Throwable keine Instanz der RunTimeException ist. Wenn ja, kopieren Sie das entsprechende Attribut in die neu erstellte Instanz. Oder verwenden Sie verschiedene Blocks für Catch -Anweisungen, um RunTimeException und andere Ausnahmen zu fangen. Persönliche Präferenzmethode 1, die Vorteile sind selbstverständlich.
Missverständnis 8: Ausnahme auf mehrstufiger Druckdruck
Schauen wir uns zunächst das folgende Beispiel an, in dem definiert wird, dass die 2 -Klassen A und B. die Klasse -B -Code in der Klasse A genannt werden, und sowohl Ausnahmen der Klasse A als auch der Klasse A und der Klasse B.
Listing 10
<strong> public </strong> <strong> Klasse </strong> <strong> a </strong> {<strong> privat </strong> <strong> static </strong> logger logger = loggerfactory.getLogger (a.class); <strong> public </strong> <strong> void </strong> <strong> prozess </strong> () {<strong> try </strong> {// Instanziierung der Klasse B, Sie können auf andere Injektionsmethoden wie B b = <strong> neu </strong> b () wechseln; B.Process (); // Ein anderer Code kann eine Ausnahme verursachen} <strong> catch </strong> (xxxexception e) {// Wenn die Verfahrensmethode der Klasse B eine Ausnahme ausgelöst hat, wird die Ausnahme in b die Klasse gedruckt und wird auch hier gedruckt, also logger.error (e); <strong> throw </strong> <strong> Neu </strong> runTimeException (/*Fehlercode*/ERRAGECODE,/*AUSSCHLIESSENFAHREN*/MSG, E); }}} <strong> public </strong> <strong> Klasse </strong> <strong> b </strong> {<strong> privat </strong> <strong> static </strong> logger logger = loggerfactory.getLogger (b.class); <strong> public </strong> <strong> void </strong> <strong> prozess </strong> () {<strong> try </strong> {// Code, der eine Ausnahme auswerfen kann} <strong> catch </strong> (xxxxexception e) {logger.error (e); <strong> throw </strong> <strong> Neu </strong> runTimeException (/*Fehlercode*/ERRAGECODE,/*AUSSCHLIESSENFAHREN*/MSG, E); }}}Die gleiche Ausnahme wird zweimal gedruckt. Wenn das Level etwas komplizierter ist, handelt es sich um Kopfschmerzen, die Systemleistung von Druckprotokollen nicht zu berücksichtigen und spezifische Probleme in Ausnahmeboten zu lokalisieren.
Druckprotokolle müssen in der Tat nur das Erfassen und Drucken in der äußersten Schicht des Codes erfassen und drucken. Der Ausnahmendruck kann auch als AOP geschrieben und in die äußerste Schicht des Rahmens eingewebt werden.
Missverständnis 9: Das Problem, das die in Ausnahmen enthaltenen Informationen nicht vollständig gefunden werden können
Ausnahmen ermöglichen es den Entwicklern nicht nur, zu wissen, was falsch ist, sondern auch häufiger Entwickler wissen, was das Problem verursacht. Wir wissen, dass Java .lang.Exception einen Konstruktor mit String-Typparametern hat und diese Zeichenfolge kann in leicht verständliche Eingabeaufforderungsinformationen angepasst werden.
Einfache benutzerdefinierte Informationsentwickler können nur wissen, wo die Ausnahme erscheint, aber in vielen Fällen müssen Entwickler mehr darüber wissen, welche Parameter eine solche Ausnahme verursachen. Zu diesem Zeitpunkt müssen wir die Parameterinformationen des Methode -Aufrufs an die benutzerdefinierten Informationen anhängen. Das folgende Beispiel listet nur den Fall eines Parameters auf. Bei mehreren Parametern können Sie eine Werkzeugklasse schreiben, um eine solche Zeichenfolge zu organisieren.
Listing 11
public <strong>void</strong> retrieveObjectById(Long id){ <strong>try</strong>{ //..some code that throws SQLException }<strong>catch</strong>(SQLException ex){ //Add parameter information to exception information <strong>throw</strong> <strong>new</strong> RuntimeException("Exception <strong>in</strong> retrieveObjectById <strong> mit </strong> Objekt -ID: "+ id, ex); }}Missverständnis 10: Mögliche Anomalien können nicht vorhersagen
Während des Schreibens des Schreibens Code ist es aufgrund des mangelnden tiefen Verständnisses des Anrufcodes unmöglich, genau zu bestimmen, ob der aufgerufene Code Ausnahmen erzeugt, sodass die Verarbeitung ignoriert wird. Nachdem ein Produktionsfehler erzeugt worden war, erinnerte ich mich, dass ich einem bestimmten Code eine Ausnahmeabruf hinzufügen sollte, und ich konnte nicht einmal genau auf die Ursache der Ausnahme hinweisen. Dies erfordert, dass Entwickler nicht nur wissen, was sie tun, sondern auch so viel wie möglich zu wissen, was andere getan haben und welche möglichen Ergebnisse verursacht werden können, und den Verarbeitungsprozess der gesamten Anwendung aus globaler Perspektive zu berücksichtigen. Diese Ideen wirken sich auf unser Schreiben und Verarbeitung von Code aus.
Missverständnis 11: gemischte Verwendung mehrerer Protokollbibliotheken von Drittanbietern
Heutzutage gibt es immer mehr Logbibliotheken von Java-Drittanbietern. In einem großen Projekt werden verschiedene Frameworks eingeführt, und diese Frameworks werden auf die Implementierung verschiedener Protokollbibliotheken beruhen. Das problematischste Problem ist nicht, alle erforderlichen Protokollbibliotheken einzuführen. Das Problem ist, dass die eingeführten Protokollbibliotheken selbst unvereinbar sind. Wenn es in der frühen Phase des Projekts leicht zu lösen ist, können Sie alle Protokollbibliotheken in Ihrem Code nach Bedarf wieder einführen oder in ein Framework ändern. Diese Art von Kosten ist jedoch für jedes Projekt nicht erschwinglich und desto höher ist das Risiko im Laufe des Projekts.
Wie können wir ähnliche Probleme effektiv vermeiden? Die meisten Frameworks haben jetzt ähnliche Probleme berücksichtigt. Sie können Eigenschaften oder XML -Dateien, Parameter oder Laufzeit -Scan -Protokoll -Implementierungsklassen in der Bibliothek der Lib und nur dann ermitteln, wenn die Anwendung ausgeführt wird, welche spezifische Protokollbibliothek angewendet werden soll.
Tatsächlich können wir basierend auf dem Prinzip, nicht mehrere Ebenen des Protokolldrucks erforderlich sind, viele Klassen vereinfachen, die ursprünglich als Protokolldruckcode bezeichnet werden. In vielen Fällen können wir Interceptors oder Filter verwenden, um Protokolle zu drucken, um die Kosten für die Wartung und Migration von Code zu senken.
Abschluss
Das obige ist rein persönliche Erfahrung und Zusammenfassung. Die Dinge sind dialektisch und es gibt keine absoluten Prinzipien. Das effektivste Prinzip ist für Sie geeignet. Ich hoffe, die obige Erklärung und Analyse kann Ihnen hilfreich sein.