目录
参考书籍:《Java核心技术(卷I)基础知识(原书第9版)》、《Java编程思想(第4版)》
学习视频:尚硅谷Java入门视频教程(B站,主讲:宋红康老师)
一、异常概述
(从C到Java的理解)在C中,调用一些函数时,如:open、read…,通常会返回某个特殊值或者标志,我们需要对该返回值进行检查,以判断执行过程是否发生错误。多次的if判断语句会使代码可读性变差,难以维护。对于构造大型、健壮、可维护的程序而言,这种错误处理已经成为主要障碍。
在《Java编程思想(第4版)》中所述,Java的基本理念是:结构不佳的代码不能运行,它的异常处理机制建立在C++的基础之上(虽然看上去更像Object Pascal)。异常处理是Java中唯一正式的错误报告机制,异常处理的目的在于通过使用少于目前数量的代码来简化大型程序。也就是说,按照写C的习惯,可以理解为,Java异常处理就是C的函数执行错误判断。
在Java中,将程序执行中发生的不正确情况称为"异常"(注意:程序语法错误和逻辑错误不是异常)Java在执行过程的异常可分为两类:①Error、②Exception。 下图是Java异常类的体系
1.Error
Java虚拟机无法解决的严重问题。 如: JVM系统内部错误、 资源耗尽等严重情况。 比如:栈溢出(java.lang.StackOverflowError)、堆溢出(OOM-OutOfMemoryError)。一般不编写代码进行处理。
public class ErrorTest {
public static void main(String[] args) {
/* 栈溢出 java.lang.StackOverflowError */
main(args);
/* 堆溢出 java.lang.OutOfMemoryError */
int[] arr = new int[1024*1024*1024];
}
}
2.Exception
其它因编程错误或偶然的外在因素导致的一般性问题, 可以使用针对性的代码进行处理。 例如:空指针访问、读取文件不存在、网络连接中断、数组角标越界等。 分类:①编译时异常(受检异常 - checked)、运行时异常(非受检异常 - unchecked 也就是 RuntimeException)
✒️编译时异常(checked)
例如:IOException、FileNotFoundException、ClassNotFoundException
package com.exception.java;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileInputStream;
public class ErrorTest {
/* 编译时异常 */
@Test
public void test1(){
File file = new File("test.txt");//文件不存在编译时出现异常
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while (data != -1){
System.out.println(data);
data = fis.read();
}
fis.close();
}
}
✒️运行时异常(unchecked)
例如:NullPointerExcepition、ArrayIndexOutOfBoundsException、ClassCastException、 NumberFormatException、InputMismatchException、ArithmeticException
package com.exception.java;
import org.junit.jupiter.api.Test;
import java.util.Date;
import java.util.Scanner;
public class ExceptionTest {
/***********运行时异常*************/
/* NullPointerException(空指针异常) */
@Test
public void test1(){
int []arr = null;
System.out.println(arr[1]);
}
/* ArrayIndexOutOfBoundsExcepiton */
/* StringIndexOutOfBoundsException */
@Test
public void test2(){
int []arr = new int[10];
System.out.println(arr[10]);
String str = "abcd";
System.out.println(str.charAt(4));
}
/* ClassCastException(类型转换异常) */
@Test
public void test3(){
Object obj = new Date();
String str = (String)obj;
}
/* NumberFormatException */
@Test
public void test4(){
String str = "abc";
int num = Integer.parseInt(str);
System.out.println(num);
}
/* InputMismatchException */
@Test
public void test5(){
Scanner scaner = new Scanner(System.in);
int data = scaner.nextInt();
System.out.println(data);
scaner.close();
}
}
二、异常处理(Exception Handing)
1.异常的处理方式一:try-catch-finally
package com.exception.java;
import org.junit.jupiter.api.Test;
public class ExceptionHanding {
@Test
/* NumberFormatException */
public void test1(){
try {
String str = "abc";
int num = Integer.parseInt(str);
System.out.println("----Test Line 1-----"); //一旦异常,后面代码不执行
}catch (NumberFormatException e){ //先捕获NumberFormatException
System.out.println(e.getMessage());
}catch (IllegalArgumentException e){ //再捕获IllegalAugumantException
System.out.println(e.getMessage()); //顺序不可调换
}finally {
System.out.println("-----Test Line 2----");//一定会执行的代码放finally里
}
System.out.println("-----Test Line 3------");//处理完异常继续执行后续代码
}
}
📓如果在try语句块中任何代码抛出了一个再catch子句中声明的异常类,那么:
(1)程序将跳过try语句块的其余代码
(2)程序将执行对应异常类的catch子句的代码
如果try语句块中的任何代码没有抛出异常,那么程序将跳过catch子句。
如果方法中的任何代码抛出了一个在catch子句中没有声明的异常类型,那么这个方法会立即退出
(希望调用者为这种类型的异常设计catch子句)
—— 来自于《Java核心技术(卷I)基础知识(原书第9版)》
📓catch多个异常时,异常类型没有父子类关系,无catch顺序;否则需要先catch子类异常,再catch父类异常,不然编译会报错。
📓catch中两个常用的方法:①String getMessage( ) 、②void printStackTrace( )
📓不管是否有异常被捕获,finally语句中的代码一定会被执行。数据库连接、I/O流、Socket等资源JVM无法自动回收,必须手动进行资源释放,这部分代码通常放置于finally中。
📓另外,还需要注意以下两点
(1)try-catch-finall处理编译时异常,使程序编译不报错,但是运行时仍可能出错。相当于使用try-catch-finally将一个编译时异常延迟到运行时出现。
(2)实际开发中,由于运行时异常比较常见,通常不针对运行时异常使用try-catch-finall处理。而编译时异常,则一定要考虑异常的处理。
package com.exception.java;
import org.junit.jupiter.api.Test;
public class ExceptionHanding {
@Test
/* finally中的代码一定会被执行 */
/* 无论catch完异常return或者又出现异常,还是没有异常return的情况,finally都会执行 */
public void test2(){
int retn = test2();
System.out.println(retn);
}
public int test1(){
try {
int a = 10;
int b = 0; //ArithmeticException算数异常
System.out.println(a/b);
return 1;
}catch(ArithmeticException e){
System.out.println(e.getMessage());
int[] arr = new int[10]; //ArrayIndexOutOfBoundsException
System.out.println(arr[15]);
return 2;
}finally {
System.out.println("finally");
return 3;
}
}
}
PS:IDEA处理异常快捷键Ctrl + Alt + T
2.异常的处理方式二:throws+异常类型
📓throws将异常抛出给方法的调用者,并没有真正处理异常("谁调谁处理")
📓子类重写的方法抛出的异常不大于(小于)父类被重写方法抛出的异常类型
package com.exception.java;
import org.junit.jupiter.api.Test;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ThrowsTest {
@Test
/* 子类重写父类方法时,抛出异常需小于父类方法 */
public static void main(String[] args) {
ThrowsTest test = new ThrowsTest();
test.method2(new subClass());
}
public static void method2(superClass s){
try {
s.method1();
} catch (IOException e) {
e.printStackTrace();
}
}
/* 子类重写父类的method1(),抛出异常必须小于父类抛出的,小到可以没有异常 */
/* 如果子类抛出异常类型大于父类,在method2中无法catch */
/* 如果父类方法没有抛出异常,子类重写该方法是不允许抛出异常 */
static class subClass extends superClass{
public void method1() throws FileNotFoundException{
System.out.println("subClass");
}
}
static class superClass{
public void method1()throws IOException{
System.out.println("superClass");
}
}
}
三、手动抛出异常throw
package com.exception.java;
import org.junit.jupiter.api.Test;
import java.util.Scanner;
public class ThrowTest {
public static void main(String[] args) {
try{
Student stu = new Student();
Scanner scan = new Scanner(System.in);
stu.register(scan.nextInt());
System.out.println(stu.id);
}catch(Exception e) {
System.out.println(e.getMessage());
}
}
/* 类(结构体) */
static class Student{
private int id; //成员变量(结构体成员)
/* 方法(函数指针)*/
public void register(int id)throws Exception{
if(id > 0){
this.id = id;
}else{
/* 手动抛出throw编译时异常Exception,处理方式是throws*/
throw new Exception("Illegal ID");
}
}
}
}
四、自定义异常类
1.如何自定义一个异常类
(1)定义一个继承于现有的异常结构(RuntimeException、Exception)的类
(2)提供全局常量serialVersionUID
(3)提供重载构造器
2.自定义并使用异常类demo1
package com.exception.java;
import java.util.Scanner;
public class ThrowTest {
public static void main(String[] args) {
try{
Student stu = new Student();
Scanner scan = new Scanner(System.in);
stu.register(scan.nextInt());
System.out.println(stu.id);
}catch(MyException e) {
System.out.println(e.getMessage());
}
}
static class Student{
private int id;
public void register(int id)throws MyException{
if(id > 0){
this.id = id;
}else{
/* 手动抛出throw编译时异常Exception,处理方式是throws*/
throw new MyException("Illegal ID");
}
}
}
/* 自定义异常类MyException继承于RuntimeException */
public static class MyException extends RuntimeException{
static final long serialVersionUID = -7034897193246939L;
public MyException(){
}
public MyException(String msg){
super(msg);
}
}
}
五、练习
1.throw和throws的区别?
✒️答:throw语句用来明确地抛出一个"异常"。
throws用来标明一个成员函数可能抛出的各种"异常"。
Finally为确保一段代码不管发生什么"异常"都被执行一段代码。
一般我们在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try语句,"异常"的框架就放到堆栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种"异常"进行处理,堆栈就会展开,直到遇到有处理这种"异常"的try语句。
2.总结编程练习
编写应用程序Summary.java, 接收命令行的两个参数, 要求不能输入负数, 计算两数相除。对数据类型不一致(NumberFormatException)、 缺少命令行参数(ArrayIndexOutOfBoundsException)、被除数为零(ArithmeticException)及输入负数(DevideBynegativeNumber 自定义的异常)进行异常处理。提示:
(1)在主类(Summary)中定义异常方法(Divede)完成两数相除功能。
(2)在main()方法中使用异常处理语句进行异常处理。
(3)在程序中,自定义对应输入负数的异常类(DevideBynegativeNumber)。
(4)运行时接受参数 java Summary 20 10 //args[0]=“20” args[1]=“10”
(5)Interger类的static方法parseInt(String s)将s转换成对应的int值。
如: int a=Interger.parseInt(“314”); //a=314;
public class Summary {
public static void main(String[] args) {
try{
int data1 = Integer.parseInt(args[0]);
int data2 = Integer.parseInt(args[1]);
int result = Divede(data1,data2);
System.out.println(result);
}catch(DivedeByNegativeNumException e){
System.out.println(e.getMessage());
}catch(NumberFormatException e){
e.printStackTrace();
}catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}catch(ArithmeticException e){
e.printStackTrace();
}
}
public static int Divede(int data1,int data2) throws DivedeByNegativeNumException{
if(data1 < 0 || data2 < 0){
throw new DivedeByNegativeNumException("Divede by negative number");
}
return data1 / data2;
}
/* 自定义异常类DivedeByNegativeNumException */
public static class DivedeByNegativeNumException extends Exception {
static final long serialVersionUID = -70397193246939L;
public DivedeByNegativeNumException(){}
public DivedeByNegativeNumException(String msg){
super(msg);
}
}
}
说明:由于笔者水平有限,文中难以避免有所错漏,敬请各读者斧正
版权声明:转载请附上原文出处链接及本声明。