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)受查异常

除了RuntimeExceptionError外,继承自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个关键字:

trycatchfinallythrowthrows

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异常处理详解的更多相关文章

  1. Java基础-面向接口编程-JDBC详解

    Java基础-面向接口编程-JDBC详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.JDBC概念和数据库驱动程序 JDBC(Java Data Base Connectiv ...

  2. java基础(3)--详解String

    java基础(3)--详解String 其实与八大基本数据类型一样,String也是我们日常中使用非常频繁的对象,但知其然更要知其所以然,现在就去阅读源码深入了解一下String类对象,并解决一些我由 ...

  3. Linux基础知识之挂载详解(mount,umount及开机自动挂载)

    Linux基础知识之挂载详解(mount,umount及开机自动挂载) 转载自:http://www.linuxidc.com/Linux/2016-08/134666.htm 挂载概念简述: 根文件 ...

  4. JAVA基础知识|java虚拟机(JVM)

    一.JVM简介 java语言是跨平台的,兼容各种操作系统.实现跨平台的基石就是虚拟机(JVM),虚拟机不是跨平台的,所以不同的操作系统需要安装不同的jdk版本(jre=jvm+类库:jdk=jre+开 ...

  5. java基础知识——Java的定义,特点和技术平台

    (作者声明:对于Java编程语言,很多人只知道怎么用,却对其了解甚少.我也是其中一员.所以菜鸟的我,去查询了教科书以及大神的总结,主要参考了<Java核心技术>这本神作.现在分享给大家!) ...

  6. [java基础知识]java安装步骤

    jre:  java运行环境.  jre =  java虚拟机 + 核心类库(辅助java虚拟机运行的文件).如果只是运行java程序,只需要安装jre.    jdk: java开发工具集   jd ...

  7. 计算机基础知识和tcp详解

    计算机基础知识 作为应用软件开发程序员是写应用软件的,而应用软件必须应用在操作系统之上,调用操作系统接口,由操作系统控制硬件 比如客户端软件想要基于网络发送一条消息给服务端软件,流程是: 1.客户端软 ...

  8. OpenStack基础知识-tox的详解介绍

    1.tox简介 tox是通用的虚拟环境管理和测试命令行工具.tox能够让我们在同一个Host上自定义出多套相互独立且隔离的python环境,每套虚拟环境中可能使用了不同的 Python 拦截器/环境变 ...

  9. java线程基础知识----SecurityManager类详解

    在查看java Thread源码的时候发现一个类----securityManager,虽然很早就知道存在这样一个类但是都没有深究,今天查看了它的api和源码,发现这个类功能强大,可以做很多权限控制策 ...

  10. Java基础(55):Exception类详解(转)

    Java中的异常 Exception java.lang.Exception类是Java中所有异常的直接或间接父类.即Exception类是所有异常的根类. 比如程序: public class Ex ...

随机推荐

  1. ArcGIS 切片与矢量图图层顺序问题

    在项目中有个需求:根据图层索引添加图层 看到这个需求一下子想到 map.addLayer(layer,index?) 接口 但是问题出现了,我切片图加载顺序在矢量图之后就不行! map = new M ...

  2. 投票通过,PHP 8 确认引入 Union Types 2.0

    关于是否要在 PHP 8 中引入 Union Types 的投票已于近日结束,投票结果显示有 61 名 PHP 开发组成员投了赞成票,5 名投了反对票. 还留意到鸟哥在投票中投了反对票~) 因此根据投 ...

  3. Python中lambda的使用,与它的三个好基友介绍!

    匿名函数lambda 除了def语句,python还提供了一种生成函数对象的表达式形式.由于它与LISP语言中的一个工具类似,所以称为lambda. 就像def一样,这个表达式创建了一个之后能够调用的 ...

  4. python3 之 判断闰年小实例

    一.方法1: while True: try: year = int(input('请输入一个年份:')) if (year % 4) == 0 and (year % 100) != 0 or (y ...

  5. python3 之 内置函数range()

    一.语法: range(stop) range(start,stop,step) start:计数从start开始,默认是从0开始.eg:range(5)等价于range(0,5) stop:计数到s ...

  6. ES6面向对象实现tab栏切换效果

    面向对象实现tab栏切换效果

  7. 环境配置——tornado项目准备工作

    新建tornado项目后,采用Pycharm作为开发工具,采用Xshell链接Ubuntu模拟服务端方便方便测试.项目编码前进行以下几个方面的配置. 1.Ubuntu配置 1.1安装ssh服务 sud ...

  8. MySQL(学生表、教师表、课程表、成绩表)多表查询

    1.表架构 student(sid,sname,sage,ssex) 学生表 course(cid,cname,tid) 课程表 sC(sid,cid,score) 成绩表 teacher(tid,t ...

  9. 深入浅出Spring(二)

    IoC概念 控制反转(Inversion of Control)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题. 它还有一个名字叫做依赖注入(Dependency Injection).Io ...

  10. CCNA 之 五 路由协议 一 静态路由

    静态路由 路由选择原理 什么是路由? 就如同去某一个地方,会有很多种路线,每一条路线经都可以称之为路由: 路由器中会维护一张路由表,每一个表项都是一条路由,也就是去往某个网络的路径,然后将对应的数据包 ...