Java中的异常Throwable分为三种情况
- Error:严重的错误,是我们无法处理的问题,比如OOM,内存溢出问题
- 运行时异常RuntimeException:编译器不会检查的异常,可以处理也可以不处理。但这个异常我们通常不处理,因为运行时异常一般都是我们写的程序代码出了问题。与其抛出解决异常,不如直接把代码修改正确就行。比如数组下标越界,访问null指针,类型转换错误等。
- 编译时异常: 除了RuntimeException运行时异常以外的异常都是编译时异常。这种异常必须要处理,如果不处理程序就无法通过编译。比如File对象中有不存在的路径文件。
如果程序出现了问题,我们没有做任何处理,JVM会给出一个默认的处理结果。把异常的名称,相关的原因,以及出现问题的相关信息,包括位置输出在控制台
同时程序结束,后面的代码不会运行
目录
那么为什么会有异常的抛出与处理机制呢?
因为在程序运行的过程中,可能会出现意想不到的情况,比如读取文件时发现文件不存在,这个就是异常。异常不一定会发生,但是如果发生了,程序要能够抛出异常,避免因为一个异常,而导致整个程序的崩溃,这个后果可能会非常严重。
抛出和处理异常后,try-catch-finally语句块之后的程序代码依旧可以正常的运行,而如果不做异常处理,之后的程序代码就不会运行了。
异常举例
运行时异常举例
package review.ExceptionDemo;
public class demo4 {
public static void main(String[] args) {
//运行时异常举例
System.out.println("befor异常之前");
int r1 = div(2,0);
System.out.println(r1);
System.out.println("after异常之后");
}
public static int div(int a,int b){
return a/b;
}
}
除数不能为0,发生异常,程序直接停止,不会继续向下运行
package review.ExceptionDemo;
public class demo4 {
public static void main(String[] args) {
//运行时异常举例
int[] arr = {1,2,3,4,5};
int r2 = array(arr);
System.out.println(r2);
}
public static int array(int[] arr){
return arr[arr.length];
}
}
数组下标越界异常
编译时异常举例
在编译的时候就会出现错误信息提示,说明这里可能会出现异常,但是程序中又没有对这个异常进行处理,所以不能编译
package review.ExceptionDemo;
import java.text.SimpleDateFormat;
import java.util.Date;
public class demo5 {
public static void main(String[] args) {
//编译时异常举例
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s ="2021-10-18";
try {
Date date = sdf.parse(s);
System.out.println(date);
}catch (Exception e){
e.printStackTrace();
}
}
}
修改后可以正常的编译程序,并且捕获异常
处理异常
try-catch-finally
那么现在的问题是,我们知道这里可能会出现异常,为了避免程序的崩溃,所以我们需要对这个异常进行抛出并且处理
异常的处理格式:
1、try…catch…finally
2、throws
try...catch...finally的处理格式:
try{
可能会出现问题的代码;
}catch(异常的类名 变量名){
针对问题的一些处理;
没有发生此catch对应的异常,此catch中的语句不会执行
}finally{
释放资源的代码
不管是否发生异常,都会执行finally中的语句
}
package review.ExceptionDemo;
public class demo6 {
public static void main(String[] args) {
System.out.println("before");
int a = 5;
int b = 0;
//try...catch处理一些可能会出现问题的代码
try {
System.out.println(a/b);
}catch (ArithmeticException a1){
System.out.println("除数不能为0");
}
System.out.println("after");
}
}
当然,一段代码中也可能有多个异常,有几个异常就需要写几个catch语句
package review.ExceptionDemo;
public class demo6 {
public static void main(String[] args) {
System.out.println("before");
int a = 5;
int b = 0;
try{
System.out.println(a / b);
int[] arr = {1, 2, 3, 4, 5};
System.out.println(arr[5]);
}catch (ArithmeticException a1){
System.out.println("除数不能为0 ");
}catch (ArrayIndexOutOfBoundsException a2){
System.out.println("数组下标越界");
}
}
}
注意:
- 异常的类型尽可能的准确,如果能明确异常的种类时,就不要用该异常的父类
比如IOException可以处理的异常,不要用Exception来概括 - 当多个异常之间是平级的关系时,多个catch语句没有前后顺序关系,谁在前谁在后都行。但是一旦出现了父类继承的关系,父类异常必须在后。
- 一旦try里面的代码出现了问题,就会去匹配catch里面的异常,继续执行catch中的语句。try里面的代码就停在了出现问题的那一步。但是try-catch语句后面的代码可以正常执行。
package review.ExceptionDemo;
public class demo6 {
public static void main(String[] args) {
try{
System.out.println("before");
int[] arr = {1, 2, 3, 4, 5};
System.out.println(arr[5]);
System.out.println("after");
} catch (ArrayIndexOutOfBoundsException a2){
System.out.println("数组下标越界");
System.out.println("继续执行");
}finally{
System.out.println("不管是否发生异常,都会执行");
}
System.out.println("之后的程序代码正常执行");
}
}
package review.ExceptionDemo;
public class demo6 {
public static void main(String[] args) {
try{
System.out.println("before");
int[] arr = {1, 2, 3, 4, 5};
System.out.println(arr[3]);
System.out.println("after");
} catch (ArrayIndexOutOfBoundsException a2){
System.out.println("数组下标越界");
System.out.println("继续执行");
}finally{
System.out.println("不管是否发生异常,都会执行");
}
}
}
JDK7的新特性,可以一次性处理多个异常
try{
可能会出现问题的代码
}catch(异常类名1|异常类名2|... 变量名){
处理异常的语句
}
public class demo7 {
public static void main(String[] args) {
int a = 3;
int b = 0;
try{
System.out.println(a / b);
int[] arr = {1, 2, 3, 4, 5};
System.out.println(arr[5]);
}catch (ArithmeticException|ArrayIndexOutOfBoundsException a1){
System.out.println("发生异常");
}
}
}
注意:
- 不同的异常处理的方式是一样的,虽然简洁但是不能针对性的进行处理
- 多个异常的类型之间必须是平级的关系
catch语句块中的方法
package review.ExceptionDemo;
public class demo8 {
public static void main(String[] args) {
int a = 10;
int b = 0;
try {
System.out.println(a/b);
}catch (ArithmeticException a1){
//方法一
String message = a1.getMessage();
//打印出现问题的原因
System.out.println(message);
//方法二
String s = a1.toString();
System.out.println(s);
//方法三
a1.printStackTrace();
}
System.out.println("之后程序的代码正常执行");
}
}
这里打印的异常信息,与没有做异常处理时,JVM自动在控制台给出的异常信息类似。但是做了异常处理后,可以让程序后面的代码正常的执行,不会因为这个异常而导致整个程序的终止。
printStackTrace()
这个方法是从java.lang.Throwable类继承过来的,会将具体的错误发生的栈轨迹信息打印在控制台上
package test.ExceptionDemo;
public class ExceptionDemo3 {
public static void test1() throws Exception{
throw new Exception("Exception");
}
public static void test2() throws Exception{
test1();
}
public static void main(String[] args) {
try{
test2();
} catch(Exception e) {
e.printStackTrace();
}
}
}
在方法test1()中抛出异常,方法test2()中调用方法test1(),在main方法中捕获异常,并且打印栈轨迹信息。因此,输出依次展示了test1() —> test2() —> main的过程。
throws
有时候我们可以针对一些异常进行处理,但是也有的时候,我们无法处理这个异常
对于这种情况,为了保证程序的正常执行,可以使用try-catch-finally之外的第二个处理方法
throws抛出异常,用在方法的声明上,表明当前方法不处理异常,等实际调用的时候,由该方法的调用者来处理异常
格式:
throws 异常类名
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2 … { }
注意:
- 抛出编译时异常,抛出异常的方法内可以不做处理,但是调用该方法的时候必须做处理,否则编译时就会报错
- 抛出运行时异常,调用时可以不做任何处理。编译时不会报错,但是程序遇到错误会终止
- 最好抛出详细的异常类型,也可以抛出更大的异常类型,但是处理的时候要对应上。比如throws Exception,则catch语句中是 Exception e
- 尽量不要在main方法上抛出,如果在main方法上抛出,就会交给JVM处理,遇到异常时程序会直接停止
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class demo9 {
public static void main(String[] args) {
System.out.println("程序开始");
try {
fun1();
}catch (Exception pe){
pe.printStackTrace();
}
try {
fun2();
}catch (ArithmeticException ae){
ae.printStackTrace();
}
System.out.println("程序结束");
}
public static void fun2() throws ArithmeticException{
int a = 10;
int b =0;
System.out.println(a/b);
}
public static void fun1() throws ParseException {
String s = "2021-10-18";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(s);
System.out.println(date);
}
}
发生异常之外的代码都正常的执行了
throw
throws和throw的区别:
- throws用来声明一个方法可能产生的所有异常,throw用来抛出一个具体的异常类型。
- throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。
throws语句用在方法声明后面,表示抛出异常,由该方法的调用者来处理 - throws只是表明一种可能性,不一定会抛出异常
throw则是一定会抛出一个具体的异常
throw举例
package review.ExceptionDemo;
public class demo10 {
public static void main(String[] args) {
fun();
}
public static void fun(){
int a = 10;
int b = 0;
if(b == 0){
System.out.println("报错,除数不能为0");
throw new ArithmeticException();
}else {
System.out.println(a/b);
}
}
}
throws举例
package review.ExceptionDemo;
public class demo10 {
public static void main(String[] args) {
try{
fun();
}catch(ArithmeticException ar){
ar.printStackTrace();
}
System.out.println("程序结束");
}
public static void fun() throws ArithmeticException{
int a = 10;
int b = 0;
System.out.println(a/b);
}
}