异常(Exception)
- Exception
- 1.1 异常的概念
- 1.2 异常体系图(☆)
- 1.3 异常处理分类
- 1.3.1 运行时异常(☆)
- 1.3.2 编译时异常(☆)
- 1.4 异常处理(☆)
- 1.4.1 try-catch异常处理
- 1.4.2 throws异常处理
- 1.5 自定义异常
- 1.6 throw和throws的对比
- 1.7 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 异常处理分类
- 异常分为两大类:运行时异常和编译时异常。
- 运行时异常:编译器不要求强制处置的异常。 一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。【java.lang.RuntimeException类及它的子类都是运行时异常】
- 对于运行时异常,可以不做处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
- 编译时异常:是编译器要求必须处置的异常。
1.3.1 运行时异常(☆)
● 常见的运行时异常
- NullPointerException (空指针异常)
- ArithmeticException (数学运算异常)
- ArrayIndexOutOfBoundsException (数组下标越界异常)
- ClassCastException (类型转换异常)
- NumberFormatException (数字格式不正确异常)
1.3.2 编译时异常(☆)
编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。
● 常见的编译异常
- SQLException (操作数据库时,查询表可能发生异常)
- IOException (操作文件时,发生的异常)
- FileNotFoundException (当操作一个不存在的文件时,发生异常)
- ClassNotFoundException (加载类,而该类不存在时,异常)
- EOFException (操作文件,到文件末尾,发生异常)
- IllegalArguementException (参数异常)
1.4 异常处理(☆)
异常处理就是当异常发生的时候,对异常处理的方式。
● 异常处理机制
1) try - catch - finally
程序员在代码中捕获发生的异常,自行处理。
2) throws
将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者是JVM。
注意:
- try - catch - finally 和 throws 必须二选一
- 如果程序没有显示处理异常,则默认是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 注意事项
- 如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块。(try块中发生异常后,try块剩下的语句不再执行)
- 如果异常没有发生,则顺序执行try的代码块,不会进入到catch。
- 如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等),则使用
- finally {}
- 可以有多个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");
}
- 可以进行 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注意事项及其使用细节
- 对于编译异常,程序中必须处理,比如 try-catch 或者 throws。
- 对于运行时异常,程序中如果没有处理,默认就是throws的方式处理。
- 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型。
- 在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子类中描述处理,这个时候就可以自己设计异常类,用于描述该错误信息。
● 自定义异常的步骤
- 定义类:自定义异常类名(程序员自定)继承Exception 或 RuntimeException。
- 如果继承Exception,则属于编译异常。
- 如果继承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());
}
}
分析
- 创建数组后并没有给数组添加值,所以数组所存的三个数据都为Null。
- 进入到if语句里,names[1]的值为Null,所以会有NullPointerException异常的错误信息报出。
- 当获取到NullPointerException时会进入return3的catch中。
- 但是注意:try - catch 的结构最后有一个finally,所以即使你捕获到了NullPointerException也必须要执行finally,所以最后的结果是return 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());
}
}
分析
- int i = 1;
- 进入到try,i++(i=2),创建了数组后,并没有给数组赋值,所以数组依旧是三个Null。
- 进入到if,由于数组里是null,所以程序会报空指针异(NullPointerException)。
- 进入到catch里,传入NullPointerException e,由于有finally,所以return 不执行,但是 i 是先自增才输出,所以i就先变为了3,才继续下一步。
- 执行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());
}
}
分析
- i=1,程序进入try中,i++(i=2),由于创建数组时并没有给出数组的值,所以数组里3个位置都为Null。
- 进入if,由于三个位置的值都是Null,所以程序出现NullPointerException异常,进入到catch中。
- 进入到NullPointerException e 的catch中,++i(i=3),这里的 i 由一个临时变量temp存储起来,再进入到finally。
- 进入到finally中,++i(i=4),打印出:“i=4”,由于finally中只有输出语句,程序并没有结束,所以还会回到上一级的catch里。
- 回到NullPointerException的catch中,执行return ++i;(return temp)输出结果3。
- 所以最终得结果为:i=4,3。
答案
● 本章练习
题一
- 编写应用程序EcmDef.java,接收命令行的两个参数(整数),计算两数相除。
- 计算两个数相除,要求使用方法cal(int n1,int n2)
- 对数据格式不正确(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();
}
答案