Heim > Java > JavaBase > Hauptteil

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

Freigeben: 2019-11-27 17:39:20
nach vorne
2133 Leute haben es durchsucht

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

1. Einführung in Ausnahmen

Was ist eine Ausnahme?

Abnormalität ist etwas, das von der Norm abweicht, von der normalen Situation abweicht und etwas schief geht. In Java werden Situationen, die die aktuelle Methode oder den aktuellen Bereich blockieren, als Ausnahmen bezeichnet.

Was ist das Ausnahmesystem in Java? (Empfohlen: Java-Video-Tutorial)

1. Alle abnormalen Klassen in Java erben von der Throwable-Klasse. Throwable umfasst hauptsächlich zwei Hauptkategorien, eine ist die Error-Klasse und die andere ist die Exception-Klasse.

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

2 Die Error-Klasse umfasst Fehler und Thread-Deadlocks Ein Fehler tritt auf, das Programm bleibt vollständig hängen und wird als Programmterminator bezeichnet.

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

3. Ausnahmeklasse, die allgemein als „Ausnahme“ bezeichnet wird. Bezieht sich hauptsächlich auf Probleme mit Codierung, Umgebung und Benutzeroperationseingaben. Ausnahmen umfassen hauptsächlich zwei Kategorien, ungeprüfte Ausnahmen (RuntimeException) und geprüfte Ausnahmen (einige andere Ausnahmen)

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

4 . RuntimeException-Ausnahmen umfassen hauptsächlich die folgenden vier Ausnahmen (tatsächlich gibt es viele andere Ausnahmen, die hier nicht aufgeführt sind): Nullzeigerausnahme, Array-Index-Ausnahme außerhalb der Grenzen, Typkonvertierungsausnahme und arithmetische Ausnahme. RuntimeException-Ausnahmen werden automatisch von der Java Virtual Machine ausgelöst und erfasst (auch wenn wir keine Ausnahmeerfassungsanweisung schreiben, wird während der Laufzeit ein Fehler ausgelöst!!). selbst und sollte logisch behandelt werden.

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung5. Auf Ausnahmen prüfen Es gibt verschiedene Gründe für diese Ausnahme, z. B. weil die Datei nicht vorhanden ist oder ein Verbindungsfehler vorliegt. Im Gegensatz zu seinem „Bruder“ RuntimeException müssen wir für diese Ausnahme manuell eine Capture-Anweisung zum Code hinzufügen, um die Ausnahme zu behandeln. Dies ist auch das Hauptausnahmeobjekt, mit dem wir uns beim Erlernen von Java-Ausnahmeanweisungen befassen.

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

2. Try-Catch-finally-Anweisung

(1) Try-Block: verantwortlich für das Fangen Ausnahmen: Sobald in try eine Ausnahme gefunden wird, wird die Kontrolle über das Programm an den Ausnahmehandler im Catch-Block übergeben.

[try-Anweisungsblock kann nicht unabhängig existieren und muss mit dem Catch- oder Final-Block koexistieren]

(2) Catch-Block: Wie geht man damit um? Geben Sie beispielsweise Warnungen aus: Eingabeaufforderungen, überprüfen Sie die Konfiguration, die Netzwerkverbindung, protokollieren Sie Fehler usw. Nach der Ausführung des Catch-Blocks springt das Programm aus dem Catch-Block und führt weiterhin den folgenden Code aus.

[Hinweise zum Schreiben von Catch-Blöcken: Ausnahmeklassen, die von mehreren Catch-Blöcken behandelt werden, sollten so behandelt werden, dass zuerst die Unterklasse und dann die übergeordnete Klasse abgefangen wird, da Ausnahmen [in der Nähe behandelt] werden (von oben nach unten). ). 】

(3) schließlich: endlich ausgeführter Code, der zum Schließen und Freigeben von Ressourcen verwendet wird.

Das Syntaxformat lautet wie folgt:

try{
//一些会抛出的异常
}catch(Exception e){
//第一个catch
//处理该异常的代码块
}catch(Exception e){
//第二个catch,可以有多个catch
//处理该异常的代码块
}finally{
//最终要执行的代码
}
Nach dem Login kopieren

Wenn eine Ausnahme auftritt, beendet das Programm die Ausführung und wird an den Ausnahmehandler übergeben (Auslösen einer Erinnerung oder Aufzeichnen eines Protokolls usw.). , und der Code außerhalb des Ausnahmecodeblocks wird normal ausgeführt. try löst viele Arten von Ausnahmen aus und mehrere Catch-Blöcke erfassen mehrere Fehler.

Mehrere Probleme bei der Ausnahmebehandlung der Codeblockreihenfolge: zuerst die untergeordnete Klasse und dann die übergeordnete Klasse (der Compiler gibt einen Fehler aus, wenn die Reihenfolge falsch ist), und der „finally“-Anweisungsblock verarbeitet den Code, der schließlich ausgeführt wird .

Als nächstes verwenden wir Beispiele, um die Try-Catch-Anweisung zu konsolidieren~

Schauen Sie sich zuerst das Beispiel an:

package com.hysum.test;

public class TryCatchTest {
    /**
     * divider:除数
     * result:结果
     * try-catch捕获while循环
     * 每次循环,divider减一,result=result+100/divider
     * 如果:捕获异常,打印输出“异常抛出了”,返回-1
     * 否则:返回result
     * @return
     */
    public int test1(){
        int divider=10;
        int result=100;
        try{
            while(divider>-1){
                divider--;
                result=result+100/divider;
            }
            return result;
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("异常抛出了!!");
            return -1;
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        TryCatchTest t1=new TryCatchTest();
        System.out.println("test1方法执行完毕!result的值为:"+t1.test1());
    }
    
}
Nach dem Login kopieren

Laufergebnisse:

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

Ergebnisanalyse: Die von den roten Wörtern im Ergebnis ausgelösten Ausnahmeinformationen werden von e.printStackTrace() ausgegeben, was zeigt, dass der Ausnahmetyp, den wir hier auslösen, eine arithmetische Ausnahme ist, gefolgt vom Grund: durch Null (Durch 0 verursachte arithmetische Ausnahme). Die folgenden zwei Zeilen von at geben die spezifische Position des Codes an, der diese Ausnahme verursacht hat.

Fügen Sie dem obigen Beispiel eine test2()-Methode hinzu, um den Ausführungsstatus der final-Anweisung zu testen:

/**
     * divider:除数
     * result:结果
     * try-catch捕获while循环
     * 每次循环,divider减一,result=result+100/divider
     * 如果:捕获异常,打印输出“异常抛出了”,返回result=999
     * 否则:返回result
     * finally:打印输出“这是finally,哈哈哈!!”同时打印输出result
     * @return
     */
    public int test2(){
        int divider=10;
        int result=100;
        try{
            while(divider>-1){
                divider--;
                result=result+100/divider;
            }
            return result;
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("异常抛出了!!");
            return result=999;
        }finally{
            System.out.println("这是finally,哈哈哈!!");
            System.out.println("result的值为:"+result);
        }        
    }         
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        TryCatchTest t1=new TryCatchTest();
        //System.out.println("test1方法执行完毕!result的值为:"+t1.test1());
        t1.test2();
        System.out.println("test2方法执行完毕!");
    }
Nach dem Login kopieren

Laufende Ergebnisse:

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

Ergebnisanalyse: Aus den Ergebnissen können wir ersehen, dass der „finally“-Anweisungsblock zuletzt ausgeführt wird, nachdem die „try“- und „catch“-Blockanweisungen ausgeführt wurden. „final“ wird ausgeführt, nachdem der Ausdruck hinter „return“ berechnet wurde (zu diesem Zeitpunkt wird der berechnete Wert nicht zurückgegeben, aber der zurückzugebende Wert wird zuerst gespeichert. Unabhängig vom Code in „final“ ändert sich der zurückgegebene Wert nicht. ist immer noch der zuvor gespeicherte Wert), sodass der Rückgabewert der Funktion vor der endgültigen Ausführung ermittelt wird;

这里有个有趣的问题,如果把上述中的test2方法中的finally语句块中加上return,编译器就会提示警告:finally block does not complete normally

public int test2(){
        int divider=10;
        int result=100;
        try{
            while(divider>-1){
                divider--;
                result=result+100/divider;
            }
            return result;
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("异常抛出了!!");
            return result=999;
        }finally{
            System.out.println("这是finally,哈哈哈!!");
            System.out.println("result的值为:"+result);
            return result;//编译器警告
        }
        
    }
Nach dem Login kopieren

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

分析问题: finally块中的return语句可能会覆盖try块、catch块中的return语句;如果finally块中包含了return语句,即使前面的catch块重新抛出了异常,则调用该方法的语句也不会获得catch块重新抛出的异常,而是会得到finally块的返回值,并且不会捕获异常。

解决问题:面对上述情况,其实更合理的做法是,既不在try block内部中使用return语句,也不在finally内部使用 return语句,而应该在 finally 语句之后使用return来表示函数的结束和返回。如:

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

总结:

1、不管有木有出现异常或者try和catch中有返回值return,finally块中代码都会执行;

2、finally中最好不要包含return,否则程序会提前退出,返回会覆盖try或catch中保存的返回值。

3、 e.printStackTrace()可以输出异常信息。

4、 return值为-1为抛出异常的习惯写法。

5、 如果方法中try,catch,finally中没有返回语句,则会调用这三个语句块之外的return结果。

6、 finally 在try中的return之后 在返回主调函数之前执行。

三、throw和throws关键字

java中的异常抛出通常使用throw和throws关键字来实现。

throw ----将产生的异常抛出,是抛出异常的一个动作。

一般会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常。如:

语法:throw (异常对象),如:

public static void main(String[] args) { 
    String s = "abc"; 
    if(s.equals("abc")) { 
      throw new NumberFormatException(); 
    } else { 
      System.out.println(s); 
    } 
    //function(); 
}
Nach dem Login kopieren

运行结果:

Exception in thread "main" java.lang.NumberFormatException
at test.ExceptionTest.main(ExceptionTest.java:67)
Nach dem Login kopieren

throws----声明将要抛出何种类型的异常(声明)。

语法格式:

 public void 方法名(参数列表)
    throws 异常列表{
 //调用会抛出异常的方法或者:
 throw new Exception();
 }
Nach dem Login kopieren

当某个方法可能会抛出某种异常时用于throws 声明可能抛出的异常,然后交给上层调用它的方法程序处理。如:

public static void function() throws NumberFormatException{ 
    String s = "abc"; 
    System.out.println(Double.parseDouble(s)); 
  } 
    
  public static void main(String[] args) { 
    try { 
      function(); 
    } catch (NumberFormatException e) { 
      System.err.println("非数据类型不能转换。"); 
      //e.printStackTrace(); 
    } 
}
Nach dem Login kopieren

throw与throws的比较

1、throws出现在方法函数头;而throw出现在函数体。

2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。

3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

来看个例子:

throws e1,e2,e3只是告诉程序这个方法可能会抛出这些异常,方法的调用者可能要处理这些异常,而这些异常e1,e2,e3可能是该函数体产生的。

throw则是明确了这个地方要抛出这个异常。如:

void doA(int a) throws (Exception1,Exception2,Exception3){
      try{
         ......
 
      }catch(Exception1 e){
       throw e;
      }catch(Exception2 e){
       System.out.println("出错了!");
      }
      if(a!=b)
       throw new Exception3("自定义异常");
}
Nach dem Login kopieren

分析:

1、代码块中可能会产生3个异常,(Exception1,Exception2,Exception3)。

2、如果产生Exception1异常,则捕获之后再抛出,由该方法的调用者去处理。

3、如果产生Exception2异常,则该方法自己处理了(即System.out.println("出错了!");)。所以该方法就不会再向外抛出Exception2异常了,void doA() throws Exception1,Exception3 里面的Exception2也就不用写了。因为已经用try-catch语句捕获并处理了。

4、Exception3异常是该方法的某段逻辑出错,程序员自己做了处理,在该段逻辑错误的情况下抛出异常Exception3,则该方法的调用者也要处理此异常。这里用到了自定义异常,该异常下面会由解释。

使用throw和throws关键字需要注意以下几点:

1.throws的异常列表可以是抛出一条异常,也可以是抛出多条异常,每个类型的异常中间用逗号隔开

2.方法体中调用会抛出异常的方法或者是先抛出一个异常:用throw new Exception() throw写在方法体里,表示“抛出异常”这个动作。

3.如果某个方法调用了抛出异常的方法,那么必须添加try catch语句去尝试捕获这种异常, 或者添加声明,将异常抛出给更上一层的调用者进行处理

自定义异常

为什么要使用自定义异常,有什么好处?

1、我们在工作的时候,项目是分模块或者分功能开发的 ,基本不会你一个人开发一整个项目,使用自定义异常类就统一了对外异常展示的方式。

2、有时候我们遇到某些校验或者问题时,需要直接结束掉当前的请求,这时便可以通过抛出自定义异常来结束,如果你项目中使用了SpringMVC比较新的版本的话有控制器增强,可以通过@ControllerAdvice注解写一个控制器增强类来拦截自定义的异常并响应给前端相应的信息。

3、自定义异常可以在我们项目中某些特殊的业务逻辑时抛出异常,比如"中性".equals(sex),性别等于中性时我们要抛出异常,而Java是不会有这种异常的。系统中有些错误是符合Java语法的,但不符合我们项目的业务逻辑。

4、使用自定义异常继承相关的异常来抛出处理后的异常信息可以隐藏底层的异常,这样更安全,异常信息也更加的直观。自定义异常可以抛出我们自己想要抛出的信息,可以通过抛出的信息区分异常发生的位置,根据异常名我们就可以知道哪里有异常,根据异常提示信息进行程序修改。比如空指针异常NullPointException,我们可以抛出信息为“xxx为空”定位异常位置,而不用输出堆栈信息。

说完了为什么要使用自定义异常,有什么好处,我们再来看看自定义异常的毛病:

我们不可能期待JVM(Java虚拟机)自动抛出一个自定义异常,也不能够期待JVM会自动处理一个自定义异常。发现异常、抛出异常以及处理异常的工作必须靠编程人员在代码中利用异常处理机制自己完成。这样就相应的增加了一些开发成本和工作量,所以项目没必要的话,也不一定非得要用上自定义异常,要能够自己去权衡。

最后,我们来看看怎么使用自定义异常:

在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。

所有异常都必须是 Throwable 的子类。

如果希望写一个检查性异常类,则需要继承 Exception 类。

如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

可以像下面这样定义自己的异常类:

class MyException extends Exception{ }
Nach dem Login kopieren

我们来看一个实例:

package com.hysum.test;
public class MyException extends Exception {
     /**
     * 错误编码
     */
    private String errorCode;   
    public MyException(){}
    
    /**
     * 构造一个基本异常.
     *
     * @param message
     *        信息描述
     */
    public MyException(String message)
    {
        super(message);
    }
    public String getErrorCode() {
        return errorCode;
    }
    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }   
}
Nach dem Login kopieren

使用自定义异常抛出异常信息:

package com.hysum.test;

public class Main {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String[] sexs = {"男性","女性","中性"};
                  for(int i = 0; i < sexs.length; i++){
                      if("中性".equals(sexs[i])){
                          try {
                            throw new MyException("不存在中性的人!");
                        } catch (MyException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                     }else{
                         System.out.println(sexs[i]);
                     }
                } 
    }

}
Nach dem Login kopieren

运行结果:

Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

四、java中的异常链

异常需要封装,但是仅仅封装还是不够的,还需要传递异常。

异常链是一种面向对象编程技术,指将捕获的异常包装进一个新的异常中并重新抛出的异常处理方式。原异常被保存为新异常的一个属性(比如cause)。这样做的意义是一个方法应该抛出定义在相同的抽象层次上的异常,但不会丢弃更低层次的信息。

我可以这样理解异常链:

把捕获的异常包装成新的异常,在新异常里添加原始的异常,并将新异常抛出,它们就像是链式反应一样,一个导致(cause)另一个。这样在最后的顶层抛出的异常信息就包括了最底层的异常信息。

》场景

比如我们的JEE项目一般都又三层:持久层、逻辑层、展现层,持久层负责与数据库交互,逻辑层负责业务逻辑的实现,展现层负责UI数据的处理。

有这样一个模块:用户第一次访问的时候,需要持久层从user.xml中读取数据,如果该文件不存在则提示用户创建之。

那问题就来了:如果我们直接把持久层的异常FileNotFoundException抛弃掉,逻辑层根本无从得知发生任何事情,也就不能为展现层提供一个友好的处理结果,最终倒霉的就是展现层:没有办法提供异常信息,只能告诉用户“出错了,我也不知道出了什么错了”—毫无友好性而言。

正确的做法是先封装,然后传递,过程如下:

1、把FileNotFoundException封装为MyException。

2、抛出到逻辑层,逻辑层根据异常代码(或者自定义的异常类型)确定后续处理逻辑,然后抛出到展现层。

3、展现层自行确定展现什么,如果管理员则可以展现低层级的异常,如果是普通用户则展示封装后的异常。

示例

package com.hysum.test;

public class Main {
    public void test1() throws RuntimeException{
        String[] sexs = {"男性","女性","中性"};
        for(int i = 0; i < sexs.length; i++){
            if("中性".equals(sexs[i])){
                try {
                    throw new MyException("不存在中性的人!");
                } catch (MyException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    RuntimeException rte=new RuntimeException(e);//包装成RuntimeException异常
                    //rte.initCause(e);
                    throw rte;//抛出包装后的新的异常
                }
           }else{
               System.out.println(sexs[i]);
           }
      } 
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Main m =new Main();
        
        try{
        m.test1();
        }catch (Exception e){
            e.printStackTrace();
            e.getCause();//获得原始异常
        }
        
    }

}
Nach dem Login kopieren

运行结果:

1Detaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung

Ergebnisanalyse: Wir können sehen, dass die Konsole zuerst die ursprüngliche Ausnahme ausgibt, die von e.getCause(); ausgegeben wird, und dann e.printStackTrace() ausgibt, wo Sie sehen können Verursacht durch: die ursprüngliche Ausnahme und e Die Die Ausgabe von .getCause() ist konsistent. Dies bildet eine abnormale Kette.

Die Funktion von initCause() besteht darin, die ursprüngliche Ausnahme einzuschließen. Wenn Sie wissen möchten, welche Ausnahme auf der untersten Ebene aufgetreten ist, können Sie die ursprüngliche Ausnahme abrufen, indem Sie getCause() aufrufen.

Es wird empfohlen, dass

Ausnahmen gekapselt und übergeben werden müssen. Wenn wir das System entwickeln, sollten wir keine Ausnahmen „verschlucken“ oder „nackt“ auslösen Werfen Sie sie oder durch Ausnahmekettenübertragung kann das System robuster und benutzerfreundlicher gemacht werden.

5. Fazit

Das Wissen über die Ausnahmebehandlung in Java ist kompliziert und etwas schwer zu verstehen Es folgen einige gute Codierungsgewohnheiten bei der Verwendung der Java-Ausnahmebehandlung:

1 Verwenden Sie bei der Behandlung von Laufzeitausnahmen Logik, um die Try-Catch-Verarbeitung angemessen zu vermeiden und zu unterstützen.

2 , können Sie einen Catch (Ausnahme) hinzufügen, um Ausnahmen zu behandeln, die möglicherweise übersehen werden

3. Bei unsicherem Code können Sie auch Try-Catch hinzufügen, um potenzielle Ausnahmen zu behandeln

4. Versuchen Sie, damit umzugehen Denken Sie daran, so viele Ausnahmen wie möglich auszuschließen. Denken Sie daran, zum Drucken einfach printStackTrace() aufzurufen.

5. Die konkrete Behandlung von Ausnahmen sollte entsprechend den unterschiedlichen Geschäftsanforderungen und Ausnahmetypen entschieden werden.

6. Versuchen Sie, etwas hinzuzufügen ein „finally“-Anweisungsblock, um die belegten Ressourcen freizugeben

Für weitere Java-Kenntnisse beachten Sie bitte die Spalte Java Basic Tutorial.

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der JAVA-Ausnahmen und der Ausnahmebehandlung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:cnblogs.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!