Java第八章总结

8.1 异常的概述

在程序中,错误可能产生于程序员没有预料到的各种情况,或者是超出了程序员可控范围的环境因素,如试图打开一个根本不存在的文件等,在Java中,这种在程序运行时可能出现的一些错误称为异常。

在 Java 中一个异常的产生,主要有如下三种原因:

  1. Java 内部错误发生异常,Java 虚拟机产生的异常。
  2. 编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。
  3. 通过 throw 语句手动生成的异常,一般用来告知该方法的调用者一些必要信息。

创建Baulk类,再主方法中定义int型变量,将0作为除数赋值给该变量。

public class Baulk {                            //创建类Baulk
    public static void main(String[] args) {   //主方法
           int result = 3 / 0;                 //定义int型变量并赋值
         System.out.println(result);           //将变量输出
     }
}

8.2 异常的分类

Java类库的每个包中都定义了异常类,所有这些类都是Throwable类。

Throwable类派生了Error和Exception连个子类。

Error类及其子类用来描述Java运行系统中的内部错误以及资源耗尽的错误,这类错误比较严重。

  Exception称为非致命类,可以通过捕捉处理使程序继续执行

Throwable 类是所有异常和错误的超类,下面有 Error 和 Exception 两个子类分别表示错误和异常 

Exception 类用于用户程序可能出现的异常情况,它也是用来创建自定义异常类型类的类。
Error 定义了在通常环境下不希望被程序捕获的异常。一般指的是 JVM 错误,如堆栈溢出。
Error(错误)和 Exception(异常)都是 java.lang.Throwable 类的子类,在 Java 代码中只有继承了 Throwable 类的实例才能被 throw 或者 catch。

Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类,Exception 是程序正常运行过程中可以预料到的意外情况,并且应该被开发者捕获,进行相应的处理。Error 是指正常情况下不大可能出现的情况,绝大部分的 Error 都会导致程序处于非正常、不可恢复状态。所以不需要被开发者捕获。
 

1 系统错误——Error

      Error类定义了常规环境下不希望由程序捕获的异常,比如:OutOfMemoryError、ThreadDeath等,这些错误发生时Java一般会选择终止。

public static void main(String[] args) { //主方法
                                  
                 System.out.println("梦想照亮现实!!!")         //此处缺少必要的分号
             }

2 异常---Exception
1)运行时异常(RuntimeException):

NullPropagation:空指针异常;
ClassCastException:类型强制转换异常
IllegalArgumentException:传递非法参数异常
IndexOutOfBoundsException:下标越界异常
NumberFormatException:数字格式异常

2)非运行时异常:

ClassNotFoundException:找不到指定 class 的异常
IOException:IO 操作异常

// 通过无限递归演示堆栈溢出错误
class StackOverflow {
    public static void test(int i) {
        if (i == 0) {
            return;
        } else {
            test(i++);
        }
    }
}
public class ErrorEg {
    public static void main(String[] args) {
        // 执行StackOverflow方法
        StackOverflow.test(5);
    }
}

 

8.3 捕捉处理异常

在处理非运行时异常时,提到了系统会自动为非运行时异常提供两种解决方案,一种是使用throws关键字,一种是使用try...catch代码块。

try...catch代码块代码块主要用来对异常进行捕捉并处理。在实际使用时,改代码块还有一个可选的finally代码块。

try {undefined

//程序代码块

}

catch(Exceptiontype e) {undefined

  //对Exceptiontype的处理

}

finally {undefined

   //代码块

}
 

1 try...catch代码块 

public class Baulk {       //创建类
 
	public static void main(String[] args) { //主方法
		try {
		String str = "lili";                 //定义字符串                     
	    System.out.println(str + "年龄是:");     //输出的提示信息
	 	int age = Integer.parseInt("20L");    //数据类型的转换
	    System.out.println(age);                 //输出信息
	} catch (Exception e) {                   //catch代码块用来获取异常信息
		e.printStackTrace();                 //输出异常性质
	}
		System.out.println("program over");   //输出信息
		     }
		}

 注意:Exception是try代码块传递给catch代码块的类型,e是对象名。

上面代码中,在catch代码块中使用了Exception对象的printStackTrace()方法输出了异常的栈日志,除此之外,Excrption对象还提供了其他的方法用于获取异常的相关信息,其最常用的3个方法如下:

(1)getMessage()方法:获取有关异常事件的信息。

(2)toString()方法:获取异常的类型与性质。

(3)printStackTrace()方法:获取异常事件发生时执行堆栈的内容。

在遇到需要处理多种异常信息的情况时,可以在一个try代码块后面跟多个catch代码块。

public class FootballTeam {
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			String str = "lili";                 //定义字符串                     
		    System.out.println(str + "年龄是:");     //输出的提示信息
		 	int age = Integer.parseInt("20L");    //数据类型的转换
		    System.out.println(age);                 //输出信息
 
		} catch (NumberFormatException nfx) {     //捕捉NumberFormatException异常
		    	nfx.printStackTrace();
 
		} catch (Exception e) {                   //Exception异常
			e.printStackTrace();                 //输出异常性质
		}
			System.out.println("program over");   //输出信息
	}
}

这时将两个catch代码块的位置互换,代码块如下:

public class FootballTeam {
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			String str = "lili";                 //定义字符串                     
		    System.out.println(str + "年龄是:");     //输出的提示信息
		 	int age = Integer.parseInt("20L");    //数据类型的转换
		    System.out.println(age);                 //输出信息
		
		} catch (Exception e) {                   //Exception异常
			e.printStackTrace();                 //输出异常性质
 
	    } catch (NumberFormatException nfx) {     //捕捉NumberFormatException异常
    	   nfx.printStackTrace();
	    }
			System.out.println("program over");   //输出信息
	}
}

这时编译器

会出现以下错误提示,该错误就是由于多个catch代码块时,父异常类放在了子异常类的前面所引起的。Exception是所有异常类的父类。

 2  finally代码块 

根据 try catch 语句的执行过程,try 语句块和 catch 语句块有可能不被完全执行,而有些处理代码则要求必须执行。例如,程序在 try 块里打开了一些物理资源(如数据库连接、网络连接和磁盘文件等),这些物理资源都必须显式回收。

try {
    // 可能会发生异常的语句
} catch(ExceptionType e) {
    // 处理异常语句
} finally {
    // 清理代码块
}
try {
    // 逻辑代码块
} finally {
    // 清理代码块
}

 try catch finally 语句块的执行情况可以细分为以下 3 种情况:

如果 try 代码块中没有拋出异常,则执行完 try 代码块之后直接执行 finally 代码块,然后执行 try catch finally 语句块之后的语句。
如果 try 代码块中拋出异常,并被 catch 子句捕捉,那么在拋出异常的地方终止 try 代码块的执行,转而执行相匹配的 catch 代码块,之后执行 finally 代码块。如果 finally 代码块中没有拋出异常,则继续执行 try catch finally 语句块之后的语句;如果 finally 代码块中拋出异常,则把该异常传递给该方法的调用者。
如果 try 代码块中拋出的异常没有被任何 catch 子句捕捉到,那么将直接执行 finally 代码块中的语句,并把该异常传递给该方法的调用者

import java.util.Scanner;
public class Test04 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("Windows 系统已启动!");
        String[] pros = { "记事本", "计算器", "浏览器" };
        try {
            // 循环输出pros数组中的元素
            for (int i = 0; i < pros.length; i++) {
                System.out.println(i + 1 + ":" + pros[i]);
            }
            System.out.println("是否运行程序:");
            String answer = input.next();
            if (answer.equals("y")) {
                System.out.println("请输入程序编号:");
                int no = input.nextInt();
                System.out.println("正在运行程序[" + pros[no - 1] + "]");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("谢谢使用!");
        }
    }
}

8.4在方法中抛出异常 


throws 关键字和 throw 关键字在使用上的几点区别如下:

throws 用来声明一个方法可能抛出的所有异常信息,表示出现异常的一种可能性,但并不一定会发生这些异常;throw 则是指拋出的一个具体的异常类型,执行 throw 则一定抛出了某种异常对象。
通常在一个方法(类)的声明处通过 throws 声明方法(类)可能拋出的异常信息,而在方法(类)内部通过 throw 声明一个具体的异常信息。
throws 通常不用显示地捕获异常,可由系统自动将所有捕获的异常信息抛给上级方法; throw 则需要用户自己捕获相关的异常,而后再对其进行相关包装,最后将包装后的异常信息抛出。

1 使用throws关键字抛出异常


当一个方法产生一个它不处理的异常时,那么就需要在该方法的头部声明这个异常,以便将该异常传递到方法的外部进行处理。使用 throws 声明的方法表示此方法不处理异常。throws 具体格式如下:

returnType method_name(paramList) throws Exception 1,Exception2,…{…}
 

import java.io.FileInputStream;
import java.io.IOException;
public class Test04 {
    public void readFile() throws IOException {
        // 定义方法时声明异常
        FileInputStream file = new FileInputStream("read.txt"); // 创建 FileInputStream 实例对象
        int f;
        while ((f = file.read()) != -1) {
            System.out.println((char) f);
            f = file.read();
        }
        file.close();
    }
    public static void main(String[] args) {
        Throws t = new Test04();
        try {
            t.readFile(); // 调用 readFHe()方法
        } catch (IOException e) {
            // 捕获异常
            System.out.println(e);
        }
    }
}
public class OverrideThrows {
    public void test() throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");
    }
}
class Sub extends OverrideThrows {
    // 子类方法声明抛出了比父类方法更大的异常
    // 所以下面方法出错
    public void test() throws Exception {
    }
}

使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由向上一级的调用者处理;如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。JVM 对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。

2  使用throw关键字抛出异常

throw关键字通常用于在方法体中“制作”一个异常,程序在执行到throw语句时立即终止,它后面的语句都不执行。

throw new 异常类型名(异常信息)

 通常用于在程序出现逻辑错误1时,由开发者主动抛出某个特定类型的异常。

public class ThrowTest {
 
	public static void main(String[] args) { //主方法
		// TODO Auto-generated method stub
		int num1 = 25;
		int num2 = 0;
		int result;
		if (num2 == 0)  //判断num2是否等于0,如果等于0,抛出异常
		{
			 //抛出ArithmeticException异常
			throw new ArithmeticException("这都不会,小学生都知道:除数不能是0!!!");
		}
            result = num1 / num2; //计算int1除以int2的值
            System.out.println("两个数的商为: " + result);
	}
 
}

 throws和throw的区别:

(1)throws用在方法声明后面,表示抛出异常,由方法的调用者处理。而throw用在方法体内,用来制造一个异常,由方法体内的语句处理。

(2)throws是声明这个方法会抛出这种类型的异常,以便使它的调用者知道要捕获这个异常,而throw是直接抛出一个异常实例

(3)Theo我是表示出现异常的一种可能性,并不一定会发生这些异常,如果使用throw,就一定会产生某种异常。
 

8.5 自定义异常

int age = -50;

System.out.println("王师傅今年  "+age+"  岁了!");

 在程序中使用自定义异常类,大体可分为以下几个步骤。

(1)创建自定义异常类。

(2)在方法中通过throw关键字抛出异常对象。

(3)如果在当前抛出异常的方法中处理异常,可以使用try...catch代码块捕获并处理,否则,在方法的声明处通过throws关键字指明要抛给方法调用者的异常,继续进行下一步操作。

(4)在出现异常方法的调用者中捕获并处理异常。

public class MyException extends Exception {   //创建自定义异常,继承Exception类
     public  MyException(String ErrorMessage) {  //构造方法
          super(ErrorMessage);                  //父类构造方法
   }
}
 

public class Tran { 
	//定义方法,抛出自定义的异常
	static void avg(int age) throws MyException {
		if (age < 0) {      //判断方法中参数是否满足指定条件
			throw new MyException("年龄不可以使用负数");  //错误信息		
		} else {
			System.out.println("王师傅今年 " + age + "岁了!"); //输出信息
		}
	}
 
	public static void main(String[] args) {  //主方法
		// TODO Auto-generated method stub
             try {                        //try代码块处理可能出现异常的代码
            	 
            	 avg(-50); 
             } catch (MyException e) {
            	 e.printStackTrace();
             }
	   }
 
}

8.6 异常的使用原则

编写代码处理某个方法可能出现的异常时,可遵循以下原则:

(1)不要过度使用异常。虽然通过异常可以增强程序的健壮性,但如果使用过多不必要的异常处理,可能会影响程序的执行效率。

(2)不要使用过于庞大的try...catch块。在一个try块中放置大量的代码,这种写法看上去“很简单”,但是由于try块中的代码过于庞大,业务过于复杂,会造成try块中出现异常的可能性大大增加,从而导致分析异常原因的难度也大大增加。

(3)避免使用catch(Exception e)。因为如果所有异常都采用相同的处理方式,将无法对不同异常分情况处理;另外,这种捕获方式可能将程序中的全部错误、异常捕获到,这时如果出现一些“关键”异常,可能会被“悄悄地”忽略掉。

(4)不要忽略捕捉到的异常,遇到异常一定要及时处理。

(5)如果父类抛出多个异常,则覆盖方法必须抛出相同的异常或其异常的子类,不能抛出新异常。

class SelfException extends RuntimeException {
    SelfException() {
    }
    SelfException(String msg) {
        super(msg);
    }
}
public class PrintStackTraceTest {
    public static void main(String[] args) {
        firstMethod();
    }
    public static void firstMethod() {
        secondMethod();
    }
    public static void secondMethod() {
        thirdMethod();
    }
    public static void thirdMethod() {
        throw new SelfException("自定义异常信息");
    }
}

多线程异常的跟踪栈,从发生异常的方法开始,到线程的 run 方法结束。从上面的运行结果可以看出,程序在 Thread 的 run 方法中出现了 ArithmeticException 异常,这个异常的源头是 ThreadExcetpionTest 的 secondMethod 方法,位于 ThreadExcetpionTest.java 文件的 14 行。这个异常传播到 Thread 类的 run 方法就会结束(如果该异常没有得到处理,将会导致该线程中止运行)。

前面已经讲过,调用 Exception 的 printStackTrace() 方法就是打印该异常的跟踪栈信息,也就会看到上面两个示例运行结果中的信息。当然,如果方法调用的层次很深,将会看到更加复杂的异常跟踪栈。

提示:虽然 printStackTrace() 方法可以很方便地用于追踪异常的发生情况,可以用它来调试程序,但在最后发布的程序中,应该避免使用它。应该对捕获的异常进行适当的处理,而不是简单地将异常的跟踪栈信息打印出来。 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值