异常(java)

一、Java 异常概念

1.概述:在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的。

  • 比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。

2.异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”,也就是在运行时出现的不正常情况,但是可以通过异常处理机制来处理的就叫做异常。

3.Java程序在执行过程中所发生的异常(运行时一切不正常情况)事件可分为两类:

Error : Java虚拟机无法解决的严重问题。

  • 例如:JVM系统内部错误、资源耗尽等严重情况。一般不编写针对性的代码进行处理。

public class Demo1 {
    //对象内存不够用的情况,程序无法处理
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        while(true){
            list.add(new String("abcdef"));
        }
        //Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    }
}

Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以通过异常处理机制处理,处理之后程序可以继续向下执行。

  • 例如: 访问数组下标越界、试图读取不存在的文件、网络连接中断

public class Demo2 {
    //数组下表越界,通过try、catch语句处理
    public static void main(String[] args) {
       try {
           int[] a  = new int[5];
           a[5]= 10;
         /*若不加处理语句,则会抛出如下异常:
           Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
              at com.ffyc.javaexception.Demo1.main(Demo1.java:8)
         */
       }catch (Exception e){
           System.out.println("程序出问题了");
       }
    }
}

举例几种常见的异常类型:

public class Demo2 {
    public static void main(String[] args) {
        //数组越界异常
        int[] a  = new int[5];
        a[5]= 10;
        /*
         Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
             at com.ffyc.javaexception.Demo2.main(Demo2.java:8)
         */
​
        //算术异常
        int c = 10;
        int d = 0;
        int e = c/d;
        System.out.println("aaa");
        /*
         Exception in thread "main" java.lang.ArithmeticException: / by zero
             at com.ffyc.javaexception.Demo2.main(Demo2.java:17)
         */
​
        //空指针异常
        String s = null;
        s.length();
        /*
         Exception in thread "main" java.lang.NullPointerException
              at com.ffyc.javaexception.Demo2.main(Demo2.java:26)
         */
​
        //类转换异常
        Object s1 = "abc";
        Integer i = (Integer) s1;
        /*
         Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
             at com.ffyc.javaexception.Demo2.main(Demo2.java:34)
         */
​
        //数字格式化异常
        new Integer("1a0");
        /*
        Exception in thread "main" java.lang.NumberFormatException: For input string: "1a0"
            at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
            at java.lang.Integer.parseInt(Integer.java:580)
            at java.lang.Integer.<init>(Integer.java:867)
            at com.ffyc.javaexception.Demo2.main(Demo2.java:41)
         */
    }
}

Java中将程序中有可能出现的每一类问题,都包装成了一个类,当出现对应的某类的异常时,虚拟机会抛出响应的问题,对于这些异常,一般有两种解决方法:

①默认抛出异常信息。

②通过异常处理机制处理,在编码时,就需要对有可能出现问题的代码进行处理。

捕获异常最理想的是在编译期间,但有的异常只有在运行时才会发生。

比如:除数为0、数组下标越界...

分类:编译时异常和运行时异常

二、异常的体系

1.Throwable类有两个直接子类

①Error类:表示错误,可能是编译期错误或者系统错误,往往程序中并不处理。

②Exception类:表示异常,是所有异常类的父类,是程序员所关心的。

cff0b511d45f4b13aeef33258bd3c3b6.png

 2.异常分为运行期异常和编译期异常两种

①运行期异常:程序运行时抛除的异常,所有RuntimeException的子类都是运行期异常 ,在编译期间,可以不用强制处理。

例如:数学异常、空指针异常、数组下标越界

②编译期异常(Checked Exception):没有继承RuntimeException,除去运行期的异常都是编译期异常,也称为检测异常,编译时就强制的要进行处理。

例如:IOException、SQLException...

e0bc83be82b248fe9a488b85ff1122a1.png

 三、异常处理

Java编程语言使用异常处理机制为程序提供了处理错误的能力。

515e8e01c168440a9109e891d9268d79.png

注意:处理完毕并不代表是把坏的处理好了

例如:用户在使用计算器计算除法时,除数输入了0,此时不符合运算规则,但是我们不能改变用户的意愿,我们能做的处理就是将问题给用户提示出来,不让程序卡死,让后续程序可以继续执行。

 86483144175247e68e7a9f92ea0fbe78.png

 

Java的异常处理是通过5个关键字来实现的:try、catch、 finally、throw、throws

b9cb828562744a36bd7878b208f9a44c.png

基本语法:

 

try{
   可能会出现异常的代码 
}catch(异常类型 引用名){ 
   处理办法(处理不是把坏的处理好了)
}finally{ 
   必须执行代码 
}

try:检测不安全的代码块(发现异常)

try块中任何一条语句发生了异常,下面的代码将不会被执行,程序将跳转到异常处理代码块中,即catch块。因此,不要随意将不相关的代码放到try块中,因为随时可能会中断执行。

catch:把抓到的类型匹配的异常捕获,保证程序能继续运行下去

catch语句必须紧跟着try语句之后,称为捕获异常,也就是异常处理函数,一个try后面可以写多个catch,分别捕获不同类型的异常,要从子类往父类的顺序写,否则有编译错误 。

捕获异常的有关信息:

与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法。

  • getMessage() 获取异常信息,返回字符串

  • printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。

a30c8252e514417595b6bd26e9fb2905.png

finally:即使try里边的代码块出现了异常,该内容总是会执行的,只能有一个finally语句,所以finally中就可以写一些即使出现了异常,也要执行的代码

finally{ 
  必须执行的逻辑 
}

异常处理的方式:

①try、catch

案例:

public class Demo3 {
    public static void main(String[] args) {
     /*
         1.什么样的代码写在try{ } ?
         2.catch中异常的类型是什么?
         try{
            可能会出现异常的代码
         }catch(异常类型 e){
            处理办法(处理不是把坏的处理好了)
         }
      */
        try {
            int a = 10;
            int b = 0;
            new Integer("a");
            int c = a / b;
        //ArithmeticException 算术异常
        }catch(ArithmeticException  a){
            a.printStackTrace();
            System.out.println("除数不能为0");
        }catch (NumberFormatException n){
            n.printStackTrace();
            System.out.println("数字格式不正确"+n.getMessage());
        }catch (Exception e){
            //打印栈中出现的日志信息
            e.printStackTrace();
            System.out.println("系统忙,请稍后再试!");
        }
        System.out.println("aaa");
    }
}

注意:

我们在不知道还会出现哪种类型的异常时,可以在后面加Exception,这是即使没有对应精确的异常类型捕获,也会被Exception捕获,因为它是所有异常的父类,可以用它来捕获它的子类。

②try、catch、finally

import java.io.FileNotFoundException;
public class Demo4 {
       public static void main(String[] args) throws FileNotFoundException {
            try {
                int a = 10;
                int b = 0;
                int c = a / b;
                new Integer("a");
            }catch (NumberFormatException n){
                n.printStackTrace();
                System.out.println("数字格式不正确"+n.getMessage());
            }finally {//无论是否出现异常,都会执行finally代码块
                System.out.println("bbbbbbbbbbbbbbbb");
            }
            System.out.println("aaa");
        }
}

③try、finally

一般在写底层方法时,异常是不需要用catch处理的,因为底层如果处理了并在catch中随便返回了一个结果,上层这个时候就无法知道底层出现了异常,还带着底层随便返回的结果继续向下执行,这样就会出问题,所以在有些场景下我们并不处理异常,我们将需要执行的代码放在finally中执行,底层方法就崩掉了,上层在调用时就知道底层方法出现了异常,也就是在出现异常时不要藏着掖着,留给上层调用时处理,即try里面出现异常直接执行finally,没有处理异常,由上层程序来处理。

import java.io.FileNotFoundException;
​
public class Demo5 {
        public static void main(String[] args) throws FileNotFoundException {
          try {
             test();//由上层调用的程序来处理
          }catch (Exception e){
             System.out.println("除数不能为0");
          }
          System.out.println("aaaaaaaa");
        }
    
        public  static void  test(){
            try {
                int a = 10;
                int b = 0;
                int c = a / b;//没有处理异常
            }finally {
                System.out.println("sdfdsf");
            }
        }
}

四、throws 和 throw

(一) throws,定义一个方法的时候可以使用throws关键字声明,表示此方法不处理异常,而交给方法调用处进行处理。

例如:

public void test() throws 异常1,异常2,异常3{ 
​
} 

注意:

①任何方法都可以使用throws关键字声明异常类型,包括抽象方法。

案例:

public abstract class Animal {
    public abstract void eat() throws NullPointerException;
}

②子类重写父类中的方法,子类方法不能声明抛出比父类类型更大的异常(针对编译期异常,因为运行期异常在编译的时候不检测,所以怎么写无所谓)。

案例:

import java.io.IOException;
public abstract class Animal {
    /*
       方法重写要求:返回值、方法名、参数一致
                  访问权限等于、大于父类方法
                  声明异常等于、小于父类方法(针对编译期异常)
     */
     abstract void eat() throws IOException;
}
public class Cat extends Animal{
    @Override
    protected void eat() throws NullPointerException {
​
    }
}

③使用了throws的方法,调用时必须处理声明的异常,要么使用try-catch,要么继续使用throws声明。

案例:

import java.io.UnsupportedEncodingException;
public class Demo6 {
    public static void main(String[] args) {
        //  "abc".getBytes("utf-8");
        try {
            test1();//main方式此时已经是最顶层了,就不能再抛出了,再抛就抛给虚拟机了. 此时在顶层就需要try catch处理
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
​
    }
    /*
       继续向上抛出
    */
    public static void test1() throws UnsupportedEncodingException, NumberFormatException{
        test2();
        System.out.println("ccccc");
    }
    /*
         throws在方法参数列表后面声明此方法可能会出现异常,
         谁调用此方法,在调用处需要根据情况对象异常是否处理
     */
    public static void test2() throws UnsupportedEncodingException, NumberFormatException {
        "abc".getBytes("gbk");
        System.out.println("aaaaaaa");
    }
}

(二)throw关键字用于显式抛出异常,抛出的时候是抛出的是一个异常类的实例化对象.

在异常处理中,try语句要捕获的是一个异常对象,那么此异常对象也可以自己抛出。

语法:

throw new 异常类构造方法

案例:getBytes()方法底层实现,如果传入的字符串为null,则抛出一个空指针异常的实例化对象

 0356144049284115a04d942778952d8f.png

 

import java.io.UnsupportedEncodingException;
public class Demo7 {
    public static void main(String[] args) {
        try {
            String s = null;
            byte[] b = "abc".getBytes(s);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        System.out.println("aaa");
    }
}
/*
  Exception in thread "main" java.lang.NullPointerException
      at java.lang.String.getBytes(String.java:917)
    at com.ffyc.javaexception.Demo7.main(Demo7.java:11)
​
public byte[] getBytes(String charsetName)
        throws UnsupportedEncodingException {
        if (charsetName == null) throw new NullPointerException();
        return StringCoding.encode(charsetName, value, 0, value.length);
}
*/

如: throw new RunTimeException();

public static void someMethod() { 
    if (1==1) { 
        throw new RuntimeException("错误原因"); 
    } 
}

(三)throws和throw

① throw用于方法体中,用来抛出一个实际的异常对象,使用throw后,要么使用try、catch捕获异常,要么使用throws声明异常。

②throws用于方法声明处,用来声明该方法可能发生的异常类型,可以是多个异常类型,用来强制调用该方法时处理这些异常。

③抽象方法也可以使用throws。

五、自定义异常

自定义异常就是自己定义的异常类,也就是API中的标准异常类的直接或间接的子类

作用:用自定义异常标记业务逻辑的异常,避免与标准异常混淆自定义异常类

基本语法 :

public class 异常类名 extends Exception/RuntimeException{ 
    public 异常类名(String msg){
              super(msg); 
    } 
} 

注意:

①自定义异常类中往往不写其他方法,只重载需要使用的构造方法

②继承Exception,在方法中使用throw抛出后,必须在方法中try-catch或throws抛出

自定义异常示例:

/*
  当分数不满足要求时抛出此类对象
 */
public class ScoreException extends  Exception{
​
    public ScoreException() {
    }
​
    public ScoreException(String message) {
        super(message);
    }
}
public class Demo7 {
    /*
        throw: 在方法体内,抛出一个异常的对象,抛出后,程序不继续向下执行,一般在方法中不符合业务逻辑要求时,可以主动使用throw抛出异常,告诉调用的地方法,程序出现问题。
        throws: 在方法声明处,声明此方法可能会出现异常,此方法不处理异常。
     */
    public static void main(String[] args) {
        try {
            test(10);
        } catch (ScoreException e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }
    public static char test(int score) throws ScoreException {
        /*
           throw:在方法体内,抛出一个异常对象,抛出后,程序不能继续向下执行,
           一般在方法中不符合业务逻辑要求时,可以主动使用throw抛出异常,告诉调用的方法,程序出现问题
         */
        if (score < 0 || score > 100) {
            //不合法分数,没必要继续向下执行,此处可以使用throw直接在方法中抛出一个异常对象
            //主动抛出一个异常对象,让方法终止
            throw new ScoreException("不合法的分数");
        }
        if (score >= 80 || score <= 100) {
            return 'A';
        }
        if (score >= 60 || score < 80) {
            return 'B';
        }
        else {
            return 'C';
        }
    }
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

chen☆

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

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

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

打赏作者

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

抵扣说明:

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

余额充值