12、异常Exception:
我们来看一段代码,了解下为什么需要异常处理:
package com.jiangxian.Exception_;/*** @author JiangXian~* @version 1.0*/
public class Exception01 {public static void main(String[] args) {int num1 = 10;int num2 = 0;int res = num1/num2;System.out.println("程序继续运行...");}
}
好的,这段代码会报错,出错的原因是除数为0了。我们来想一想这样子合不合理,倘若我们有一个很庞大的代码,我们因为这一个非常细微的错误,导致我们代码的崩溃,从而后续的所有代码都不执行了,这样做合理吗?显然是不合理的,这样我们的系统不够健壮。
解决方法:异常捕获
对异常进行捕获,保证程序可以继续运行。
package com.jiangxian.Exception_;/*** @author JiangXian~* @version 1.0*/
public class Exception01 {public static void main(String[] args) {int num1 = 10;int num2 = 0;// 1.num1/num2 => 10/0// 2.当执行到 num1/num2 因为 num2 = 0,程序就会出现(抛出)异常,此处为 ArithmeticException// 3.当抛出异常后,程序就崩溃了,下面的代码就不再执行;// 4.当我们认为一段代码可能出现 异常/问题,可以使用 try-catchtry {int res = num1/num2;} catch (Exception e) {e.printStackTrace();}System.out.println("程序继续运行...");// 加入异常处理机制,虽然有异常仍然会执行后面的代码。}
}
异常介绍:
基本概念:
java语言中,将程序执行中发生的不正常情况称为“异常“。(开发过程中的语法错误和逻辑错误不是异常!)
可以分为两大类:
- Error(错误):java虚拟机无法解决的严重问题。如JVM系统内部错误,资源耗尽等严重情况。
- Exception:因编译错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。其还能继续细分。
- 运行时异常(RUNTIME),在程序运行时会发生的异常;
- 编译时异常,编译器检查出的异常。
异常体系图:
- 异常分为两大类,运行时异常和编译时异常;
- 运行时异常,编译器检查不出来。一般指编程时的逻辑错误,==程序员应该尽力去避免其出现(否则整个代码中全是try-catch语句)。对于运行时异常,可以不做异常处理,报错了去更改即可,否则这种异常过于普遍,导致程序的可读性大大降低;
- 编译时异常,是编译器要求必须处理的异常。
常见的运行时异常:
- NullPointException——空指针异常;
- ArithmeticException——数学运算异常;
- ArrayIndexOutOfBoundsException——数组下标越界异常;
- ClassCastException——类型转换异常;
- NumberFormatException——数字格式不正确异常。
举例:
NullPointException:
当应用程序在需要对象的地方使用null时,抛出异常。(对象还没有创建出来就使用了)
package com.jiangxian.Exception_;/*** @author JiangXian~* @version 1.0*/
public class NullPointException_ {public static void main(String[] args) {String name = null;System.out.println(name.length());}
}
ArithmeticException:
当出现异常的运算条件时,抛出此异常。例如一个整数除以0。
/*** @author JiangXian~* @version 1.0*/
public class ArithmeticException_ {public static void main(String[] args) {int num1 = 10;int num2 = 0;int res = num1 / num2;}
}
ArrayIndexOutOfBoundsException:
用非法的索引去访问数组时抛出的异常。如索引为负数或大于等于数组的length-1,则该索引为非法索引。
package com.jiangxian.Exception_;/*** @author JiangXian~* @version 1.0*/
public class ArrayIndexOutOfException_ {public static void main(String[] args) {int[] a = new int[5];System.out.println(a[5]);}
}
ClassCastException:
当试图将对象强制转换为不是实例的子类时,抛出该异常。
package com.jiangxian.Exception_;/*** @author JiangXian~* @version 1.0*/
public class ClassCastException {public static void main(String[] args) {A a = new B(); // 向上转型B b = (B)a; // 向下转型,okC c = (C)a; // 不行}
}class A{}
class B extends A{}
class C extends A{}
NumberFormatException:
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,会抛出异常。
例子就是英文不能作为数字输出。
package com.jiangxian.Exception_;/*** @author JiangXian~* @version 1.0*/
public class NumberFormatException {public static void main(String[] args) {String name = "JiangXian";int num = Integer.parseInt(name);System.out.println(num);}
}
小结一些我的发现:
这些异常,都是会被标注为黄色的Warning,我们可以无视它运行程序(但是程序可能会报错)。
所以运行异常会被标注为黄色的Warning;但是并不是所有的黄色Warning都是运行异常!
其能够通过编译,但是运行不能成功。
编译异常:
编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。(你可以去终端中尝试 javac 编译一下,发现,运行异常,是会正常生成字节码文件.class的,但是编译异常是生成不了的,还会报错)。
常见的编译异常:
一般发生在,网络、文件、数据库操作的情况。
- SQLException——操作数据库时,查询表可能发生异常;
- IOException——操作文件时,发生的异常;
- FileNotFoundException——当操作一个不存在的文件时,发生异常;
- ClassNotFoundException——加载类,而该类不存在时,异常;
- EOFException——操作文件,到文件末尾,发生异常;
- IllegalArguementException——参数异常。
异常处理:
基本介绍:
异常处理就是当异常发生时,对异常处理的方式;
异常处理的方式:
- try-catch-finally:程序员在代码中捕获发生的异常,自行处理。
- throws:将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM。
try-catch-finally:
java中提供 try 和 catch块来处理代码。
catch块——用于处理 try 中发生地异常。
根据需求在程序中可以有多个 try…catch块。
处理机制:
try{代码/可能有异常
}catch(Exception e){// 捕获到异常:// 1.当异常发生时;// 2.系统将异常封装为Exception 对象 e,传递给 catch;// 3.得到异常对象后,程序员,自行处理;// 4.注意若没有异常,catch 代码块不执行代码块
}fianlly{// 1.不管是否有异常发生,始终要执行finally;// 2.所以,通常将释放资源的代码,放在finally中。代码块
}
细节:
-
若异常发生了,那么异常发生后面地代码不会执行,直接进入到 catch 块;
-
若异常没有发生,则顺序执行 try 代码块,不会进入到 catch;
-
若希望不管是否发生异常,都执行某段代码(关闭连连接,释放资源),则使用 finally{}代码块;
-
可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception 在后,NullPointException 在前),若发生异常,指挥匹配一个catch;
package com.jiangxian.exception_;public class try_catch_detail {public static void main(String[] args) {// 解读:// 1. 若try代码块可能有多个异常// 2.可以用多个catch去捕获不同的异常// 3.要求子类异常在前,父类异常写在后面try{Person person = new Person();person = null;System.out.println(person.getage()); // NullPointExceptionint n1 = 10;int n2 = 0;int res = n1 / n2; // ArithmaticException}catch(NullPointerException e){System.out.println("空指针异常:" + e.getMessage());}catch(ArithmeticException e){System.out.println("算数异常:" + e.getMessage());}catch(Exception e){System.out.println(e.getMessage());}} }class Person{int age = 10;public int getage(){return age;} }
-
可以进行 try-finally 配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉/退出。应用场景——执行一段代码,不管是否发生异常,都必须执行某个业务逻辑。
package com.jiangxian.exception_;public class tryCatchDetail {public static void main(String[] args) {try{int n1 = 10;int n2 = 0;int res = n1 / n2;}finally{System.out.println("程序继续运行...");}} } // 由于没有进行任何的处理,所以实际上时JVM进行的异常处理,在终端会直接报错。
-
当 catch 和 finally 都有return的时候,若捕获到异常,先执行 catch 中的 return 语句,但暂时不返回,返回的是finally中的 return 语句中的结果(catch 的语句执行但不返回,返回的是 finally 中 的renturn);
-
当 finally 无 return,但catch 中有return时,先执行catch 中的return 语句,但暂时不执行,然后执行 finally 中的语句,最后回到catch 的语句执行返回。(如 catch 中是 return i; 但是 finally 中有个++i,那么在第一次遇到return 语句的时候,会有个temp变量暂存i,执行完++i操作后,回到catch 中的return 语句是,将temp 的值重新赋值给i,再返回)。
Exercise:
要求输入字符串,判断是不是整数,不是就一直输入,直到是整数为止。
throws(抛出):
基本介绍:
-
若一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表面该方法将不对这些异常进行处理,而是由方法的调用者负责处理;
-
在方法声明中throws语句可以声明抛出异常的列表(可以抛出多个种类的异常),throws后面地异常类型可以是方法中产生地异常类型,也可以是它的父类。
package com.jiangxian.exception_.throws_;import java.io.FileInputStream; import java.io.FileNotFoundException;public class Throws01 {public static void main(String[] args) {}public void f1() throws FileNotFoundException,NullPointerException/*写成Exception即它的父类也可以*/ {// 创建了一个文件流对象// 这是一个编译时异常,必须处理// 1.可以使用try-catch// 2.使用throws,明确抛出异常,让调用f1方法的调用者处理// 3.throws后也可以是异常列表(也可以用一个Exception代替是所有的)FileInputStream fis = new FileInputStream("d://aa.txt");} }
处理机制:
- try-catch-finally 和 throws 二选一;
- 若程序员,没有显示地处理异常,默认采用throws。
模拟流程:
JVM -》调用 main -》调用 f1 -》调用f2(此处发生异常);
f2(抛出异常)-- throws(当也可以时try-catch)-》f1– throws(也可以try-catch)-》main–throws(try-catch)-》JVM。
throws 实际上就是 当前不想处理,返回给上级,让上级去处理,若都不处理,那么最高级JVM处理异常机制特别暴力,其会输出异常并退出程序。
细节:
-
对于编译异常,程序中必须处理,比如 try-catch 或 throws;
-
对于运行时异常,程序中若没有处理,默认就是 throws的方法;
public class ThrowsDetails {public static void main(String[] args) {}public static void f1() /*默认有 throws ArithmeticExpection*/{int n1 = 10;int n2 = 0;int res = n1/n2;} }
-
子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类异常的子类;
class Father{void method() throws RuntimeException{} }class Son extends Father{// 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,// 所重写的异常类型需要是父类的异常或是父类异常的子类@Overridepublic void method() throws NullPointerException /*换成Exception就不行了*/{}// 在方法的访问范围,子类可以扩展(业务方面,儿子可以做大做强),但是在异常上,子类只能缩小(儿子犯的错得要是老子能解决的) }
-
在 throws 过程中,若有 try-catch,相当于处理异常,不用再throws了。
自定义异常:
基本概念:
当程序中出现了某些“错误”,但该错误信息并没有在Throwable子类的描述中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。
自定义异常的步骤:
- 定义类:自定义异常类型的类名(程序员自己写)继承Exception或RuntimeException;
- 若继承Exception则为编译异常;
- 若继承RuntimeException,则为运行异常(一般来说继承这个)。
- 好处是,我们可以使用默认的处理机制;
- 否则我们需要显式地处理。
package com.jiangxian.Exception_.customException;/*** @author JiangXian~* @version 1.0*/
public class CustomException_ {public static void main(String[] args) {int age = 121;if(!(age >= 18 && age <= 120)){throw new ArithmeticException("年龄需要在 18~120之间。");}System.out.println("你的年龄范围正确。");}
}class AgeException extends RuntimeException{public AgeException(String message) { // 构造器super(message);}
}
throw VS throws:
意义 | 位置 | 后面跟的东西 | |
---|---|---|---|
throws | 异常处理地一种方式 | 方法声明处 | 异常类型 |
throw | 手动生成异常对象地关键字 | 方法体中 | 异常对象 |
Homework:
package com.jiangxian.Exception_.homework;
import java.util.Scanner;/*** @author JiangXian~* @version 1.0*/
public class Homework01 {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while(true){try{System.out.println("请输入第一个整数");String n1 = scanner.next();int num1 = Integer.parseInt(n1);System.out.println("请输入第二个整数");String n2 = scanner.next();int num2 = Integer.parseInt(n2);System.out.println(cal(num1,num2));break;}catch (NumberFormatException e){System.out.println("输入的数不是整数,请重新输入...");}catch(ArithmeticException e){System.out.println("你输入的除数为0...");}}}public static double cal(int n1, int n2){return n1 / n2;}
}
(459/910)
不知不觉已经学完一半啦,比我预想的慢一点~