javaapi3异常处理

java异常处理机制

  • Java中左右错误的超类为:Throwable.其下有两个子类:Error和Exception
  • Error的子类面试的都是错误的系统错误,比如虚拟机内存溢出等
  • Exception的子类描述都是程序错误,比如空指针,下标越界等.
  • 通过我们程序中处理的溢出都是Exception

Throwble(1.throw:抛出,扔.2.error:错误.3.exception:例外)

  • Error:这一类表示的都是系统级别错误.(比如:虚拟机内存溢出错误)
  • Exception:这一类表示的都是程序级别(空指针溢出NullPointerException)数组下标越界异常ArrayInderOutOfBoundsException

我们使用异常处理机制通常只关心Exception这类问题,很少关注Error,异常处理机制的目的在程序运行期间出现了错误可以提供补救措施(B计划)

异常处理机制try-catch

/**
* 异常处理机制中的try-catch 
* 语法: 
* try{ 
* 代码片段... 
* }catch(XXXException e){ 
* 出现错误后的补救措施(B计划) 
* } 
*/ 
public class TryCatchDemo { 
public static void main(String[] args) { 
System.out.println("程序开始了...");
/* try{}语句块不能单独写,后面要么跟catch语句块要么跟finally语句块 
异常处理机制关注的是:明知道程序可能出现某种错误,但是该错误无法通过修改逻辑 
完全规避掉时,我们会使用异常处理机制,在出现该错误是提供一种补救办法。 
凡是能通过逻辑避免的错误都属于bug!就应当通过逻辑去避免! 
*/
try { 
// String str = null; 
// String str = ""; 
String str = "a"; 
/* 
若str=null的情况
当JVM执行到下面代码时:str.length()会出现空指针,此时虚拟机就会根据该情况 
实例化一个对应的异常实例出来,即:空指针异常实例 NullPointerException实例 
然后将程序从一开始执行到报错这句话的过程设置到该异常实例中,此时该异常通过 
类型名字可以表达出现了什么错误,并将来可以通过输出错误信息来得知错误出现在那里 
虚拟机会将该异常抛出 当某句代码抛出了一个异常时,JVM会做如下操作: 
1:检查报错这句话是否有被异常处理机制控制(有没有try-catch) 
如果有,则执行对应的catch操作,
如果没有catch可以捕获该异常则视为没有 异常处理动作 
2:如果没有异常处理,则异常会被抛出当当前代码所在的方法之外由调用当前方法的 
代码片段处理该异常 
*/
System.out.println(str.length());//抛出空指针异常 
System.out.println(str.charAt(0)); 
System.out.println(Integer.parseInt(str)); 
/*
 当try中某句代码报错后,就会跳出try执行下面对应的catch块,执行后就会 
退出catch继续向后执行。因此try语句块中报错代码以下的内容都不会被执行 
*/
System.out.println("!!!!!!!!!!!!!!"); 
// }catch(NullPointerException e){ 
//    这里实际开发中是写补救措施的,通常也会将异常信息输出便于debug 
// System.out.println("出现了空指针,并解决了!"); 
// }catch(StringIndexOutOfBoundsException e){ 
// System.out.println("处理字符串下标越界问题!"); 
// } 
/* 
当try语句块中可能出现的几种不同异常对应的处理办法相同时,
可以采取合并 catch的做法,用同一个catch来捕获这几种可能出现的异常,
而执行措施使用 同一个。 
*/ 
}catch(NullPointerException|StringIndexOutOfBoundsException e){ System.out.println("处理空指针或下标越界!"); 
/* 
当catch捕获某个超类型异常时,那么try语句块中出现其它类型异常时都可以被这个 catch块捕获并处理。
如果多个catch捕获的异常之间存在继承关系时,一定是子类异常在上超类异常在下 
*/
}catch(Exception e){ 
System.out.println("反正就是出了个错!"); 
}
System.out.println("程序结束了..."); 
} 
}

异常处理机制中的finally

  • finally块定义在异常处理机制中的最后一块,它可以直接跟在try之后,或者最后一个catch之后
  • finally可以保证之哟啊程序执行到了try语句块中,无论try语句块中的代码是否出现异常,最终finally都必定执行.
  • finally通常用来做到释放资源这类操作,比如IO操作后的关闭流动作就非常适合在finally中进行
public class FinallyDemo {
    public static void main(String[] args) {
        System.out.println("程序开始了");
        try {
            String line = "null";
            System.out.println(line.length());
            return;
        } catch (Exception e) {
            System.out.println("出错了");
        } finally {
            System.out.println("finally中的代码执行了");
        }

        System.out.println("程序结束了");
    }
}
这里控制台输出是:4 和finally中的代码执行了

说一说final,finally,和finalize是什么.

  1. final:最终的,最后的,不可改变的,被它修饰的变量不能被改变,修饰的方法不能 被重写,修饰的类不能被继承.
  2. finally:finally块是异常处理机制中最后一块,他可以直接跟在try语句块之后或最后一个catch块之后,finally保证只要执行到try语句块中,无论try语句块中的代码是否出现异常,finally都必定执行,通常finally块用于做资源释放.
  3. finalize:finalize是Object定义的一个方法,该方法是一个对象生命周期中最后一个方法,被GC调用,当GC发现一个对象没有引用后,就会将其释放,释放前最后调用的方法就是finalize.

IO操作时的异常处理机制

/**
 * 异常处理机制在IO中的应用
 */
public class FinallyDemo3 {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("fos.dat");
            fos.write(1);
        } catch (IOException e) {
            System.out.println("出错了!");
        } finally {
            try {
                if(fos!=null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

自动关闭特性

上面我们可以看见在finally块中,对于IO流的操作的关闭挺麻烦,所以我们可以用自动关闭特性.

JDK7之后,java提供了一个新的特性:自动关闭,旨在IO操作中可以更简洁的使用异常处理机制完成最后的close操作.

语法:
try(
 定义需要在finally中调用close()方法关闭的对象
){
IO操作
}catch(xxxException e){
.....
}
语法中可在try的"()"中定义的并初始化的对象必须实现
java.io.AutoCloseable接口,否则编译不通过.
(但是目前所有的流都实现了)

我们进行自动关闭特性的代码演示

public class AutoCloseableDemo {
    public static void main(String[] args) {
        //自动关闭特性是编译器认可的,最终代码会被改为FinallyDemo3的样子
        try(
                //只有实现了AutoCloseable接口的类才可以在这里定义。所有的流都实现了。
                FileOutputStream fos = new FileOutputStream("fos.dat");
        ){
            fos.write(1);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

自动关闭特中的代码格式是编译器认可的,而不是虚拟机,编译器在编译上述代码后会在编译后的class文件中改回finally块中在进行try..catch的格式

throw关键字

throw用来对外主动抛出一个异常,通常下面两种情况我们主动对外抛出异常

  • 当程序遇到一个满足语法,但是不满足业务要求时,可以抛出一个异常告知调用者
  • 当程序执行遇到一个异常,但是该异常不应当在当前代码片段被解决时可以抛出给调用则.

package exception; 
/**
* 测试异常的抛出 
*/ 
public class Person { 
private int age; 

public int getAge() { 
return age;
 }
public void setAge(int age) { 
if(age<0||age>100){ 
//使用throw对外抛出一个异常 
throw new RuntimeException("年龄不合法!"); 
}
this.age = age; 
} 
}

package exception; 
/**
* throw关键字,用来对外主动抛出一个异常。 
* 通常下面两种情况我们主动对外抛出异常: 
* 1:当程序遇到一个满足语法,但是不满足业务要求时,可以抛出一个异常告知调用者。 
* 2:程序执行遇到一个异常,但是该异常不应当在当前代码片段被解决时可以抛出给调用者。 
*/ 
public class ThrowDemo { 
public static void main(String[] args) { 
Person p = new Person(); 
p.setAge(10000);//符合语法,但是不符合业务逻辑要求。 
System.out.println("此人年龄:"+p.getAge()); 
} 
}


throws关键字

  • 当一个方法中使用throw抛出一个非RuntimeException的异常时,就要在该方法上使用throws声明这个异常的抛出情况,此时调用该方法的代码就必须处理这个而异常,否则编译不通过.

注意:永远不要在main方法上使用throws,这个本案例是因为是演示,没有办法

package exception
/**
* 测试异常的抛出 
*/ 
public class Person {
private int age; 
public int getAge() { 
return age; 
}
/**
* 当一个方法使用throws声明异常抛出时,调用此方法的代码片段就必须处理这个异常 
*/ 
public void setAge(int age) throws Exception { 
if(age<0||age>100){ 
//使用throw对外抛出一个异常 
// throw new RuntimeException("年龄不合法!"); 
//除了RuntimeException之外,抛出什么异常就要在方法上声明throws什么异常 
throw new Exception("年龄不合法!"); 
}this.age = age; 
} 
}


当调用一个含有throws声明异常抛出的方法时,编译器要求我们必须处理这个异常,否则编译不通过,处理手段有两种:

  • 1.try-catch捕获并处理这个异常
  • 2.在当前方法(本案例就是main方法上)继续使用throws声明该一次的抛出,给调用者解决,具体选取哪种取决于异常处理的责任问题.
package exception

public class ThrowDemo {
    public static void main(String[] args){
        Person p = new Person();
        try {
            /*
                当我们调用一个含有throws声明异常抛出的方法时,编译器要求我们必须
                处理该异常,处理方式有两种:
                1:使用try-catch主动捕获并处理该异常
                2:可以在当前方法上继续使用throws声明该异常的抛出给上层调用者
                具体选取哪种取决于责任制问题.
             */
            p.setAge(1000);//不满足业务场景
            System.out.println("此人年龄:"+p.getAge()+"岁"); 
         } catch (Exception e) { 
              e.printStackTrace();  
         }
              System.out.println("程序结束了...");
    }
}

含有throws的方法被子类重写时规则(说白了就是抛出小于等于超类的异常)

如何理解:就是超类都没有抛出这个异常,你却抛出了比超类大的,那么你还如何继承超类.

package exception;

import java.awt.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;

/**
 * 重写一个含有throws声明异常抛出的方法,对throws的重写规则
 */
public class ThrowsDemo {
    public void dosome()throws IOException, AWTException{}

}
class SubClass extends  ThrowsDemo{
  // public void dosome()throws IOException,AWTException{}

  /*  //允许不再抛出异常
    public void dosome(){}
    */

  /*  //允许仅仅抛出部分异常
    public void dosome()throws IOException{}
    */

    /*//允许抛出超类方法抛出异常的子类型异常
    public void dosome() throws FileNotFoundException{}
    */

    //不允许抛出额外异常(超类没有超出且也不存在继承关系的异常)
  //  public void dosome()throws SQLException{}

    //不允许抛出超类方法抛出异常的超类型异常
   // public  void dosome()throws Exception{}
}

java异常可以分为检测异常,非检测异常

  • 可检测异常:可检测异常经过编译器验证,对于声明抛出异常的任何方法,编译器将强制执行处理或声明规则,不捕捉这个异常,编译器不通过,不允许编译
  • 非检测异常:非检测异常不遵循处理或声明规则,在产生此类型异常时,不一定非要采取任何适当操作,编译器不会检测是否已经解决了这样一个异常
  • RuntimeException类属于非检测异常,因为普通的JVM操作引起的运行时异常随时可能发生,此类异常一般是由特性操作引发,但这些操作在java应用程序中会频繁出现,因此它们不受编译器检查与吃力或声明规则的限制.

常见的RuntimeException子类

  • IllegalArgumentException:抛出的异常表明方法传递了一个不合法或不正确的参数
  • NullPointerException:当应用程序试图在需要对象的地方使用null时,抛出该异常
  • ArrayIndexOutOfboundsException:当使用数组下标超出数组允许范围时,抛出的异常
  • ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出该异常
  • NumberFormatException:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常.

异常中常用的方法

package exception; 
/**
* 异常常见的方法 
*/
 public class ExceptionApiDemo { 
public static void main(String[] args) { 
System.out.println("程序开始了"); 
try {
String str = "abc"; 
System.out.println(Integer.parseInt(str));
 } catch (NumberFormatException e) { 
//异常最常用的方法,用于将当前错误信息输出到控制台 
e.printStackTrace(); 
//获取错误消息.记录日志的时候或提示给用户可以使用它 
String message = e.getMessage(); 
System.out.println(message);
 }
System.out.println("程序结束了"); 
} 
}

自定义异常

自定义异常通常用来定义那些业务上的异常问题

自定义异常需要注意一下问题:

  • 异常的类名要做到见名知义
  • 需要是Exception子类
  • 提供了超类异常提供的所有种类构造器
package exception; 
/**
* 非法的年龄异常 
*
* 自定义异常通常用来说明业务上的错误. 
* 自定义异常要注意以下问题: 
* 1:定义的类名要做到见名知义 
* 2:必须是Exception的子类 
* 3:提供Exception所定义的所有构造方法 
*/ 
public class IllegalAgeException extends Exception{ 
public IllegalAgeException() { }

public IllegalAgeException(String message) { super(message); }

public IllegalAgeException(String message, Throwable cause) { super(message, cause); }

public IllegalAgeException(Throwable cause) { super(cause); }

public IllegalAgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }

package exception;
 /**
* 测试异常的抛出
*/
 public class Person { 
private int age; 
public int getAge() { 
return age; 
}
/**
* 当一个方法使用throws声明异常抛出时,调用此方法的代码片段就必须处理这个异常 
*/ public void setAge(int age) throws IllegalAgeException { 
if(age<0||age>100){ 
//使用throw对外抛出一个异常 
// throw new RuntimeException("年龄不合法!"); 
//除了RuntimeException之外,抛出什么异常就要在方法上声明throws什么异常 
// throw new Exception("年龄不合法!"); 
//抛出自定义异常 
throw new IllegalAgeException("年龄超范围:"+age); 
}
this.age = age; 
} 
}

package exception; 
/**
* throw关键字,用于主动对外抛出一个异常
 */ 
public class ThrowDemo { 
public static void main(String[] args){
 System.out.println("程序开始了..."); 
try {Person p = new Person(); 
/* 当我们调用一个含有throws声明异常抛出的方法时,编译器要求 
我们必须添加处理异常的手段,否则编译不通过.而处理手段有两种 
1:使用try-catch捕获并处理异常 
2:在当前方法上继续使用throws声明该异常的抛出 具体用哪种取决于异常处理的责任问题 
*/ 
p.setAge(100000);//典型的符合语法,但是不符合业务逻辑要求 
System.out.println("此人年龄:"+p.getAge()+"岁"); 
} catch (IllegalAgeException e) { 
e.printStackTrace(); 
}
System.out.println("程序结束了..."); 
} 
}

总结:

异常处理机制是用来处理那些可能存在的异常,但是无法通过秀爱逻辑完全避免的场景

而如果通过修改逻辑可以规避的异常是bug,不应当用异常处理机制在允许期间解决,应当在编码是及时修正.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Galaxy@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值