异常处理基础

异常处理

一 什么是程序的异常

异常 :指的是程序在执行过程中,出现的非正常情况,如果不处理最终会导致JVM的非正常停止。

二 异常的抛出机制

Java中把不同的异常用不同的类表示,一旦发生某种异常,就创建该异常类型的对象,并且抛出(throw)。然后程序员可以捕获(catch)到这个异常对象,并处理;如果没有捕获(catch)这个异常对象,那么这个异常对象将会导致程序终止。

三 Java异常体系

1 Throwable

java.lang.Throwable 类是Java程序执行过程中发生的异常事件对应的类的根父类。

2 Error 和 Exception

Throwable可分为两类:Error和Exception。分别对应着java.lang.Errorjava.lang.Exception两个类。

**Error:**Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。一般不编写针对性的代码进行处理。

  • 例如:StackOverflowError(栈内存溢出)和OutOfMemoryError(堆内存溢出,简称OOM)。

Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,需要使用针对性的代码进行处理,使程序继续运行。否则一旦发生异常,程序也会挂掉。例如:

  • 空指针访问
  • 试图读取不存在的文件
  • 网络连接中断
  • 数组角标越界

3 编译时异常和运行时异常

  • 编译时期异常(即checked异常、受检异常):在代码编译阶段,编译器就能明确警示当前代码可能发生(不是一定发生)xx异常,并明确督促程序员提前编写处理它的代码。如果程序员没有编写对应的异常处理代码,则编译器就会直接判定编译失败,从而不能生成字节码文件。通常,这类异常的发生不是由程序员的代码引起的,或者不是靠加简单判断就可以避免的,例如:FileNotFoundException(文件找不到异常)。
  • 运行时期异常(即runtime异常、unchecked异常、非受检异常):在代码编译阶段,编译器完全不做任何检查,无论该异常是否会发生,编译器都不给出任何提示。只有等代码运行起来并确实发生了xx异常,它才能被发现。通常,这类异常是由程序员的代码编写不当引起的,只要稍加判断,或者细心检查就可以避免。
    • java.lang.RuntimeException类及它的子类都是运行时异常。比如:ArrayIndexOutOfBoundsException数组下标越界异常,ClassCastException类型转换异常。

4 运行时常见的异常举例

package com.atguigu.exception;

import org.junit.Test;

import java.util.Scanner;

public class TestRuntimeException {
    @Test
    public void test01(){
        //NullPointerException
        int[][] arr = new int[3][];
        System.out.println(arr[0].length);
    }

    @Test
    public void test02(){
        //ClassCastException
        Object obj = 15;
        String str = (String) obj;
    }

    @Test
    public void test03(){
        //ArrayIndexOutOfBoundsException
        int[] arr = new int[5];
        for (int i = 1; i <= 5; i++) {
            System.out.println(arr[i]);
        }
    }

    @Test
    public void test04(){
        //InputMismatchException
        Scanner input = new Scanner(System.in);
        System.out.print("请输入一个整数:");//输入非整数
        int num = input.nextInt();
        input.close();
    }

    @Test
    public void test05(){
        int a = 1;
        int b = 0;
        //ArithmeticException
        System.out.println(a/b);
    }
}

5 编译时常见的异常举例

package com.atguigu.exception;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class TestCheckedException {
    @Test
    public void test06() {
        Thread.sleep(1000);//休眠1秒  InterruptedException
    }

    @Test
    public void test07(){
        Class c = Class.forName("java.lang.String");//ClassNotFoundException
    }
    
    @Test
    public void test10() {
        File file = new File("hello.txt");
		FileInputStream fis = new FileInputStream(file);//FileNotFoundException
		int b = fis.read();//IOException
		while(b != -1){
			System.out.print((char)b);
			b = fis.read();//IOException
		}
		
		fis.close();//IOException
    }
}

四 异常处理的方式

方式1(抓抛模型):try-catch-finally

过程1:“抛”

程序在执行过程中,一旦出现异常,就会在出现异常的代码处,生成对应异常类的对象,并将此对象抛出。

一旦抛出,此程序就不执行其后的代码了。

过程2:“抓”

针对于过程1抛出的异常对象,进行捕获处理。此捕获处理的过程,就称为“抓”。

一旦将异常进行了处理,代码就可以继续执行、

2 基本格式

try{
	......	//可能产生异常的代码
}
catch( 异常类型1 e ){
	......	//当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){
	...... 	//当产生异常类型2型异常时的处置措施
}  
finally{
	...... //无论是否发生异常,都无条件执行的语句
} 

3 使用细节:

  • 将可能出现异常的代码声明在try语句中,一旦代码出现异常,就会自动生成一个对应异常类的对象。并将此对象抛出。

  • 针对于try中抛出的异常类的对象,使用之后的catch语句进行匹配。一旦匹配上,就进入catch语句块进行处理。一旦处理结束,代码就可继续向下执行。

  • 如果声明了多个catch结构,不同异常类型在不存在子父类关系的情况下,谁声明在上面,谁声明在下面都可以。

  • 如果多个异常类型满足子父类的关系,则必须将子类声明在父类结构的上面,否则就会报错。

  • catch中异常处理的方式:

    1 自己编写输出的语句

    2 printStackTrace():打印异常的详细信息(推荐)

    3 getMessage():获取发生异常的原因

  • try中声明的变量,出了try结构之后,就不可以进行调用了。

  • try-catch结构是可以嵌套使用的

4 举例:

public class IndexOutExp {
    public static void main(String[] args) {
        String friends[] = { "lisa", "bily", "kessy" };
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println(friends[i]);
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            //System.out.println("index err");
            e.printStackTrace();
        }
        System.out.println("程序结束");
    }
}

4 finally的使用说明

4.1 我们将一定要被执行的代码声明在finally结构中。

4.2 更深刻的理解:无论try中或catch中是否存在仍未被处理的异常,无论try中或catch中是否存在return语句等,finally中声明的语句都一定要被执行。

4.3 finally语句和catch语句是可选的,但finally不能单独使用

4.4 举例:

public class FinallyTest1 {
    public static void main(String[] args) {
        int result = test("12");
        System.out.println(result);

    }
    public static int  test(String string){
        try{
            Integer.parseInt(string);
            return 1;
        }catch(NumberFormatException e){
            e.printStackTrace();
            return -1;

        }finally{
            System.out.println("程序结束");
        }

    }
}
//运行结果:

//test结束

//1
public class FinallyTest2 {
    public static void main(String[] args) {
        int result = test1("a");
        System.out.println(result);
    }
    public static int test1(String string){
        try{
            Integer.parseInt(string);
            return 1;
        }catch(NumberFormatException e){
            e.printStackTrace();
            return -1;
        }finally{
            System.out.println("程序结束");
        }
    }
}
//运行结果:

//test结束

//-1
public class FinallyTest3 {
    public static void main(String[] args) {
        int result = test("a");
        System.out.println(result);
    }

    public static int test(String str) {
        try {
            Integer.parseInt(str);
            return 1;
        } catch (NumberFormatException e) {
            return -1;
        } finally {
            System.out.println("test结束");
            return 0;
        }
    }
}
//运行结果:

//test结束

//0

方式2:声明抛出异常类型(throws)

如果在编写方法体的代码时,某句代码可能发生某个编译时异常,不处理编译不通过,但是在当前方法体中可能不适合处理无法给出合理的处理方式,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。

1 throws基本格式

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2{
	//可能存在编译时异常
}	

在throws后面可以写多个异常类型,用逗号隔开。

2 举例:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ThrowsTest {
    public static void main(String[] args) {
        mothods3();

    }

    public static void mothods3(){
        try{
            mothods2();
        }catch(FileNotFoundException e){
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            System.out.println("程序结束");
        }

    }


    public static void mothods2() throws FileNotFoundException,IOException{//mothods也解决不了继续往上抛
        mothods1();
    }

    public static void mothods1() throws FileNotFoundException, IOException {
        File file = new File("hello.txt");
        FileInputStream fis = new FileInputStream(file);//可能会报FileNotFoundException
        int data = fis.read();
        while(data != -1){
            System.out.println((char)data);
            data = fis.read();
        }
        fis.close();//可能会报IOException
    }
}

3 方法重写中throws的要求

对于throws异常列表要求:

  • 如果父类被重写方法的方法签名后面没有 “throws 编译时异常类型”,那么重写方法时,方法签名后面也不能出现“throws 编译时异常类型”。
  • 如果父类被重写方法的方法签名后面有 “throws 编译时异常类型”,那么重写方法时,throws的编译时异常类型必须 <= 被重写方法throws的编译时异常类型,或者不throws编译时异常。
  • 方法重写,对于“throws 运行时异常类型”没有要求。

举例:

import java.io.IOException;

class Father{
    public void method()throws IOException,ClassCastException{
        System.out.println("Father.method");
    }
}
class Son extends Father{
    @Override
    public void method() throws IOException,ClassCastException {
        System.out.println("Son.method");
    }
}

4 两种异常处理方式的选择

前提:对于异常,使用相应的处理方式。此时的异常,主要指的是编译时异常

  • 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须考虑使用try-catch-finally来处理,保证不出现内存泄漏。

  • 如果父类被重写的方法没有throws异常类型,则子类重写的方法中如果出现异常,只能考虑使用try-catch-finally进行处理,不能throws。

  • 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,我们通常选择使用throws,而方法a中通常选择使用try-catch-finally。4 两种异常处理方式的选择

五 手动抛出异常对象:throw

1 使用格式

throw new 异常类名(参数);

如果是编译时异常类型的对象,同样需要使用throws或者try…catch处理,否则编译不通过。

2 使用注意点:

无论是编译时异常类型的对象,还是运行时异常类型的对象,如果没有被try…catch合理的处理,都会导致程序崩溃。

throw语句会导致程序执行流程被改变,throw语句是明确抛出一个异常对象,因此它下面的代码将不会执行

如果当前方法没有try…catch处理这个异常对象,throw语句就会代替return语句提前终止当前方法的执行,并返回一个异常对象给调用者。

3 举例:

package Test1;

public class ThrowTest {
    public static void main(String[] args) {
        Student student = new Student();
        try{

            student.regist(10);
            student.regist(-18);
            System.out.println(student);
        }catch(Exception e){
            e.printStackTrace();
        }

    }
}

package Test1;

public class Student {
    int id;
    public void regist(int id) throws Exception {
        if(id > 0){
            this.id = id;
        }else{
            throw new Exception("输入非法字符");
        }
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                '}';
    }
}

六 自定义异常

1 如何自定义异常类

(1)要继承一个异常类型

​ 自定义一个编译时异常类型:自定义类继承java.lang.Exception

​ 自定义一个运行时异常类型:自定义类继承java.lang.RuntimeException

(2)建议大家提供至少两个构造器,一个是无参构造,一个是(String message)构造器。

(3)自定义异常需要提供serialVersionUID

2 注意点

  1. 自定义的异常只能通过throw抛出。
  2. 自定义异常最重要的是异常类的名字和message属性。当异常出现时,可以根据名字判断异常类型。比如:BelowZeroException("除0");
  3. 自定义异常对象只能手动抛出。抛出后由try…catch处理,也可以甩锅throws给调用者处理。

3 举例:

package Test1;

public class DivisionDemo {
    public static void main(String[] args) {
        try{
            int m = Integer.parseInt(args[0]);
            int n = Integer.parseInt(args[1]);
            int result = divide(m,n);
            System.out.println(result);
        }catch(BelowZeroException e){
            System.out.println(e.getMessage());
        }catch(NumberFormatException e){
            System.out.println("数据类型不一致");
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("缺少命令行参数");
        }catch(ArithmeticException e){
            System.out.println("除0");
        }

    }
    public static int divide(int m,int n) throws BelowZeroException{//手动抛出
        if(m < 0 || n < 0){
            throw new BelowZeroException("输入负数");
        }else{
            return m / n ;
        }
    }
}

package Test1;

public class BelowZeroException extends Exception{
    public static final long serialVertionUID = -33875169939948L;
    public BelowZeroException() {

    }

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

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值