Java中异常是一个非常重要的概念。它是一种在程序执行期间发生错误的情况,并且可以通过使用try-catch
语句来捕获和处理这些错误。如果没有适当处理异常,可能会导致应用程序的崩溃,因此我们需要掌握 Java 中的异常处理机制。在这篇博客中,这篇博客将深入探讨Java中异常处理的方方面面,包括异常的定义、分类和处理方法等。
一、什么是异常
首先,我们需要弄清楚什么是异常。在 Java 中,异常是指程序运行期间发生的错误或意外的情况,这些异常的情况出现了,就会破坏程序的正常执行流程。Java异常的产生可以是由于程序代码、操作系统或其他外部因素引起的。
二、Java中异常的分类
在 Java 中,Exception
有两类:
一类是受检查异常,另一类则是非受检查异常。在Java中,所有继承自Exception
的异常都是受检查异常,而继承自RuntimeException
的异常则是非受检查异常。
2.1、受查异常
在Java中,受查异常又称为编译时期异常,就是在编译的时候编译器就会给你报错,这种异常必须在方法的throws
子句中声明。这意味着调用者必须使用try-catch
语句捕获并处理这些异常,否则编译时将会出错。
受检查异常通常表示输入参数错误、I/O 错误、网络错误等异常情况。
以下是受查异常(编译时期异常)的示例代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
String strDate = "2023-04-25";
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = format.parse(strDate);
System.out.println(date);
}
}
上面这个代码的意思是我们要把一个字符串(strDate
)解析为一个日期对象(Date
),然后去调用这个对象的parse
方法,我们可以点Ctrl+鼠标左键
进去看这个方法,如下:
我们可以看到有个throws ParseException
的东西,他这个具体是什么意思呢?其实可以不用管,大致的意思就是这个方法有异常,具体怎么处理这个异常就要调用他的方法的方法(也就是这个main
这个方法)实现,具体怎么处理,我们现在先不说,先把异常介绍完一起说。
2.2、非受查异常
非受查异常也被称为运行时异常,通常是由于程序错误而引起的异常情况。与受查异常不同的是,非受查异常不需要在方法声明中声明,编译器不会强制要求使用try-catch
语句捕获这些异常。
在Java中,通常的非受查异常包括 NullPointerException
、ArithmeticException
、InputMismatchException
等。它们通常表示程序中的逻辑错误或数据错误。
如下代码:
public static void main(String[] args) {
System.out.println(10 / 0);
}
如上代码,你去IDEA编译器试试,他也不报错啊,但是大家都知道,编译器运行的时候也知道,这个代码错了,运行结果如下:
编译器是知道它错,但是不是特别聪明的样子,非要运行才报错,这就叫非受查异常。
三、Java 异常处理
当程序出现异常时,我们需要捕获并处理这些异常,以确保程序可以继续执行。在 Java 中,我们使用try-catch
块来捕获和处理异常。在try
块中,我们编写可能会导致异常的代码块,而在catch
块中,我们编写处理异常的代码块,什么意思呢?我写个模板给大家看一下,如下:
//语法格式:
try{
// 将可能出现异常的代码放在这里
}catch(要捕获的异常类型 e){
// 如果try中的代码抛出异常了,
// 此处catch捕获时异常类型与try中抛出的异常类型一致时,
// 或者是try中抛出异常的基类时,就会被捕获到
// 对异常就可以正常处理,
// 处理完成后,跳出try-catch结构,继续执行后序代码
}[catch(异常类型 e){
// 对异常进行处理
}finally{
// 此处代码一定会被执行到
}
3.1、捕获异常并处理
我们先看try—catch
的写法,就比如我刚刚举的受查异常的例子,我们可以改成这样,编译器就不会报错了,如下:
public static void main(String[] args) {
String strDate = "2021-07-01";
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = format.parse(strDate);
System.out.println(date);
} catch (ParseException e) {
System.out.println("日期格式不正确,捕获到了->ParseException异常!");
e.printStackTrace();//判断异常发生在第几行
} catch (Exception e) {//所有异常的父类,可以捕获所以异常,要是前面没捕获到,它可以兜底
e.printStackTrace();
}
}
catch
括号里面的是判断是否捕获到的是这个异常,和if—else
语句差不多逻辑,意思是如果你是这个异常,我就进去执行这个异常的代码,里面的代码自己定。e.printStackTrace()
这个方法是判断异常具体发生在第几行的,如果你的catch
没有捕获到异常,那就会交给JVM去处理,那就是直接终止程序了。
3.2、finally
块
在 Java 中,finally
块用于在try-catch
块结束后执行一些必要的清理工作,例如关闭Scanner
,释放空间内存等。
以下是 finally 块的示例代码:
public static void main3(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("=========开始=========");
try {
int a = sc.nextInt();
int ret = 10 / a;
System.out.println("ret->" + ret);
} catch (ArithmeticException e) {
System.out.println("我捕获到了分母为0 -> ArithmeticException的异常");
} catch (NullPointerException e) {
System.out.println("我捕获到了空指针 -> NullPointerException异常");
} catch (InputMismatchException e) {
System.out.println("输入参数异常 -> InputMismatchException异常");
} finally {
sc.close();//Scanner也是一个类,打开就要释放
System.out.println("finally执行了!");
}
System.out.println("=========结束=========");
}
在上面的代码中,我们使用try-catch-finally
块判断你输入的a
与执行的结果。在finally
块中,我们使用close()
方法关闭Scanner
。
3.3、抛出异常
在方法声明中使用throws
子句声明可能会抛出的异常。这将会导致方法的使用者必须捕获并处理这些异常,否则编译时将无法通过。
以下是抛出异常的示例代码:
public static void main(String[] args) {
int a = 10;
if (a == 10) {
throw new NullPointerException("空指针异常");//throw:抛出自定义异常,这段代码的意思为抛出空指针异常
}
}
上面这段代码的意思是:如果a == 10
,那就抛出空指针异常,类似于return
,仅仅只是这一点类似哈,别搞混了。
四、自定义异常
哎,已经学了这么久的理论了,一直学理论也太无聊了,那我们就用上面学的这么多来开心,爽一下,写一个自定义的异常,和自定义函数一样,返回值直接就是void
,这也是和return
不一样的一个点,下面是代码实现:
import java.util.Scanner;
class ZiDing {
public void A(int a) {
try {
System.out.println(10 / a);
} catch (ArithmeticException e) {
throw new RuntimeException();
}
System.out.println(10 / a);
}
}
public class ZiDingYi {
public static void main(String[] args) {
int n = 0;
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
ZiDing ziding = new ZiDing();
try {
ziding.A(n);
} catch (ArithmeticException e) {
e.printStackTrace();
System.out.println("我捕捉到了a为0的异常->ArithmeticException");
} finally {
sc.close();
System.out.println("结束");
}
}
}
试着输入一下n
,0和非零是两种不一样的结果哦,结果如下:
首先看这上面两个结果,应该都能理解,但是要注意一个点,那就是他们最后的退出代码结果都是0,如果是给JVM识别出错误的话,退出的结果应该是1。
五、异常使用的注意事项
在使用异常时,需要注意以下几个方面:
5.1、异常捕获的顺序
在catch
块中,需要先捕获继承自Exception
类的子类,最后捕获Exception
类。否则,如果有相同的异常类型,则会先执行子类捕获块,而后续的父类捕获块将会被忽略。
5.2、避免空catch
块
如果catch
块不提供任何处理代码,可能会掩盖问题或不正确的处理异常,因此应尽可能避免使用空catch
块。
5.3、避免过度使用finally
块
在finally
块中,需要注意资源清理工作的顺序,避免资源泄漏。但是,如果过度使用finally
块,可能会使代码逻辑变得复杂和难懂。
5.4、异常的处理方式应该根据实际情况选择
在处理异常时,我们应该根据实际情况选择合适的异常类型,并为它们提供适当的处理方法。只有这样,才能保证程序可以正确地处理异常。
六、结论
本篇博客深入探讨了 Java 中异常处理的方方面面,包括异常的定义、分类和处理方法等。通过学习本篇博客,读者应该已经掌握了 Java 中异常处理的基本知识和相关技能。
在编写 Java 代码时,合理处理异常是必要的。良好的异常处理机制可以提高程序的健壮性和可靠性,有效避免程序崩溃、数据丢失和安全漏洞等问题。
以上就是关于 Java 异常处理的全部内容,不知道你是否有所收获。值得注意的是,异常处理是 Java 中的重要知识点之一,需要细心和耐心地掌握。在实际开发中,我们要灵活运用异常处理机制,避免出现不必要的错误和隐患。