异常的介绍

异常

  • 概念:在程序运行过程中出现的一种意外的情况,而非编译错误。

1 异常的分类

  • 比如:
    1. Throwable:所有异常的父类。
    2. Error:Throwable子类,表示错误,JVM崩溃,硬件出现问题或与操作系统相关的问题;特点:无法避免,无法处理。
    3. Exception:Throwable子类,表示异常,编译时异常或已检查异常。
    4. RuntimeException:Exception子类,表示异常,运行时异常或未检查异常。
    
    Exception所有的子类都是已检查异常,编译时异常。
    是一种程序的已知异常,必须手动处理予以解决方案,否则编译错误。
    特点:无法避免,必须处理。
    
    RuntimeException所有的子类都是未检查异常,运行时异常。
    是一种因为程序员不好好检查出现的异常,可以提供解决方案也可以不提供,编译器不干预。
    特点:可以避免,可处理可不处理。
    

2 异常的产生方式

2.1 自动抛出异常
  • 写程序时不严谨,不好好检查,用错误的使用方式自动出现的异常。
    演示:数组下标越界异常。
    String str = "ABC";
    char[] cs = str.toCharArray();
    System.out.println( cs[3] );
    
2.2 手动抛出异常
  • 语法如下:
    throw 异常对象;
    
    演示:
    NullPointerException npe = new NullPointerException();
    throw npe;
    
    throw new NullPointerException();
    throw new NullPointerException("自定义异常描述信息");
    
2.3 异常对程序的影响
  • 程序会因为异常的出现而终止。
  • 异常传递过程:
    类似于方法的返回值,将异常对象沿着方法调用链逐层返回。
    
    演示的代码如下:
package com.txw.test;

public class TestException {
    public static void main(String[] args) {
        System.out.println("main---start");
        m1( 0 );
        System.out.println("main---end");

    }
    public static void m1(int n){
        System.out.println("m1---start");
        // 异常对象
        // NullPointerException npe = new NullPointerException();
        // if(n==0) throw npe;
        m2( n );
        System.out.println("m1---end");
    }
    public static void m2(int n){
        System.out.println("m2---start");
        if(n==0)throw new NullPointerException("空指针异常");
        System.out.println("m2---end");
    }
}

3 异常的处理方式【重点】

3.1 声明抛出【消极处理】
  • 语法:在出现已检查的方法中添加声明抛出。
    访问权限修饰符 返回值类型 方法名(参数表) throws 异常,异常2...{
        
    }
    
    声明抛出对程序的影响:自己不处理谁调用谁处理。
    演示:
    public static void main(String[] args) throws Exception{
            System.out.println("main---start");
            m1( 2 );
            System.out.println("main---end");
    
        }
    
        public static void m1(int n)throws IOException,SQLException {
            System.out.println("m1---start");
            m2( n );
            System.out.println("m1---end");
        }
        public static void m2(int n)throws EOFException,FileNotFoundException,SQLException{
            System.out.println("m2---start");
            if(n==0)throw new NullPointerException("空指针异常");
            if(n==1)throw new EOFException();
            if(n==2)throw new FileNotFoundException();
            if(n==3)throw new SQLException();
            System.out.println("m2---end");
        }
    
    可以声明抛出父类异常处理所有子类异常(多态)。
    演示的代码如下:
package com.txw.test;

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
public class TestThrows {
    public static void main(String[] args) throws Exception{
        System.out.println("main---start");
        m1( 2 );
        System.out.println("main---end");
    }
    public static void m1(int n)throws IOException,SQLException {
        System.out.println("m1---start");
        m2( n );
        System.out.println("m1---end");
    }
    public static void m2(int n)throws EOFException,FileNotFoundException,SQLException{
        System.out.println("m2---start");
        if(n==0)throw new NullPointerException("空指针异常");
        if(n==1)throw new EOFException();
        if(n==2)throw new FileNotFoundException();
        if(n==3)throw new SQLException();
        System.out.println("m2---end");
    }
}
3.2 捕获处理【积极处理】
  • 当方法中出现异常时,可以捕获异常对象,跳转到专门用于处理异常的代码块中,处理完毕后程序由异常自动转换为正常,程序不会因为异常的产生而意外终止,可以配合throws一起使用。
  • 语法如下:
    try{
        // 可能出现异常的代码
        // 用于捕获异常
    }catch(声明处理的异常1 | 声明处理异常2 e){
        [可选]e.printStackTrace();		// 打印异常栈信息(异常来源)
        [可选]String message = e.getMessage();	// 打印描述信息
             System.out.println(message);
        [可选]throw e;		// 再次声明抛出异常
        
    }catch(声明处理的异常 e){
        
    }...finally{
        // 无论是否有异常产生,必定执行
        // 通常用于释放(关闭)资源
    }
    
    演示的代码如下:
package com.txw.test;

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.sql.SQLException;
import java.util.zip.DataFormatException;
public class TestTryCatch2 {
    public static void main(String[] args) {
        try {
            m1(1);
        }catch(SQLException e){

        }catch(Exception e){

        }
    }

    public static void m1(int n) throws SQLException,DataFormatException{		// 耳鼻喉科
        try {
            System.out.println("m1------1");
            m2( n );
            System.out.println("m1------2");
        }catch(FileNotFoundException | EOFException e) {
            e.printStackTrace();
        }
            /*catch(IOException e){
            //  可以处理IOException或任意子类异常
        }*/
        System.out.println("m1------3");
    }
    public static void m2(int n)throws EOFException,FileNotFoundException,SQLException,DataFormatException{
        System.out.println("m2---start");
        if(n==0)throw new NullPointerException("空指针异常");
        if(n==1)throw new EOFException("文件已读完!!!!");	// 打喷嚏
        if(n==2)throw new FileNotFoundException();		// 嗓子疼
        if(n==3)throw new SQLException();				// 牙疼
        if(n==4)throw new DataFormatException();		// 头疼
        System.out.println("m2---end");
    }
}

可以声明处理父类异常,当需要多个catch时,遵循从子到父的顺序。
演示的代码如下:

package com.txw.test;

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.sql.SQLException;
public class TestTryCatch1 {
    public static void main(String[] args) {
        System.out.println("main---start");
        m1( 1 );
        System.out.println("main---end");
    }
    public static void m1(int n) {
        try {
            // 捕获异常对象,为catch中异常引用赋值
            System.out.println("m1---start");
            m2(n);
        }catch(EOFException e){
            // 出现EOFException时执行
            // System.out.println("catch EOFException");
            // e.printStackTrace();			// 打印异常信息
            String message = e.getMessage();
            System.out.println(message);
        }catch(FileNotFoundException e){
            // System.out.println("catch FileNotFoundException");
        }catch(SQLException e){
            // System.out.println("catch SQLException");
        }
        System.out.println("m1---end");
    }
    public static void m2(int n)throws EOFException,FileNotFoundException,SQLException{
        System.out.println("m2---start");
        if(n==0)throw new NullPointerException("空指针异常");
        if(n==1)throw new EOFException("文件已读完!!!!");
        if(n==2)throw new FileNotFoundException();
        if(n==3)throw new SQLException();
        System.out.println("m2---end");
    }
}

演示:

try {
          
}catch(SQLException e){

}catch(Exception e){

}

注意:try框中的代码不一定执行,当方法需要返回值时,return语句尽量写在外层。

try{
	1. 登录输入卡号密码
	2. 输入转账金额
	3. 验证余额
	4. 从原账户扣款 -2000 出现异常
	5. 为对方账户增加余额
}catch(Exception e){
	原账户余额 +2000
}finally{
	退卡
}	
  • 常见的try-catch结构。
    1. try{ }catch(Exception e){ }
    2. try{ }catch(Exception e){ }finally{   }
    3. try{ }finally{  }
    
    如果finally代码框中有return语句,那么将作为该方法最终结果返回。
    关于异常:一般情况下程序员不会手动抛出异常,因为代价太大,通常情况都是在处理由框架或其他类抛出的已检查异常。
    演示的代码如下:
package com.txw.test;

import java.io.EOFException;
import java.sql.SQLException;
public class TestTryCatchFinally {
    public static void main(String[] args)throws SQLException{
        System.out.println("main---start");
        m1( 2 );
        System.out.println("main---end");
    }

    public static void m1(int n) throws SQLException{
        try {
            System.out.println("m1---1");
            m2(n);
            System.out.println("m1---2");
        }catch(EOFException e){
            e.printStackTrace();
        }finally{
            System.out.println(" finally {   }");
        }
        System.out.println("m1---3");
    }
    public static void m2(int n)throws EOFException,SQLException{
        System.out.println("m2---start");
        if(n==0)throw new NullPointerException("空指针异常");
        if(n==1)throw new EOFException();
        if(n==2)throw new SQLException();
        System.out.println("m2---end");
    }
}

4 自定义异常

  • 自定义已检查异常。
    // 继承Exception
    class MyException1 extends Exception{
        public MyException1(){}
        public MyException1(String message){
            super(message);			// 使用父类构造方法,添加描述信息
        }
    }
    // 抛出异常
    throw new MyException1("自定义已检查异常");
    
  • 自定义未检查异常[推荐]。
    // 继承RuntimeException
    class MyException2 extends RuntimeException{
       public MyException2(){}
        public MyException2(String message){
            super(message);		// 使用父类构造方法,添加描述信息
        } 
    }
    // 抛出异常
    throw new MyException2("自定义已检查异常");
    

演示的代码如下:

package com.txw.test;

public class TestMyException {
    public static void main(String[] args) throws MyException1{
        m1(2);
    }
    public static void m1(int n)throws MyException1{
        if(n==1)throw new MyException1("自定义已检查异常");			// 抛出自定义已检查异常
        if(n==2)throw new MyException2("自定义未检查异常");
    }
}
// 自定义已检查异常
class MyException1 extends Exception{
    public MyException1(){}
    public MyException1(String message){
        super(message);		// 使用父类构造方法
    }
}
// 自定义未检查异常
class MyException2 extends RuntimeException{
    public MyException2(){}
    public MyException2(String message){
        super(message);
    }
}

5 方法覆盖的语法要求

  • 语法要求:
    访问权限修饰符相同或更宽,返回值类型、方法名、参数表与父类一致。
    不能比父类抛出更宽泛的异常。
    
    演示的代码如下:
    class Super{
        public void m1()throws IOException {}
    }
    class Sub extends Super{
        @Override
        public void m1()throws FileNotFoundException, EOFException {}
        // 虽然m1方法抛出的异常数量多,但是都是IOException的子类,并没有比父类方法抛出的异常更宽泛
    }
    
  • 面试问题
    1. final、finally、finalize三个的区别
    final 是修饰符
    finally 是代码块
    finalize 是Object类中的一个方法
    
    演示的代码如下:
package com.txw.test;

import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
public class TestOverride {
    public static void main(String[] args){
        // 可以表示计算机中的一个文件
        try {
            File f = new File("D:/abc.txt");
            f.createNewFile();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
class Super{
    public void m1()throws IOException {}
}

class Sub extends Super{
    @Override
    public void m1()throws FileNotFoundException, EOFException {}
}
package com.txw.test;

public class TestReturnResult {
    public static void main(String[] args) {
        System.out.println(method());
    }
    public static int method(){
        // 返回值通常都声明在try框外
        int count = 0;
        try {
            // 定义计数变量
            for (int i = 1; i <= 999; i++) {
                if(i%2==0)count++;
            }
            return 1;
        }catch(Exception e){
            e.printStackTrace();
            return 2;
        }finally{
            return 2000;
        }
    }
}

习题

1.Java 中所有的错误都继承自 Throwable 类;在该类的子类中,Error 类表示严重的底层错误,对于这类错误,程 序代码不可处理;Exception 表示例外、异常。
2. 查阅 API,完成以下填空:
(1) 异常类 java.rmi.AlreadyBoundException,从分类上说,该类属于已检(已检查|运行时)异常, 从处理方式上说,对这种异常必须处理。
(2) 异常类 java.util.regex.PatternSyntaxException,从分类上说,该类属于|运行时(已检查|运行时) 异常,从处理方式上说,对这种异常可以处理也可以不处理。
3. Java 中用来抛出异常的关键字是( C )。
A. try
B. catch
C. throw
D. finally
4. 在异常处理中,释放资源、关闭文件等应由____处理(C ) 。
A. try 语句
B. catch 语句
C. finally 语句
D. throw 语句
5. finally 语句块中的代码(A) 。
A. 总是被执行
B. 当 try 语句块后面没有 catch 时,finally 中的代码才会被执行
C. 异常发生时才被执行
D. 异常没有发生时才被执行
6. 自定义异常类时,可以继承的类是(C )
A. Error
B. ArrayList
C. Exception
D. NullPointerException
7. 对于 try{ … }catch…语句的排列方式,下列描述正确的是(A )。
A. 子类异常在前,父类异常在后
B. 父类异常在前,子类异常在后
C. 只能有子类异常
D. 父类异常不能与子类异常同时出现
8. 仔细阅读以下代码,将代码补全。

package com.txw.test;

public class TestThrow {
    public static void main(String[] args) {
        throwException(10);
    }

    public static void throwException(int n){
        if (n == 0) {
            // 抛出一个NullPointerException
        }else {
            // 抛出一个ClassCastException
            // 并设定详细信息为类型“转换出错”
        }
    }
}

答:代码如下:

package com.txw.test;

public class TestThrow {
    public static void main(String[] args) {
        throwException(10);
    }

    public static void throwException(int n){
        if (n == 0) {
            // 抛出一个NullPointerException
            throw  new NullPointerException();
        }else {
            // 抛出一个ClassCastException
            // 并设定详细信息为"类型转换出错”
            throw new ClassCastException("类型转换出错");
        }
    }
}
  1. 代码改错:仔细阅读以下程序,将错误的代码进行改正。
package com.txw.test;

class MyException{

}
public class TestException {
    public static void main(String[] args) {
        ma();
    }
    public static int ma(){
        try {
            m();
            return 100;
        } catch (Exception e) {
            System.out.println("Exception");
        }catch (ArithmeticException e){
            System.out.println("ArithmeticException");
        }
    }

    public static void m(){
        throw new MyException();
    }
}

答:改正的代码如下:

package com.txw.test;

// MyException类必须要继承自Exception的某一个子类
class MyException extends RuntimeException{

}
public class TestException {
    public static void main(String[] args) {
        ma();
    }

    public static int ma(){
        try {
            m();
        } catch (ArithmeticException e) {
            System.out.println("ArithmeticException");
        }catch (Exception e){           // 捕获Exception的语句应当放在所有catch语句的最后。
            System.out.println("Exception");
        }
        return 100;
    }

    // 根据 MyException是否继承自RuntimeException
    // 此处考虑是否声明抛出
    public static void m(){
        throw new MyException();
    }
}
  1. 仔细阅读以下代码,当读入的 n 分别为 1,2,3,4,5 时,输出的结果分别是什么?
    在这里插入图片描述
    在这里插入图片描述
    答:
    I. n = 1 时,输出 main1 ma1 mb1 Catch EOFException In finally main2
    II. n = 2 时,输出 main1 ma1 mb1 Catch IOException In finally main2
    III. n = 3 时,输出 main1 ma1 mb1 Catch SQLException In finally main2
    IV. n = 4 时,输出 main1 ma1 mb1 Catch Exception In finally main2
    V. n = 5 时,输出 main1 ma1 mb1 mb2 ma2 In finally main2 注意:不论是否出现异常,出现什么异常,In finally 语句都会被打印出来。
  2. 仔细阅读以下代码:
    在这里插入图片描述
    在//1 处,填入以下__A B____代码可以编译通过,在//2 处,填入____D__代码可以编译通过。
    A. throws java.io.IOException
    B. throws java.io.FileNotFoundException, java.io.EOFException
    C. throws java.sql.SQLException
    D. 不能抛出任何异常
    原因:
    I. 根据方法覆盖的要求,子类的覆盖方法不能比父类的被覆盖方法抛出更宽泛的异常。
    II. ma 方法抛出 IOException,则子类可以抛出 IOException,也可以抛出 IOException 的子类异常。
    III. mb 方法没有抛出任何异常,则子类也不能抛出任何异常。
  3. 仔细阅读以下代码,关于程序描述正确的是(A)。
    在这里插入图片描述
    A. 编译不通过
    B. 编译通过,输出-1
    C. 编译通过,输出 0
    D. 以上描述都不正确
    原因:n的赋值语句产生异常后,找到catch执行里面的代码,最后return n,此时因为对n的赋值语句异常所以赋值失败,n没有 值,局部变量必须先有值才能使用。
  4. 仔细阅读以下代码,在 ma 方法中,当读入的 b 为 100 时,输出结果为___100___,当读入的 b 为 0 时,输出结果为100。
    在这里插入图片描述
    原因:Finally中的代码一定会执行。
  5. 仔细阅读以下代码,在 ma 方法中,读入整数 b,如果读入的值为 10,则输出_ma1 ma2 1 In Finally ;如果读入的 值为 0,则输出 ma1 In Finally。
    在这里插入图片描述
  6. 仔细阅读以下代码,是否能编译通过?如果不能,应该如何修改?
    在这里插入图片描述
    答:不能编译通过。由于 MySub2 继承自 MySub 类,因此不能抛出比MySub 中的方法更多的异常。由于MySub 类中的 m 方法抛出 EOFException,而 MySub2 类的 m 方法抛出的 FileNotFoundException不是 EOFException 的子 类,因此这个方法 抛出了 MySub 类中的 m 方法更多的异常,因此编译不通过。
  7. 仔细阅读以下代码,关于程序描述正确的是(A)。
    在这里插入图片描述
    A. 编译出错
    B. 编译正常,输出 main1 ma1 In Catch
    C. 编译正常,运行时出错
    D. 以上描述都不正确
    原因:ma 方法中第二个输出语句,由于上一个语句是 throw 语句,因此第二个输出语句永远都执行不到,因此编译出错。
  8. 仔细阅读以下程序,下面哪些代码放在/1/处可以编译通过(AB)。
    在这里插入图片描述
    A. catch(NullPointerException npe){}
    B. catch(IOException ioe){}
    C. catch(SQLException sqle){}
    原因:
    I. 在/1/处,由于 ma 方法声明有可能抛出 IOException 异常,因此 B 编译通过。
    II. 由于 NullPointerException 异常是运行时异常,即使 ma 方法的throws 语句中没有声明抛出,调用时也有可 能产生该异常,因此 A编译通过;
    III. 由于 SQLException 没有声明抛出,并且是已检查异常,因此在/1/处不可能捕获到该类异常,因此 C 编译不 通过。
  9. 简述 final、finlize、finally 的区别。
    答:final:修饰变量,方法,类。
    修饰变量为常量,只能赋值一次 修饰方法,不能被覆盖 修饰类不能被继承 finally:是异常处理的一部分,finally中的代码一定会执行,多数用于数 数据库和io流中用来释放资源。
    finalize:是Ojbect类中的一个方法用于垃圾回收。
  10. 在 try 里有 return 语句,那 finally 里的语句还会执行么?为什么?
    答:会执行,放在finally中的代码一定会执行。
  11. 仔细阅读以下代码,写出程序执行的结果。
    在这里插入图片描述
    答:
    StepB StepE
    原因:s为null调用方法会产生空指针异常找到对应的catch,执行完毕后,执行finally中的代码。
  12. 编程:创建两个自定义异常类 MyException1 和 MyException2,要求如下:
    (1) MyException1 为已检查异常,MyException2 为运行时异常;
    (2) 这两个异常均具有两个构造函数:一个无参,另一个带字符串参数,参数表示产生异常的信息。
    演示的代码如下:
package com.txw.test;

public class TestMyException {
    public static void main(String[] args) throws MyException1{
        m1(2);
    }
    public static void m1(int n)throws MyException1{
        if(n==1)throw new MyException1("自定义已检查异常");			// 抛出自定义已检查异常
        if(n==2)throw new MyException2("自定义未检查异常");
    }
}
// 自定义已检查异常
class MyException1 extends Exception{
    public MyException1(){}
    public MyException1(String message){
        super(message);		// 使用父类构造方法
    }
}
// 自定义未检查异常
class MyException2 extends RuntimeException{
    public MyException2(){}
    public MyException2(String message){
        super(message);
    }
}
  1. 在上一题的基础上,把下面代码补充完整。
    在这里插入图片描述
    演示的代码如下:
package com.txw.test;

import java.util.Scanner;
class MyException1 extends Exception {
    public MyException1() {
        super();
    }

    public MyException1(String message) {
        super(message);
    }
}
class MyException2 extends RuntimeException {
    public MyException2() {
        super();
    }

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

public class TestMyException {
    public static void main(String args[]) {
        int n;
        // 读入n
        Scanner sc = new Scanner(System.in);
         n = sc.nextInt();
        try {
            m(n);
        } catch (MyException1 ex1) {
            // 输出ex1详细的方法调用栈信息 
            ex1.printStackTrace();
        } catch (MyException2 ex2) {
            // 输出ex2的详细信息 
            System.out.println(ex2.getMessage());
            // 并把ex2重新抛出 throw ex2; 
        }
    }

    public static void m(int n) throws MyException1 {
        // 声明抛出MyException1 
        if (n == 1) {
            // 抛出MyException1 
            // 并设定其详细信息为“n == 1” 
            throw new MyException1("n == 1");
        } else {
            // 抛出MyException2 
            // 并设定其详细信息为“n == 2”
            throw new MyException2("n == 2");
        }
    }
}

总结在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学无止路

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

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

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

打赏作者

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

抵扣说明:

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

余额充值