文章目录


Java中的异常(Exception)

一、异常的基本概念

1.什么是异常?

  • 第一,异常模拟的是我们日常生活中"不正常"的事件
  • 第二,java中采用"类"去模拟异常
  • 第三,异常类是可以创建对象的:NullPointerException e = 0x1234;
  • e是引用类型,e中保存的内存地址指向堆内存中的"对象"
  • 这个对象一定是NullPointerException类型的
  • 这个对象表示真实存在的异常事件
  • NullPointerException是一类异常

2.异常机制的作用

  • JAVA语言为我们提供了一种完善的异常处理机制
  • 作用是:程序发生异常事件之后,为我们输出详细的信息,可以通过这些信息对程序进行处理,使程序更加完善
public class ExceptionTest01 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub

int a = 10;
int b = 0;
int c = a/b;
System.out.println(c);

System.out.println("hello world!");

/* 以上程序出现异常:
* Exception in thread "main" java.lang.ArithmeticException: / by zero
at 异常的基本概念.ExceptionTest01.main(ExceptionTest01.java:28)
* JVM向控制台输出信息,其本质:
程序执行过程中发生了算数异常这个事件,JVM创建了一个ArithmeticException类型的对象
并且该对象中包含了详细的异常信息,还将这个对象中的信息输出到了控制台
* 异常未被处理时下面的代码不会继续执行,直接退出
}
}

结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at 异常的基本概念.ExceptionTest01.main(ExceptionTest01.java:31)

​​


二、异常的主要分类

2.1 异常的分类

异常主要分为:错误​一般性异常(受控异常)​​运行期异常(非受控异常)​

  • 错误如果应用程序出现了Error,那么将无法恢复,只能重新启动应用程序,最典型的Error异常是 — OutOfMemoryError
  • 受控异常出现了这种异常必须显示的处理,不显示处理java程序将无法编译通过
  • 非受控异常此种异常可以不用显示的处理

【Java】异常(Exception)_子类

  • 异常体系结构图
  • 【Java】异常(Exception)_异常_02

2.2 常见的异常

【Java】异常(Exception)_异常_03

​​


三、异常的捕获和处理

3.1 处理异常的两种方式

  • 声明抛出 throws
  • 捕捉 try…catch…
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ExceptionTest03 {

/**
* @param args
* @throws FileNotFoundException
*/
/* 通过抛出异常解决 */
public static void main(String[] args) throws FileNotFoundException {
// TODO Auto-generated method stub
//创建文件输入流,读取文件

FileInputStream fis = new FileInputStream("c:/ab.text");

/* 通过try/catch方法解决
try {
FileInputStream fis = new FileInputStream("c:/ab.text");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
*/
}
}

结果:
Exception in thread "main" java.io.FileNotFoundException: c:\ab.text (系统找不到指定的文件。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:106)
at java.io.FileInputStream.<init>(FileInputStream.java:66)
at 异常的捕获和处理.ExceptionTest03.main(ExceptionTest03.java:24)

​​


3.2 方式一:throws 方法声明异常

通过throws继续声明异常。如果调用者不知道如何处理该异常,可以继续通过throws声明该异常,让上一级调用者处理异常。main方法声明的异常将由Java虚拟机来处理。

import java.io.*;
public class ExceptionTest04 {

public static void m1() throws FileNotFoundException{
m2();
}

public static void m2() throws FileNotFoundException{
m3();
}

public static void m3() throws FileNotFoundException{
new FileInputStream("fdafds"); //FileInputStream构造方法声明位置上使用向上throws

}
/**
* @param args
* @throws FileNotFoundException
*/
public static void main(String[] args) throws FileNotFoundException {
// TODO Auto-generated method stub
m1();

/*使用throws处理异常不是真正的处理异常,而是推卸责任。、
*上面的m1方法如果出现异常,因为采用的是上抛,给了JVM,JVM遇到异常就会退出,不会继续执行下面代码
*在运行过程中发生了FileNotFoundException类型的异常
*JVM为我们创建了一个FileNotFoundException类型的对象
*该对象携带以下信息
*JVM负责将该对象的信息打印到控制台
*并且停止了程序的运行
*/

System.out.println("hello world");
}
}
结果:
Exception in thread "main" java.io.FileNotFoundException: fdafds (系统找不到指定的文件。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:106)
at java.io.FileInputStream.<init>(FileInputStream.java:66)
at 异常的捕获和处理.ExceptionTest04.m3(ExceptionTest04.java:18)
at 异常的捕获和处理.ExceptionTest04.m2(ExceptionTest04.java:14)
at 异常的捕获和处理.ExceptionTest04.m1(ExceptionTest04.java:10)
at 异常的捕获和处理.ExceptionTest04.main(ExceptionTest04.java:30)

3.2.1 抛出异常—throw(自定义异常)

//顾客相关业务
public class CustomerService {

// 注册方法
public void register(String name) throws IllegalNameExceptoin {
// 完成注册
if(name.length()<6){
//异常
IllegalNameExceptoin i = new IllegalNameExceptoin("用户名长度不小于六位数");

//手动抛异常
throw new IllegalNameExceptoin("用户名长度不小于六位数");
}
System.out.println("注册成功!");
}
}
/*
* 自定义无效名字异常
* 1.编译时异常
* 2.运行时异常
*/
public class IllegalNameExceptoin extends Exception { //编译时异常
//public class IllegalNameExceptoin extends RunTimeException { 运行时异常

//定义异常一般提供两个构造方法
public IllegalNameExceptoin() {
super();
// TODO Auto-generated constructor stub
}
public IllegalNameExceptoin(String msg) {
super(msg);
// TODO Auto-generated constructor stub
}
*
* 模拟注册
*
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//假如用户提供的用户名
String name = "masd"; //masdasd
CustomerService cs = new CustomerService();
try {
cs.register(name);
} catch (IllegalNameExceptoin e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果:
自定义异常.IllegalNameExceptoin: 用户名长度不小于六位数
at 自定义异常.CustomerService.register(CustomerService.java:14)
at 自定义异常.Test.main(Test.java:17)
//注册成功!

​​


3.2.2 对比 throws 和 throw

  • 作用不同:throw 用于程序员自行产生并抛出异常,throws 用于声明在该方法内部抛出了异常
  • 使用的位置不同:throw 用于方法体内部,可以作为单独语句使用。throws 必须跟在方法参数列表的后面,不能单独使用。
  • 内容不同:throw 抛出一个异常对象,而且只能是一个。throws 后面跟异常类,而且可以跟多个异常类。

​​


3.3 方式二:捕捉(try…catch…final…)

  • 语法结构:
try {
// 可能出现异常的代码
} catch (异常类型1 变量) {
// 处理异常的代码
} catch (异常类型2 变量) {
// 处理异常的代码
} ...
  • catch语句块可以有多个
  • try…catch…语句块只能执行一个catch语句
  • try…catch…语句会出现三种情况:
  • 若try块中的语句正常执行完毕,不会发生异常,那么catch块中的语句将被忽略
  • 若try块中的语句执行中出现异常,并且这个异常与catch中的异常类型相匹配(catch所处理的异常类型与所生成的异常类型完全一致或是它的父类),那么在try块中的其余代码将被忽略,而相应的catch块将被执行。
  • 若try语句块在执行中遇到异常,而抛出的异常在catch块中没有得到声明,那么程序立刻退出。
  • 在使用多个catch语句时,catch语句的排列顺序应该是先特殊后一般,也就是子类在父类前面(如果子类在父类后面,子类永远不会到达)

例:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/* 以下程序无法编译通过:
try {
FileInputStream fis = new FileInputStream("c:/ab.text");
fis.read(); //这个方法里又抛出了IOException异常
} catch (FileNotFoundException e) {
e.printStackTrace();
}
catch抓取异常不完全
*/

try {
FileInputStream fis = new FileInputStream("c:/ab.text");
fis.read();
} catch (IOException e) { //这里已经捕获过异常了
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//catch可以写多个,但是必须从上到下,从小到大
//捕捉在使用多个catch语句时,catch语句的排列顺序应该是先特殊后一般,也就是子类在父类前面(如果子类在父类后面,子类永远不会到达)

public class ExceptionTest05 {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//try...catch...语句块只能执行一个catch语句
try {
//程序到此处发生了FileNotFoundException异常
//JVM自动创建了一个FileNotFoundException类型的对象,并将该对象的内存地址赋值给了catch语句中的变量e

FileInputStream fis = new FileInputStream("c:/ab.text");
System.out.println("000000");
fis.read();
} catch (FileNotFoundException e) {
System.out.println("读取的文件不存在");
//FileNotFoundException将Object类的toString方法重写了
System.out.println(e); //java.io.FileNotFoundException: c:\ab.text (系统找不到指定的文件。)
} catch (IOException e) {
System.out.println("其他IO异常");
}
System.out.println("ABC");
}
}

结果:
读取的文件不存在
java.io.FileNotFoundException: c:\ab.text (系统找不到指定的文件。)

​​


四、getMessage() 与 printStackTrace()

  • printStackTrace()方法:打印出所有与之相关的异常出处,换句话说就是不仅打印出异常名字还是显示出位置。便于程序的调试。
  • getMessage()方法:打印出具体异常的名字。不显示具体位置,不方便调试程序。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ExceptionTest06 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub

try {
FileInputStream fis = new FileInputStream("c:/ab.text");
//JVM为我们执行了以下代码
//FileNotFoundException e = new FileNotFoundException("c:\ab.text (系统找不到指定的文件。)");
} catch (FileNotFoundException e) {
//打印异常堆栈信息
//一般都会使用该方式去调试程序
e.printStackTrace();
/*
java.io.FileNotFoundException: c:\ab.text (系统找不到指定的文件。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:106)
at java.io.FileInputStream.<init>(FileInputStream.java:66)
at 异常的捕获和处理.ExceptionTest06.main(ExceptionTest06.java:15)

*/
String msg = e.getMessage();
System.out.println(msg); //c:\ab.text (系统找不到指定的文件。)
}
}
}

结果:
java.io.FileNotFoundException: c:\ab.text (系统找不到指定的文件。)
c:\ab.text (系统找不到指定的文件。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:106)
at java.io.FileInputStream.<init>(FileInputStream.java:66)
at 异常的捕获和处理.ExceptionTest06.main(ExceptionTest06.java:17)

​​


五、finally语句

  • 可以直接与try语句块连用
try...finally...
  • 在finally中的代码一定会执行
  • 即使在try块、catch块中出现了return语句,final块中的语句也会执行,发生异常时的执行顺序是:try块、catch块 return 语句之前的语句;finally块中的语句;return语句结束。
  • finally块中语句唯一不执行的情况:在异常代码中执行了 System.exit(1),将退出Java虚拟机
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ExceptionTest07 {

/**
* @param args
* @throws FileNotFoundException
*/
public static void main(String[] args) throws FileNotFoundException {
// TODO Auto-generated method stub
try{
System.out.println("ABC");
return;
}finally{
System.out.print("test");
}

//ABC test
try{
FileInputStream fis = new FileInputStream("c:/ab.text");
System.out.println("ABC");
}finally{
System.out.println("test");
}

// test
// Exception in thread "main" java.io.FileNotFoundException: c:\ab.text (系统找不到指定的文件。)
// at java.io.FileInputStream.open(Native Method)
// at java.io.FileInputStream.<init>(FileInputStream.java:106)
// at java.io.FileInputStream.<init>(FileInputStream.java:66)
// at 异常的捕获和处理.ExceptionTest07.main(ExceptionTest07.java:32)

try{
System.exit(0);
}finally{
System.out.println("test");
}
//只要在执行finally语句之前退出了JVM,则finally语句块就不会执行
}
}

​​


5.1 深入 finally 语句块

  • finally的执行原理
public class ExceptionTest08 {

public static int m1(){
int i = 10;
try{
return i; //10
}finally{
i++;
System.out.println(i); //11
}
}

/* 以上代码的原理:
int i = 10;
try{
int temp = i; 在运行时内部定义了变量temp
return temp ;
}finally{
i++;
System.out.println(i); //11
}
*/
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub

int i = m1();
System.out.println(i); //10

  • 通常在程序中为了保证某资源一定会释放,一般在 finally语句块中释放资源
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionTest08 {
/**
* @param args
*/
public static void main(String[]) {
// TODO Auto-generated method stub

FileInputStream fis = null;
try {
fis = new FileInputStream("c:/ab.text");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally{
//为了保证资源一定会释放
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

​​


六、final、finalize、finally区别

final:修饰变量、修饰类
final修饰的类无法被继承
final修饰的方法无法被覆盖
final修饰的变量无法被更改
final修饰的成员变量需要手动赋值
final与static连用的变量称之为常量,且常量名字母全部大写

finally:是异常处理机制中的finally语句处理块

finalize:Object类里的一个方法,只是一个方法名
垃圾回收器在回收垃圾数据之前会自动调用

​​


七、方法覆盖与异常

  • 重写的方法不能比被重写的方法抛出更宽泛的异常
  • 子类不能抛出比父类更多的异常
import java.io.*;

public class ExceptionTest10 {
/* 1.子类不能抛出比父类更多的异常
class a{
public void m1(){}
}

class b extends a {
public void m1() throws Exception{}
}
*/
class a{
//public void m1() throws FileNotFoundException {}
public void m1() throws IOException {}
}

class b extends a {
//public void m1() throws IOException {}
public void m1() throws FileNotFoundException {}
}
}

【Java】异常(Exception)_异常_04


【Java】异常(Exception)_子类_05

​​