《Java基础知识》Java异常处理详解
1. Java 中的异常
前言:Java 中的异常处理是处理程序运行错误时的强大机制之一,它可以保证应用程序的正常流程。
首先我们将了解java异常、异常的类型以及受查和非受查异常之间的区别。
1.1 什么是异常?
字面意义:异常是一种不正常的情况。
在 java 中,异常是扰乱程序正常流程的事件,它是在程序运行时抛出的对象。
1.2 什么是异常处理?
异常处理一种在运行时解决程序错误的机制,例如 ClassNotFound、IO、SQL、Remote 等。
1.2.1 异常处理的优势
异常通常会干扰程序的正常流程,而异常处理的核心优势是维护程序的正常流程。现在让我们假设一下:
statement 1;
statement 2;
statement 3;
statement 4;
statement 5;//发生异常
statement 6;
statement 7;
statement 8;
statement 9;
statement 10;
假设你的程序中有10条语句,如果在第5条中出现了一个异常,那么语句6-10将不会继续执行。如果你使用了异常处理,那么语句6-10的部分将正常执行,这就是我们为什么需要在程序中使用异常处理的原因。
1.3 Java 异常类的层次结构
1.4 异常类型
主要有两种类型的异常:受查和非受查异常,Error
被视为非受查异常。Sun公司认为有三种异常类型:
- 受查异常(Checked Exception)
- 非受查异常(UnChecked Exception)
- 错误(Error)
1.5 受查和非受查异常之间的区别
1)受查异常
除了RuntimeException
和Error
外,继承自Throwable
类的类称为受查异常,例如:IOException、SQLException 等。受查异常在编译时进行检查。
常见的有以下几个方面:
- 试图在文件尾部后面读取数据
- 试图打开一个不存在的文件
- 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在
2)非受查异常
继承自RuntimeException
类的异常被称为非受查异常,例如:ArithmeticException、 NullPointerException、 ArrayIndexOutOfBoundsException 等。非受查异常不会在编译时检查,而是在运行时进行检查。
常见的有以下几个方面:
- 错误的类型转换
- 数组访问越界
- 访问null指针
“如果出现了RuntimeException
异常,那么一定是你自身的问题”,是一条相当有道理的规则。
3)错误(Error)
错误是一种无法恢复的异常类型,通常是在java运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。如果出现了这样的内部错误,除了通告给用户,并尽力的使得程序安全的终止之外,再也无能为力了。这种情况很少出现。
1.6 可能出现异常的常见场景
在某些情况下,可能出现未检查的异常,它们如下:
1)发生ArithmeticException
的场景
如果我们将任何数字除以0,就会出现一个 ArithmeticException 异常。
int a = 50/0;//ArithmeticException
2)发生NullPointerException
的场景
如果变量的值为null
,那么调用此变量将会出现 NullPointerException 异常。
String s = null;
System.out.println(s.length());//NullPointerException
3)发生NumberFormatException
的场景
任何值的格式错误,都有肯能发生 NumberFormatException 异常。假设一个字符串变量,其中包含了字符,若将此变量转换为数字类型,将会发生 NumberFormatException 异常。
String s = "abc";
int i = Integer.parseInt(s);//NumberFormatException
4)发生ArrayIndexOutOfBoundsException
的场景
如果你在一个不存在的的数组索引中插入任何值,则会导致 ArrayIndexOutOfBoundsException 异常。
int a[] = new int[5];
a[10] = 50; //ArrayIndexOutOfBoundsException
1.7 Java 异常处理关键字
下面是 Java 异常处理中的5个关键字:
try
、catch
、finally
、throw
、throws
1.8 创建自定义异常类
在程序中,可能会遇到任何标准异常类都没有能够充分地描述清楚的问题。在这种情况下,创建自己的异常类就是一件顺理成章的事情了。我们需要做的只是定义一个派生于 Exception 的类,或者派生于 Exception 子类的类。例如,定义一个派生于 IOException 的类。
习惯上,定义的类应该包含两个构造器,一个是默认构造器,一个是描述详细信息的的构造器(超类 Throwable 的 toString 方法将会打印出这些详细信息,这在调试中非常有用。)
示例如下:
class FileFormatException extends IOException {
public FileFormatException() {}
public FileFormatException(String gripe) {
super(gripe);
}
}
String readData(BufferedReader in) throws FileFormatException {
...
while (...) {
// EOF encountered
if (ch == -1) {
if (n < len)
throw new FileFormatException();
}
...
}
return s;
}
2. Java try-catch
将可能发生异常的代码放在try
块中,且必须在方法中才能使用。try 块后必须使用catch
块或finally
块。
2.1 Java try 块
1)try-catch 语法
try{
// 可能抛出异常的代码
}catch(Exception_class_Name ref){}
2)try-finally 语法
try{
// 可能抛出异常的代码
}finally{}
2.2 Java catch 块
Java catch
块被用于处理异常,必须在try
块后使用。
你可以在一个try
块后使用多个catch
块
2.3 未使用异常处理的问题
如果我们不使用try-catch
处理异常,看看会发生什么。
public class Testtrycatch1 {
public static void main(String args[]) {
int data=50/0;// 可能抛出异常
System.out.println("代码的其余部分...");
}
}
运行结果:
如上面的示例所示,代码的其余部分并没有执行。("代码的其余部分..."未打印)
2.4 使用异常处理解决问题
让我们通过try-catch
块来查看上述问题的解决方案。
public class Testtrycatch2 {
public static void main(String args[]) { try {
int data = 50/0;
}
catch(ArithmeticException e) {
System.out.println(e);
} System.out.println("代码的其余部分...");
}
}
运行结果:
现在,正如上面的示例所示,代码的其余部分执行了.(也就是"代码的其余部分..."被打印)
Java 虚拟机首先检查异常是否被处理,如果异常未处理,则执行的一个默认的异常处理程序:
- 打印异常描述
- 打印堆栈跟踪(异常发生方法的层次结构)
- 终止程序
如果程序员处理了异常,则应用程序按照正常流程执行。
3. 使用多个 catch 块
如果需要在发生不同异常时执行不同的任务,则需要使用多个 catch 块。
查看下面一个简单的多 catch 块示例。
public class TestMultipleCatchBlock{
public static void main(String args[]) { try{
int a[] = new int[5];
a[5] = 30/0;
}
catch(ArithmeticException e) {
System.out.println("任务1已完成");
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println("任务2已完成");
}
catch(Exception e) {
System.out.println("已完成通用任务");
} System.out.println("代码的其余部分..."); }
}
输出:
规则:一次只有一个异常发生,并且一次只执行一个catch块。
规则: 所有异常必须从最具体到最通用的顺序排序,即捕获ArithmeticException
必须在捕获Exception
之前发生。
class TestMultipleCatchBlock1 {
public static void main(String args[]) { try{
int a[]=new int[5];
a[5]=30/0;
}
catch(Exception e) {
System.out.println("已完成通用任务");
}
catch(ArithmeticException e) {
System.out.println("任务1已完成");
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println("任务2已完成");
}
System.out.println("代码的其余部分...");
}
}
编译报错。
4. Java 嵌套 try 块
Java try块中的try块被称为try嵌套块。
4.1 为什么使用 try 嵌套块?
有时可能会出现一种情况,一个块的某个部分可能导致一个错误,而整个块的本身可能会导致另一个错误。在这种情况下,必须使用嵌套异常处理程序。
语法:
....
try
{
statement 1;
statement 2;
try
{
statement 1;
statement 2;
}
catch(Exception e)
{
...
}
}
catch(Exception e) {...}
....
4.2 Java try 嵌套块示例
class Excep6 {
public static void main(String args[]) {
try {
// try 嵌套块1
try {
System.out.println("try 嵌套块1");
int b = 39 / 0;
}
catch(ArithmeticException e) {
System.out.println(e);
}
// try 嵌套块2
try {
int a[] = new int[5];
a[5] = 4;
}
catch(ArrayIndexOutOfBoundsException e) {
System.out.println(e);
}
System.out.println("try外部块其他语句...");
}
catch(Exception e) {
System.out.println("handeled");
}
System.out.println("正常流...");
}
}
输出:
5. Java finally 块
Java finally 块是用来执行重要代码的块(如关闭连接、流等)。
无论是否处理异常,最终都会执行 finally 块。
finally 块紧跟 try 或 catch 块后
注意:无论你是否处理异常,在终止程序之前,JVM都将执行finally块(如果存在的话)
5.1 为什么要使用 finally 块
finally 块可以用于放置"clear"代码,例如关闭文件,关闭连接等。
5.2 使用 finally 块案例
接下来让我们来看看在不同情况下使用 finally 块。
案例:
public class TestFinallyBlock2 {
public static void main(String args[]) {
try {
int data = 25 / 0;
System.out.println(data);
}
catch(ArithmeticException e) {
System.out.println(e);
}
finally {
System.out.println("finally 块总是执行");
}
System.out.println("代码的其余部分...");
}
}
输出:
规则:对于 try 块可以有0个或多个 catch 块,但仅仅只能有一个 finally 块。
规则:如果程序退出(通过调用 System.exit() 或通过导致进程中止的致命错误),finally块将不会被执行。
6. Java 抛出异常
6.1 Java throw 关键字
Java throw 关键字用于显示的抛出异常。
我们可以使用 throw 关键字在 Java 中抛出检查(Checked)或未检查(UnChecked)异常。throw 关键字主要用于抛出自定义异常。
Java throw 语法如下:
throw exception;
抛出IOException
异常的例子:
throw new IOException("sorry device error");
6.2 Java throw 示例
在本例中,我们创建了一个将整数值作为参数的 validate 方法。如果年龄小于18岁,我们将抛出一个ArithmeticException
异常,否则打印一条消息"欢迎投票"。
public class TestThrow1 {
static void validate(int age) {
if(age < 18)
throw new ArithmeticException("无效");
else
System.out.println("欢迎投票");
} public static void main(String args[]) {
validate(13);
System.out.println("代码的其余部分...");
}
}
运行结果:
7. Java 异常传递
异常首先从堆栈顶部抛出,如果未捕获,则将调用堆栈下降到前一个方法,如果没有捕获,则将异常再次下降到先前的方法,以此类推,知道它们被捕获或到达调用堆栈底部为止。以上称为异常传递。
规则:默认情况下,非受查异常在调用链中(传递)转发。
异常传递示例:
class TestExceptionPropagation1 {
void m(){
int data = 50 / 0;
}
void n() {
m();
}
void p() {
try{
n();
}
catch(Exception e) {
System.out.println("异常处理器");
}
} public static void main(String args[]) {
TestExceptionPropagation1 obj = new TestExceptionPropagation1();
obj.p();
System.out.println("正常流...");
}
}
运行结果:
在上面的示例中。异常发生在 m() 方法中,如果未对其进行处理,则将其传递到未处理它的前 n() 方法,再次将其传递到处理异常的 p() 方法。
可以在 main()、p()、n()、p()、 m() 中的任何方法中处理异常。
规则:默认情况下,受查异常不会在调用链中(传递)转发。
用于描述受查异常不会在程序中传递的示例:
class TestExceptionPropagation2{
void m(){
throw new java.io.IOException("设备异常"); // 受查异常
}
void n(){
m();
}
void p(){
try{
n();
}
catch(Exception e){
System.out.println("异常处理器");
}
}
public static void main(String args[]){
TestExceptionPropagation2 obj=new TestExceptionPropagation2();
obj.p();
System.out.println("正常流...");
}
}
编译报错
8. Java throws 关键字
Java throws
关键字被用于声明一个异常。它给程序员提供了一个信息,说明可能会发生异常,所以程序员最好提供异常处理代码,以保证程序正常的流程。
异常处理主要用于处理受查异常,如果出现任何非受查异常,如"NullPointerException",都是程序员自身的错误,请认真检查你的代码。
8.1 Java throws 语法
return_type method_name() throws exception_class_name {
// method code
}
8.2 应该声明哪个异常?
仅仅声明受查异常,因为:
- 非受查异常:程序员应该更正代码以确保代码正确无误。
- Error:无法控制,如果出现了
VirtualMachineError
或StackOverflowError
等异常,将无法进行任何操作。
8.3 Java throws 优势
使用 throws 声明受查异常后,使得受查异常可以在调用堆栈中进行(传递)转发。它向处理该异常的方法提供异常信息。
8.4 Java throws 示例
下面的示例描述了受查异常可以通过throws
关键字进行传递:
import java.io.IOException;
class Testthrows1{
void m() throws IOException{
throw new IOException("设备异常"); // 受查异常
}
void n()throws IOException{
m();
}
void p(){
try{
n();
}
catch(Exception e){
System.out.println("异常处理器");
}
}
public static void main(String args[]){
Testthrows1 obj=new Testthrows1();
obj.p();
System.out.println("正常流...");
}
}
运行结果:
规则:如果你正在调用一个声明了异常的方法,则必须捕获或声明异常。
现在有两种情况:
- 情况1:你遇到了一个异常,使用 try-catch 处理了异常。
- 情况2:你声明了异常,使用方法指定抛出。
1) 情况1:处理了异常
- 在这种情况下,如果你处理了异常,则不管程序是否出现了异常,程序都将继续执行。
import java.io.*;
class M{
void method() throws IOException{
throw new IOException("设备异常");
}
}
public class Testthrows2{
public static void main(String args[]){
try{
M m = new M();
m.method();
}
catch(Exception e){
System.out.println("异常处理器");
}
System.out.println("正常流...");
}
}
运行结果:
2) 情况2:声明了异常
- A)如果声明了异常,但代码未出现异常,程序将正常执行。
- B)如果声明了异常且发生了异常,则在运行时抛出异常,因为程序会抛出不处理的异常。
A)声明了异常但未发生异常:
import java.io.*;
class M{
void method()throws IOException{
System.out.println("执行设备操作");
}
}
class Testthrows3{
public static void main(String args[])throws IOException{
// 声明了异常
M m=new M();
m.method();
System.out.println("正常流...");
}
}
运行结果:
B)声明了异常且发生了异常:
import java.io.*;
class M{
void method()throws IOException{
throw new IOException("设备错误");
}
}
class Testthrows4{
public static void main(String args[])throws IOException{
// 声明了异常
M m=new M();
m.method();
System.out.println("正常流...");
}
}
运行结果:
8.5 throw 与 throws 区别
No. | throw | throws |
---|---|---|
1) | Java throw 关键字用于显示的抛出异常 | Java throws 关键字用于声明一个异常 |
2) | 受查异常不能只使用 throw 进行传递 | 受查异常可以通过 throws 进行传递 |
3) | Throw 后面跟着一个异常实例 | Throws 后面跟着一个异常类 |
4) | 在方法中使用 Throw | Throws 与方法签名一起使用 |
5) | 你不能抛出多个异常 | 你可以声明多个异常,例如public void method() throws IOException,SQLException |
1)Java throw 示例:
void m(){
throw new ArithmeticException("sorry");
}
2)Java throws 示例:
void m()throws ArithmeticException{
// method code
}
3)Java throw 和 throws 示例:
void m()throws ArithmeticException{
throw new ArithmeticException("sorry");
}
10. 异常处理方法的重写
关于重写异常处理方法的规则如下:
- 超类方法没有声明异常:
如果超类方法没有声明异常,则子类重写方法不能声明受查异常,但可以声明非受查异常。 - 超类方法声明了异常:
如果超类方法声明了异常,则子类重写方法可以声明与超类方法相同的异常,也可以不声明异常。若父类方法声明父类异常,子类重写方法声明子类异常也可以,反之不可以。
1)如果超类方法没有声明异常
超类方法未声明异常,子类重写方法声明受查异常的示例:
import java.io.*;
class Parent{
void msg(){
System.out.println("parent");
}
}
class TestExceptionChild extends Parent{
void msg() throws IOException{
System.out.println("Child");
}
public static void main(String args[]){
Parent p = new TestExceptionChild();
p.msg();
}
}
编译报错
超类方法未声明异常,子类重写方法声明非受查异常的示例:
import java.io.*;
class Parent{
void msg(){
System.out.println("parent");
}
}
class TestExceptionChild1 extends Parent{
void msg() throws ArithmeticException{
System.out.println("child");
}
public static void main(String args[]){
Parent p = new TestExceptionChild1();
p.msg();
}
}
运行结果:
2)如果超类方法声明了异常
A)超类方法声明了异常,子类重写方法声明不相同父类异常的示例:
import java.io.*;
class Parent{
// 声明了子类异常
void msg() throws ArithmeticException{
System.out.println("parent");
}
}
class TestExceptionChild2 extends Parent{
// 声明了父类异常
void msg() throws Exception{
System.out.println("child");
}
public static void main(String args[]){
Parent p = new TestExceptionChild2();
try{
p.msg();
}
catch(Exception e){
}
}
}
运行结果:
编译报错
B)超类方法声明了异常,子类重写方法声明相同异常的示例:
import java.io.*;
class Parent{
void msg()throws Exception{
System.out.println("parent");
}
}
class TestExceptionChild3 extends Parent{
void msg()throws Exception{
System.out.println("child");
}
public static void main(String args[]){
Parent p=new TestExceptionChild3();
try{
p.msg();
}
catch(Exception e){
}
}
}
运行结果:
C)超类方法声明了异常,子类重写方法声明不相同子类异常的示例:
import java.io.*;
class Parent{
// 声明了父类异常
void msg()throws Exception{
System.out.println("parent");
}
}
class TestExceptionChild4 extends Parent{
// 声明了子类异常
void msg()throws ArithmeticException{
System.out.println("child");
}
public static void main(String args[]){
Parent p=new TestExceptionChild4();
try{
p.msg();
}
catch(Exception e){
}
}
}
运行结果:
D)超类方法声明了异常,子类重写方法未声明异常的示例:
import java.io.*;
class Parent{
void msg()throws Exception{
System.out.println("parent");
}
}
class TestExceptionChild5 extends Parent{
void msg(){
System.out.println("child");
}
public static void main(String args[]){
Parent p=new TestExceptionChild5();
try{
p.msg();
}
catch(Exception e){
}
}
}
运行结果:
注意点:
- 不要在fianlly中使用return。
- 不要在finally中抛出异常。
- 减轻finally的任务,不要在finally中做一些其它的事情,finally块仅仅用来释放资源是最合适的。
- 将尽量将所有的return写在函数的最后面,而不是try ... catch ... finally中。
参考:https://www.cnblogs.com/lulipro/p/7504267.html
参考:https://www.cnblogs.com/nwgdk/p/8862353.html
《Java基础知识》Java异常处理详解的更多相关文章
- Java基础-面向接口编程-JDBC详解
Java基础-面向接口编程-JDBC详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.JDBC概念和数据库驱动程序 JDBC(Java Data Base Connectiv ...
- java基础(3)--详解String
java基础(3)--详解String 其实与八大基本数据类型一样,String也是我们日常中使用非常频繁的对象,但知其然更要知其所以然,现在就去阅读源码深入了解一下String类对象,并解决一些我由 ...
- Linux基础知识之挂载详解(mount,umount及开机自动挂载)
Linux基础知识之挂载详解(mount,umount及开机自动挂载) 转载自:http://www.linuxidc.com/Linux/2016-08/134666.htm 挂载概念简述: 根文件 ...
- JAVA基础知识|java虚拟机(JVM)
一.JVM简介 java语言是跨平台的,兼容各种操作系统.实现跨平台的基石就是虚拟机(JVM),虚拟机不是跨平台的,所以不同的操作系统需要安装不同的jdk版本(jre=jvm+类库:jdk=jre+开 ...
- java基础知识——Java的定义,特点和技术平台
(作者声明:对于Java编程语言,很多人只知道怎么用,却对其了解甚少.我也是其中一员.所以菜鸟的我,去查询了教科书以及大神的总结,主要参考了<Java核心技术>这本神作.现在分享给大家!) ...
- [java基础知识]java安装步骤
jre: java运行环境. jre = java虚拟机 + 核心类库(辅助java虚拟机运行的文件).如果只是运行java程序,只需要安装jre. jdk: java开发工具集 jd ...
- 计算机基础知识和tcp详解
计算机基础知识 作为应用软件开发程序员是写应用软件的,而应用软件必须应用在操作系统之上,调用操作系统接口,由操作系统控制硬件 比如客户端软件想要基于网络发送一条消息给服务端软件,流程是: 1.客户端软 ...
- OpenStack基础知识-tox的详解介绍
1.tox简介 tox是通用的虚拟环境管理和测试命令行工具.tox能够让我们在同一个Host上自定义出多套相互独立且隔离的python环境,每套虚拟环境中可能使用了不同的 Python 拦截器/环境变 ...
- java线程基础知识----SecurityManager类详解
在查看java Thread源码的时候发现一个类----securityManager,虽然很早就知道存在这样一个类但是都没有深究,今天查看了它的api和源码,发现这个类功能强大,可以做很多权限控制策 ...
- Java基础(55):Exception类详解(转)
Java中的异常 Exception java.lang.Exception类是Java中所有异常的直接或间接父类.即Exception类是所有异常的根类. 比如程序: public class Ex ...
随机推荐
- 《Java基础教程》第一章学习笔记
Java 是什么呀! 计算机语言总的来说分成机器语言,汇编语言,高级语言.其中Java一种高级计算机语言,它是一种可以编写跨平台应用软件,完全面向对象的程序设计语言. Java划分为三个技术平台,Ja ...
- linux运维与实践
1.容器云计算节点负载值高,通过top可以看到Load Average:70.1 71.3 70.8,虚拟机有8个cpu: cpu使用率高导致(R状态)? 同时在top中观察一段时间,消耗cpu最 ...
- Vue——watch监听对象,监听嵌套多次的对象属性
首先是watch 然后是methods
- numpy和matplotlib下载中出现的问题
在安装numpy的时候遇到如下所示的错误: 经过几个小时的查找,最终发现是pygame的路径不对导致.将pygame的具体路径加上后,问题解决.实施如下:得出一个结论:路径很重要,千万得小心哦. 报错 ...
- 人生若只如初见---Spring概述以及环境的搭建
Spring 是什么 Spring是由Apache开发的一种轻量型Java框架,能够更加便捷使用JavaBean(之前只有EJB才能实现) Spring的主要优势:分层架构: DAO层:(Data A ...
- React躬行记(14)——测试框架
测试不仅可以发现和预防问题,还能降低风险.减少企业损失.在React中,涌现了多种测试框架,本节会对其中的Jest和Enzyme做详细的讲解. 一.Jest Jest是由Facebook开源的一个测试 ...
- Erlang/Elixir精选-第1期
第1期(20191202) 文章 A short guide to the structure and internals of the Erlang distributed messaging fa ...
- Logistic回归算法梯度公式的推导
最近学习Logistic回归算法,在网上看了许多博文,笔者觉得这篇文章http://blog.kamidox.com/logistic-regression.html写得最好.但其中有个关键问题没有讲 ...
- Ansible Playbooks 介绍 和 使用 一
目录 Ansible Playbooks Playbooks 组成部分: YAML 介绍 YAML 语法 Ansible 基础元素 变量 facts registre 通过命令传递变量 通过roles ...
- Scrapy爬虫及案例剖析
由于互联网的极速发展,所有现在的信息处于大量堆积的状态,我们既要向外界获取大量数据,又要在大量数据中过滤无用的数据.针对我们有益的数据需要我们进行指定抓取,从而出现了现在的爬虫技术,通过爬虫技术我们可 ...