Java基础43 异常(Exception)


Exception

1.1 异常的概念

● 什么是异常?

异常就是程序中的一些错误。

● 异常发生的原因是什么?

异常发生的原因有很多,在Java语言中,将程序执行中发生的不正常情况称为 " 异常 " 。(开发过程中的语法错误和逻辑错误不是异常

● 程序执行过程中所发生的异常事件可分为两类

1)Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。

比如:StackOverflowError [ 栈溢出 ] 和 OOM(out of memory),Error是严重错误,程序会崩溃。

2)Exception(异常):其他因编程错误或偶然的外在因素导致的一般性问题,是可以使用针对性的代码进行处理。

例如空指针访问,视图读取不存在的文件,网络连接中断等等。

● 异常与错误的直白理解

你写了一段代码,但是你写的代码中有语法错误,少了一些标点符号,那么运行出来的结果就会是:

java.lang.Error;

这就是错误,而不是异常。

而如果你写的那段代码没有任何语法错误,但是你写的语句是用一个数除以一个0,而在java中会因为你用0做了除数,而出现异常,并给你红色的警告:

java.lang.ArithmeticException

这就是异常


1.2 异常体系图(☆)

● 异常体系图一览

图一:

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

在这里插入图片描述

(图片来源于韩老师的视频)


1.3 异常处理分类

  1. 异常分为两大类:运行时异常和编译时异常。
  2. 运行时异常:编译器不要求强制处置的异常。 一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。【java.lang.RuntimeException类及它的子类都是运行时异常】
  3. 对于运行时异常,可以不做处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
  4. 编译时异常:是编译器要求必须处置的异常。

1.3.1 运行时异常(☆)

● 常见的运行时异常

  1. NullPointerException (空指针异常)
  2. ArithmeticException (数学运算异常)
  3. ArrayIndexOutOfBoundsException (数组下标越界异常)
  4. ClassCastException (类型转换异常)
  5. NumberFormatException (数字格式不正确异常)

1.3.2 编译时异常(☆)

编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。

● 常见的编译异常

  1. SQLException (操作数据库时,查询表可能发生异常)
  2. IOException (操作文件时,发生的异常)
  3. FileNotFoundException (当操作一个不存在的文件时,发生异常)
  4. ClassNotFoundException (加载类,而该类不存在时,异常)
  5. EOFException (操作文件,到文件末尾,发生异常)
  6. IllegalArguementException (参数异常)

1.4 异常处理(☆)

异常处理就是当异常发生的时候,对异常处理的方式。

● 异常处理机制

1) try - catch - finally

程序员在代码中捕获发生的异常,自行处理。

2) throws

将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者是JVM。

注意:

  1. try - catch - finally 和 throws 必须二选一
  2. 如果程序没有显示处理异常,则默认是throws

方式一 (try - catch - finally)

try{
	代码/可能的异常
}catch(Exception e){
	捕获到异常,当异常发生时,
	系统将异常封装成Exception 对象 e,
	传递给catch,得到异常对象后,程序员自行处理

	注意:如果没有发生异常,catch代码块则不执行
}finally{
	不管try代码块是否有异常发生,始终要执行finally
}

方式二 (throws)

throws是会把异常返回给上一级,直到最后的JVM,JVM会输出异常信息,然后退出程序。

在这里插入图片描述

1.4.1 try-catch异常处理

● try - catch方式处理异常介绍

Java提供try和catch块来处理异常。try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。可以根据需要在程序中有多个数量的try…catch块。

try - catch 语法

try {
	可疑代码
	将异常生成对应的异常对象,传递给catch}catch(异常){
	对异常的处理
}

● try-catch 注意事项

  1. 如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块。(try块中发生异常后,try块剩下的语句不再执行)
  2. 如果异常没有发生,则顺序执行try的代码块,不会进入到catch。
  3. 如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等),则使用 - finally {}
  4. 可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception在后,NullPointerException在前),如果发生异常,只会匹配一个catch。
try{
	Person person = new Person();
	person.say();
	int i = 10/0;
} catch (NullPointerException e){
	System.out.println("here1");
	e.printStackTrace();
} catch (Exception e){
	System.out.println("here2");
	e.printStackTrace();
} finally {
	System.out.printl("here3");
}
  1. 可以进行 try - finally 配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉,应用场景:就是执行一段代码,不管是否发生异常,都必须执行某个业务逻辑。
try{
	//代码
} finally {
	//总是执行
}

1.4.2 throws异常处理

● 基本介绍

1) 如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理

2)在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。

● 使用演示

public class Throws01{
	public static void main(String[] args){
		
	}
	public void f2() throwsFileNoteFoundException	{
		//此处的FileNoteFoundException可以换成它的父类Exception
		//创建了一个文件流对象
		//1.这里的异常是一个FileNoteFoundException 编译异常
		//2.使用前面讲过的 try - catch - finally
		//3.使用throws,抛出异常,让调用f2方法的调用者(方法)处理
		//4.throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
		//5.throws关键字也可以是异常列表,即可以抛出多个异常
		FileInputStream fis = new FileInputStream("d://aa.txt");
	}
}

● throws注意事项及其使用细节

  1. 对于编译异常,程序中必须处理,比如 try-catch 或者 throws。
  2. 对于运行时异常,程序中如果没有处理,默认就是throws的方式处理。
  3. 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型。
  4. 在throws过程中,如果有方法 try - catch,就相当于处理异常,就可以不必throws。

注意!!!

● 第一点

下面的代码,在f1()中调用 f3(),如果我解开f3的注释,就会出现红色的警告,原因是:

f3()抛出的是一个编译异常,即这时,就要f1()必须处理这个编译异常。

public static void f1(){
	//f3();
}

public static void f3() throws FileNotFoundException{
	FileInputStream fis = new FileInputStream("d://aa.txt");
}

处理方法

在f1()中,要么 try - catch - finally,或者继续throws这个编译异常

选用throws方法处理

public static void f1() throws FileNotFoundException{
	f3();
}

public static void f3() throws FileNotFoundException{
	FileInputStream fis = new FileInputStream("d://aa.txt");
}

● 第二点

下面的代码中在f4()中调用f5()并没有报错,原因是:

f5()抛出的是运行异常,而Java中,并不要求程序员显示处理运行异常,因为有默认处理机制。

public static void f4(){
	f5();
}

public static void f5() throws AritmeticeException{
}

1.5 自定义异常

● 什么是自定义异常?

当程序中出现了某些 “错误” ,但该错误信息并没有在Throwable子类中描述处理,这个时候就可以自己设计异常类,用于描述该错误信息。

● 自定义异常的步骤

  1. 定义类:自定义异常类名(程序员自定)继承Exception 或 RuntimeException。
  2. 如果继承Exception,则属于编译异常
  3. 如果继承RuntimeException,则属于运行异常(一般来说都是继承RuntimeException)

● 自定义异常的应用实例

实例1

当我们接收Person对象年龄时,要求范围在18 - 120 之间,否则就抛出一个自定义异常(要求
继承RuntimeException),并给出提示信息。

代码实现

public class Test02 {
    public static void main(String[] args) {
        int age = 23;
        //要求范围在18 - 120 之间,否则就抛出一个自定义异常
        if(!(age >=18 && age <= 120)){
            //通过构造器设置年龄
            throw new ageException("年龄要求在18—120之间。");
        }
        System.out.println("输入的年龄范围正确。");
    }

    //自定义异常类
    static class ageException  extends RuntimeException{
        public ageException(String message) { //构造器
            super(message);
        }
    }
}

注意:

一般情况下,我们自定义异常是继承RuntimeException,即把自定义异常做成运行时异常,好处是:我们可以使用默认的处理机制,比较方便。


1.6 throw和throws的对比

意义位置后面跟的内容
throws异常处理的一种方式方法声明处异常类型
throw手动生成异常对象的关键字方法体中异常对象

throw使用实例

看下面的测试代码中输出什么内容?

ReturnExceptionDemo类

class ReturnExceptionDemo{
	static void methodA(){
		try{
			System.out.println("进入方法A");	
			throw new RuntimeException("制造异常");
		}finally{
			System.out.println("用A方法的finally");
		}
	}
	static void methodB(){
		try{
			System.out.println("进入方法B");
			return;	
		}finally{
			System.out.println("调用B方法的finally");	
		}
	}
}

main方法

public static void main(String[] args){
	try{
		ReturnExceptionDemo.methodA();
	}catch(Exception e){
		System.out.println(e.getMessage());
	}
		ReturnExceptionDemo.methodB();
}

答案

在这里插入图片描述

1.7 Exception练习

看看下面代码是否正确,为什么?

题一:

String friends[] = {"tom","jack","milan"};
for(int i = 0; i < 4; i++){
	System.out.println(friends[i]);
}

题二:

Cat c = new Cat();
cat = null;
System.out.println(cat.name);

题三:

public class AAA{
	int x;
	public static void main(){
		int y;
		AAA a = new AAA();
		y = 3 / a.x;
		System.out.println("program ends ok!");
	}
}

题四:

class Person{
	public static void main(String[] args){
		Object obj = new Date();
		Person person;
		person = (Person)obj;
		System.out.println(person);
	}
}

答案

题一:

ArrayIndexOutOfBoundsException (数组越界异常)

题二:

NullPointerException(空指针异常)

题三:

ArithmeticException (数学运算异常)

题四:

ClassCastException (类型转换异常)


● try - catch 异常处理练习

题一

以下代码输出什么?

public class Exception01{
	public static int method(){
		try{
			String[] names = new String[3];
			if(names[1].equals("tom")){
				System.out.println(names[1]);
			}else{
				names[3] = "coco";	
			}
			return 1;
		} catch (ArrayIndexOutOfBoundsException e){
			return 2;
		} catch (NullPointerException e){
			return 3;
		} finally {
			return 4;	
		}
	}
	public static void main(String[] args){
		System.out.println(method());
	}
}

分析

  1. 创建数组后并没有给数组添加值,所以数组所存的三个数据都为Null。
  2. 进入到if语句里,names[1]的值为Null,所以会有NullPointerException异常的错误信息报出。
  3. 当获取到NullPointerException时会进入return3的catch中。
  4. 但是注意:try - catch 的结构最后有一个finally,所以即使你捕获到了NullPointerException也必须要执行finally,所以最后的结果是return 4,输出的结果为:4。

结果

4


题二

public class Exception02{
	public static int method(){
		int i =1;
		try{
			i++;
			String[] names = new String[3];
			if(names[1].equals("tom")){
				System.out.println(names[1]);
			}else{
				names[3] = "coco";	
			}
			return 1;
		} catch (ArrayIndexOutOfBoundsException e){
			return 2;
		} catch (NullPointerException e){
			return ++i;
		} finally {
			return ++i;	
		}
	}
	public static void main(String[] args){
		System.out.println(method());
	}
}

分析

  1. int i = 1;
  2. 进入到try,i++(i=2),创建了数组后,并没有给数组赋值,所以数组依旧是三个Null。
  3. 进入到if,由于数组里是null,所以程序会报空指针异(NullPointerException)。
  4. 进入到catch里,传入NullPointerException e,由于有finally,所以return 不执行,但是 i 是先自增才输出,所以i就先变为了3,才继续下一步。
  5. 执行finally里的代码,return ++i;最后输出的结果为4。

答案

在这里插入图片描述


题三

public class Exception02{
	public static int method(){
		int i =1;
		try{
			i++;
			String[] names = new String[3];
			if(names[1].equals("tom")){
				System.out.println(names[1]);
			}else{
				names[3] = "coco";	
			}
			return 1;
		} catch (ArrayIndexOutOfBoundsException e){
			return 2;
		} catch (NullPointerException e){
			return ++i;
		} finally {
			++i;
			System.out.println("i="+ i);	
		}
	}
	public static void main(String[] args){
		System.out.println(method());
	}
}

分析

  1. i=1,程序进入try中,i++(i=2),由于创建数组时并没有给出数组的值,所以数组里3个位置都为Null。
  2. 进入if,由于三个位置的值都是Null,所以程序出现NullPointerException异常,进入到catch中。
  3. 进入到NullPointerException e 的catch中,++i(i=3),这里的 i 由一个临时变量temp存储起来,再进入到finally。
  4. 进入到finally中,++i(i=4),打印出:“i=4”,由于finally中只有输出语句,程序并没有结束,所以还会回到上一级的catch里。
  5. 回到NullPointerException的catch中,执行return ++i;(return temp)输出结果3。
  6. 所以最终得结果为:i=4,3。

答案

**答案**


● 本章练习

题一

  1. 编写应用程序EcmDef.java,接收命令行的两个参数(整数),计算两数相除。
  2. 计算两个数相除,要求使用方法cal(int n1,int n2)
  3. 对数据格式不正确(NumberformatException)、缺少命令行参数(ArrayIndexOutOfBoundsException)、除0进行异常处理(ArithmeticException)。

代码实现

public class Homework01{
	public static void main(String[] args){
		try{
			//验证输入的参数的个数是否正确(两个参数)
			if(args.length != 2){
				throw new ArrayIndexOutOfBoundsException("参数个数不对");	
			}
			//接收整数
			int n1 = Integer.parseInt(args[0]);
			int n2 = Integer.parseInt(args[1]);
			
			//该方法可能抛出ArithmeticException
			double res = cal(n1,n2); 
			System.out.println("计算结果是=" + res);
			
		} catch(ArrayIndexOutOfBoundsException e){
			System.out.println(e.getMessage());	
		} catch(NumberFormatException e){
			System.out.println("参数格式不正确,需输出整数");
		} catch(ArithmeticException e){
			System.out.println("出现了除0的异常");
		}
	}
	
	//cal方法,求两数商
	public static double cal(int n1,int n2){
		return n1/n2;
	}
}

题二

看看以下代码是否会发生异常,如果会,是哪种异常?如果不会,则打印结果是什么?

public static void main(String[] args){
	if(args[4].equals("jock")){ 
		System.out.println("AA");
	}else{
		System.out.println("BB");
	}
	Object o = args[2]; 
	Integer i = (Integer)o;
}

分析与答案

这里的args.length=0,因为没有给args一个值或者长度,所以会发生ArrayIndexOutOfBoundsException(数组越界异常)

if(args[4].equals("jock"))

String -> Object,发生了向上转型,所以不会异常

Object o = args[2]; 

这里会发生异常(ClassCastException),转型错误。

Integer i = (Integer)o;

题三

写出程序结果

public static void func(){
	try{
		throw new RuntimeException();
	}
	finally{
		System.out.println("B");
	}
}
public static void main(String[] args){
	try{
		func();
		System.out.println("A");
	} catch (Exception e){
		System.out.println("C");
	}
	System.out.println("D");
 }
}

答案

在这里插入图片描述


题四

写出程序结果

public static void main(){
	try{
		showExce();
		System.out.println("A");
	} catch(Exception e){
		System.out.println("B");
	} finally{
		System.out.println("C");
	}
	System.out.println("D");
}
public static void showExce() throws Exception{
	throw new Exception();
}

答案
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值