当前位置: 代码迷 >> 综合 >> 【Java专题】详解Java中的异常
  详细解决方案

【Java专题】详解Java中的异常

热度:0   发布时间:2024-02-13 02:53:36.0
什么是异常?
异常:就是程序在运行过程中出现的不正常情况,这种不正常情况就叫做异常。
下面举例说明: (运行下面的代码,就会输出相关异常)
/**
* @author Jason
* @create 2020-07-05 15:16
*/
public class ExceptionTest01 {public static void main(String[] args) {int num1=10;int num2=0;int num3=num1/num2;System.out.println(num1+"/"+num2+"="+num3);}
}

控制台输出:

Exception in thread "main" java.lang.ArithmeticException: / by zeroat ExceptionTest02.main(ExceptionTest02.java:9)

 

 
异常的作用是什么?
提高程序的健壮性,通过输出的异常信息,让程序员判断并修改代码。
 
异常是以什么形式存在的呢?
异常是以类的形式存在的,也就是说,当我们的程序遇到异常的时候,就是去实例化那个异常类,创建一个异常对象。
举例说明:
/**
* @author Jason
* @create 2020-07-05 15:04
*/
public class ExceptionTest02 {public static void main(String[] args) {NumberFormatException nfe = new NumberFormatException("数字格式化异常");System.out.println(nfe);NullPointerException npe = new NullPointerException("空指针议程发生了什么?");System.out.println(npe);}
}

 

 
输出结果:
java.lang.NumberFormatException: 数字格式化异常
java.lang.NullPointerException: 空指针议程发生了什么?  
 
那么异常类的底层图是什么样子的呢?
 
转存失败重新上传取消
 
Java的异常处理机制:
Exception的直接子类:编译时异常(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常。) RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管,都行。)
 
编译时异常和运行时异常的区别?
编译时异常:编译时异常不是在编译阶段发生的异常,而是表示程序在编写的时候预先对这种异常进行处理,如果不处理编译器就会报错,编译时异常发生的概率较高,编译时异常又称为受检异常,或受控异常。
运行时异常:在编写程序阶段可以选择处理,也可以选择处理,也可以选择不处理,运行时异常发生概率较低,运行时还有另外一个名字:未受检异常或非受控异常
注意: 所有异常都是发生在运行阶段
 
java异常的处理方式:
第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。 谁调用我,我就抛给谁。抛给上一级。
第二种方式:使用try..catch语句进行异常的捕捉。这件事发生了,谁也不知道,因为我给抓住了。异常到此为止,不在上抛
 
思考:
 异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。
一、继续上抛:throws
二、自己处理:try...catch
 
注意: Java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果。终止java程序的执行。
 
编译时异常实例:
/**
* @author Jason
* @create 2020-07-05 22:00
* 编译时异常
*/
public class ExceptionTest03 {//第一种方法:选择继续上抛异常public static void main(String[] args) throws ClassNotFoundException {//doSome()上有编译时异常,我们在调用这个方法的时候必须对这个异常预先进行处理,否则报错//doSome() //千万不可用不处理异常doSome();}//第二种方法:自己把异常拦截下来/*public static void main(String[] args) {//doSome()上有编译时异常,我们在调用这个方法的时候必须对这个异常预先进行处理,否则报错//doSome() //千万不可用不处理异常try {doSome();} catch (ClassNotFoundException e) {e.printStackTrace();}}*/public static void doSome()throws ClassNotFoundException{System.out.println("doSome!!");}
}

 

 
 
1、throws上抛给上一级处理实例:
/**
* @author Jason
* @create 2020-07-06 8:45
* 编译时异常处理:上抛形式处理
*/
public class ExceptionTest04 {public static void main(String[] args) {System.out.println("main begin");//这里就不建议继续上抛给main方法了,因为main方法会继续上抛给jvm,这里就只会对异常选择终止程序try {n1();} catch (FileNotFoundException e) {e.printStackTrace();}System.out.println("main over");}public static void n1() throws FileNotFoundException {System.out.println("n1 begin");//继续上抛n2();System.out.println("n1 over");}public static void n2() throws FileNotFoundException {System.out.println("n2 begin");//此时的异常并没有消失,这里也需要我们对异常进一步处理,这里的处理方式很多://继续上抛:这里可以选择IOException或Exception,当然这里也可以选择多个异常处理n3();System.out.println("n2 over");}public static void n3() throws FileNotFoundException {System.out.println("n3 begin");//分析保存原因:这里调用了一个构造方法,这个构造方法的底层如下:这个构造方法的声明位置上有一个编译时异常FileNotFoundException/*public FileInputStream(String name) throws FileNotFoundException {this(name != null ? new File(name) : null);}*///此时我们有两种处理方法:自己处理异常或者选择上抛给调用者处理(此处我们选择上抛给调用者)new FileInputStream("文件路径...");System.out.println("n3 over");}
}

 

控制台输出:
main begin
n1 begin
n2 begin
n3 begin
main over
java.io.FileNotFoundException: 文件路径... (系统找不到指定的文件。)at java.io.FileInputStream.open(Native Method)at java.io.FileInputStream.<init>(FileInputStream.java:138)at java.io.FileInputStream.<init>(FileInputStream.java:93)at ExceptionTest04.n3(ExceptionTest04.java:44)at ExceptionTest04.n2(ExceptionTest04.java:33)at ExceptionTest04.n1(ExceptionTest04.java:25)at ExceptionTest04.main(ExceptionTest04.java:15)

 

 
 
 
注意: 采用上报方式抛异常,此方法后续的代码不会执行,另外try语句块中某一行出现异常,该行后面的代码不会执行,try...catch捕捉异常后,后续的代码可以执行
 
 
2、try...catch捕获异常:
/**
* @author Jason
* @create 2020-07-06 9:34
* try...catch形式捕获异常深入学习
*/
public class ExceptionTest05 {public static void main(String[] args) {//第一种:/*try {FileInputStream fileInputStream = new FileInputStream("文件路径...");System.out.println("上面出现异常,这里无法执行...");//这里可以是具体异常的catch} catch (FileNotFoundException e) {e.printStackTrace();}*///第二种:/*try {FileInputStream fileInputStream = new FileInputStream("文件路径...");System.out.println("上面出现异常,这里无法执行...");//这里可以是直接父类异常的catch} catch (IOException e) { //多态:IOException e = new FileNotFoundException();e.printStackTrace();}*///第三种:/*try {FileInputStream fileInputStream = new FileInputStream("文件路径...");System.out.println("上面出现异常,这里无法执行...");//这里可以是父类异常的catch} catch (Exception e) { //多态:Exception e = new FileNotFoundException();e.printStackTrace();}*///JDK8新特性try {FileInputStream fileInputStream = new FileInputStream("文件路径...");System.out.println(5/0);} catch (FileNotFoundException | ArithmeticException |NullPointerException e) {e.printStackTrace();}}
}

 

 
3、我们在开发中有上抛异常、有try...catch捕获那么到底选择哪个呢?
希望调用者来处理,选择throws上报,其他情况使用try...catch方式捕获。
 
 
getMessage方法和printStackTrace方法
/**
* @author Jason
* @create 2020-07-06 10:19
* getMessage方法和printStackTrace方法
*/
public class ExceptionTest06 {public static void main(String[] args) {//getMessage方法:获取异常简单的描述信息NullPointerException nullPointerException = new NullPointerException("空指针异常...");String message = nullPointerException.getMessage();System.out.println(message);//printStackTrace方法:打印异常追踪的堆栈信息try {m1();} catch (FileNotFoundException e) {e.getMessage();e.printStackTrace();}}public static void m1() throws FileNotFoundException {m2();}public static void m2() throws FileNotFoundException {m3();}public static void m3() throws FileNotFoundException {FileInputStream fileInputStream = new FileInputStream("文件路径...");}
}

 

控制台打印信息:
空指针异常...
 
java.io.FileNotFoundException: 文件路径... (系统找不到指定的文件。)at java.io.FileInputStream.open(Native Method)at java.io.FileInputStream.<init>(FileInputStream.java:138)at java.io.FileInputStream.<init>(FileInputStream.java:93)at ExceptionTest06.m3(ExceptionTest06.java:34)at ExceptionTest06.m2(ExceptionTest06.java:30)at ExceptionTest06.m1(ExceptionTest06.java:26)at ExceptionTest06.main(ExceptionTest06.java:18)

 

 
注意: 异常信息需要从上往下看,很多使用后面的异常是因为前面引起的。
 
try...catch中的finally子句
如果没有finally语句,前面的语句一旦出现了异常,后面的语句就不会被执行,如果是需要被关闭的流呢? 但是流又必须关闭,因为它是需要占用资源的,所以这里就体会到了finally的关键。通过把流放到finally中,这样就可以确保他一定被执行到,也就不用担心他的关闭问题了。
下面看看这个简单的实例代码:
/**
* @author Jason
* @create 2020-07-06 10:39
* try...catch中的finally子句
*/
public class ExceptionTest07 {public static void main(String[] args) {FileInputStream fileInputStream = null;try {fileInputStream = new FileInputStream("F:\\学习资料\\JavaSE-资料\\001-JavaSE课堂笔记+思维导图\\02-JavaSE零基础每日复习与笔记\\day18课堂笔记.txt");String str = null;str.toString();System.out.println("Hello Exception!");//注意:此处不会被执行,由于上面的代码行出现了异常,但是流又必须关闭,因为它是需要占用资源的,所以这里就体会到了finally的关键//fileInputStream.close();} catch (FileNotFoundException e) {e.printStackTrace();}catch (IOException e){e.printStackTrace();}catch (NullPointerException e){e.printStackTrace();}finally {//将流放到finally中关闭是最保险的,因为finally中的代码无论如何都是会被执行到的if (fileInputStream != null){ //避免空指针异常try {System.out.println("Hello Jason!");fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}}System.out.println("Hello World!");}
}

 

控制台输出结果:
java.lang.NullPointerExceptionat ExceptionTest07.main(ExceptionTest07.java:16)
Hello Jason!
Hello World!

 

 
final  finally  finalize有什么区别?
1、final是关键字,表示最终的,不变的 ,如:final int i = 10;
2、finally关键字和try联合使用,经常在异常处理机制中使用,finally语句中的代码一定会执行
3、finalize是object类中的一个方法,作为方法名出现,finalize是标识符
 
自定义异常
1、为何需要自定义异常?
JDK中的异常不够用,在实际开发中,有很多业务出现异常后JDK中没有这些和业务相关的异常,所有这个时候就需要我们自己自定义异常。
2、如何自定义异常呢?
第一步:编写一个类继承Exception类或RuntimeException
第二步:提供两个构造方法,一个是无参数的,一个是有String类型参数的构造方法
3、自定义异常实例:
/**
* @author Jason
* @create 2020-07-06 15:26
* 自定义编译时异常
*/
public class ExceptionTest09 {public static void main(String[] args) {MyException e = new MyException("用户名不能为空!");String message = e.getMessage();System.out.println(message);e.printStackTrace();}
}/**
* @author Jason
* @create 2020-07-06 15:29
* 自定义编译时异常
*/
public class MyException extends Exception{public MyException(){}public MyException(String s) {super(s);}
}

 

 
控制台输出结果:
用户名不能为空!
MyException: 用户名不能为空!at ExceptionTest09.main(ExceptionTest09.java:7)

 

 
 
自定义异常的使用:
/**
* @author Jason
* @create 2020-07-06 15:53
*/
public class ExceptionTest10 {public static void main(String[] args) {// 创建栈对象MyStack stack = new MyStack();// 压栈try {stack.push(new Object());stack.push(new Object());stack.push(new Object());stack.push(new Object());stack.push(new Object());stack.push(new Object());stack.push(new Object());stack.push(new Object());stack.push(new Object());stack.push(new Object());// 这里栈满了stack.push(new Object());} catch (MyStackOperationException e) {// 输出异常的简单信息。System.out.println(e.getMessage());}// 弹栈try {stack.pop();stack.pop();stack.pop();stack.pop();stack.pop();stack.pop();stack.pop();stack.pop();stack.pop();stack.pop();// 弹栈失败stack.pop();} catch (MyStackOperationException e) {System.out.println(e.getMessage());}}
}/**
* @author Jason
* @create 2020-07-06 15:50
*/
public class MyStack {private Object[] elements;private int index;/*** 无参数构造方法。默认初始化栈容量10.*/public MyStack() {// 一维数组动态初始化// 默认初始化容量是10.this.elements = new Object[10];// 给index初始化this.index = -1;}/*** 压栈的方法* @param obj 被压入的元素*/public void push(Object obj) throws MyStackOperationException {if(index >= elements.length - 1){throw new MyStackOperationException("压栈失败,栈已满!");}// 程序能够走到这里,说明栈没满// 向栈中加1个元素,栈帧向上移动一个位置。index++;elements[index] = obj;// 在声明一次:所有的System.out.println()方法执行时,如果输出引用的话,自动调用引用的toString()方法。System.out.println("压栈" + obj + "元素成功,栈帧指向" + index);}/*** 弹栈的方法,从数组中往外取元素。每取出一个元素,栈帧向下移动一位。* @return*/public void pop() throws MyStackOperationException {if(index < 0){throw new MyStackOperationException("弹栈失败,栈已空!");}// 程序能够执行到此处说明栈没有空。System.out.print("弹栈" + elements[index] + "元素成功,");// 栈帧向下移动一位。index--;System.out.println("栈帧指向" + index);}// set和get也许用不上,但是你必须写上,这是规矩。你使用IDEA生成就行了。// 封装:第一步:属性私有化,第二步:对外提供set和get方法。public Object[] getElements() {return elements;}public void setElements(Object[] elements) {this.elements = elements;}public int getIndex() {return index;}public void setIndex(int index) {this.index = index;}
}/**
* @author Jason
* @create 2020-07-06 15:52
*/
public class MyStackOperationException extends Exception{ // 编译时异常!public MyStackOperationException(){}public MyStackOperationException(String s){super(s);}
}

 

 
注意: 重写之后的方法不能比重写之前的方法抛出更多的异常,可以更少