文章目录
异常机制
一. 概念
异常是Java中提供的一种识别及响应错误情况的一致性机制。有效地异常处理能使程序更加健壮、易于调试。
异常 : 就是错误的另外一种说法
异常发生的原因有很多,比如:
- 用户输入了非法数据
- 要打开的文件不存在
- 网络通信时连接中断
- JVM内存溢出
- 这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
异常还有 Robust 性 : 就是系统的健壮性
二. 系统异常分类
- 异常分两种 : 编译时 和 运行时 ,
在执行javac命令出错,是编译时
执行java命令出错,是运行时
在eclipse中 没有运行 就报错,就是编译时
- 在java中有一个专门模拟所有异常和错误的类(Throwable) 所有的异常类都必须继承这个类
- 异常机制的处理形式
- try…catch… : 一般用在客户端
- throws : 一般用在类库端
- throw : 异常源点,起点
不同的异常机制的选择 :
- 有些错误我们不想处理,或者没有办法处理(这个通常就是在类库中),这个时候就把错误信息交给调用人员,这种情况 直接使用throws
- 我们能够处理的,就try…catch…(一般在客户端,main)
finally语句块 :
必须执行的语句块,有必须要执行的代码,可以放到finally中
- Throwable异常类的祖类
Exception : 直接子类都是编译时异常,只有一个RunTimeException是运行时异常
Error : 系统内部错误,比如栈内存溢出,虚拟机错误等,这种情况JVM会选择终止程序,不需要我们做操作
语法 :
try{
高风险代码段;
}catch(异常类型 变量){
错误的处理机制
}
实例 : 除法出错异常
public class Exception_00 {
public static void main(String[] args) {
int a = 10;
int b = 0;
// java.lang.ArithmeticException: / by zero
System.out.println(a / b);
System.out.println("-----");
}
}
我们在实际写程序的过程中,因为 try … catch 会终止生命周期,所以不应该叫错误,而是换一种方式提示
使我们自己的异常,成为正常程序中的一部分
public class Exception_01 {
public static void main(String[] args) {
int a = 10;
int b = 0;
if (b == 0) {
System.out.println("除数不能为0");
} else {
int c = a / b;
System.out.println(c);
}
// try 捕获异常,catch 捕获指定的异常并进行处理
try {
int c = a / b;
System.out.println(c);
} catch (Exception e) {
e.printStackTrace();
System.out.println("除数不能为 0 ");
}
System.out.println("-----");
// 除数不能为0
// java.lang.ArithmeticException: / by zero
// 除数不能为 0 at exception.Exception_01.main(Exception_01.java:28)
//
// -----
}
}
常用的几种方式 :
-
用异常类和异常类的父类都可以捕捉
-
被捕捉之后的处理措施
1 打印追踪栈帧
printStackTrace();
2 错误栈帧的内存地址
getStackTrace();
3> 错提示信息
getMessage();
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("aaa.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("---");
System.out.println(e.getStackTrace());
System.out.println("---");
System.out.println(e.getMessage());
}
}
三. Error
1. 概念
- 系统内部错误,这类错误由系统进行处理,程序本身无需捕获处理。
比如:OOM(内存溢出错误)、VirtualMachineError(虚拟机错误)、StackOverflowError(堆栈溢出错误)等,一般发生这种情况,JVM会选择终止程序。
2. 示例
堆栈溢出错误
public class TestError {
public static void recursionMethod() {
recursionMethod();// 无限递归下去
}
public static void main(String[] args) {
recursionMethod();
}
}
报错信息:
Exception in thread "main" java.lang.StackOverflowError
at com.TestError.recursionMethod(TestError.java:5)
at com.TestError.recursionMethod(TestError.java:5)
at com.TestError.recursionMethod(TestError.java:5)
at com.TestError.recursionMethod(TestError.java:5)
at com.TestError.recursionMethod(TestError.java:5)
at com.TestError.recursionMethod(TestError.java:5)
... ...
四. Exception
1. 介绍
**Exception是所有异常类的父类。**分为非RuntimeException和RuntimeException 。
-
非RuntimeException
指程序编译时需要捕获或处理的异常,如IOException、自定义异常等。属于checked异常。 -
RuntimeException
指程序编译时不需要捕获或处理的异常,如:NullPointerException等。属于unchecked异常。一般是由程序员粗心导致的。如空指针异常、数组越界、类型转换异常等。
2. 示例
空指针异常
public class TestException {
private static int[] arr;
public static void main(String[] args) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
报错信息:
Exception in thread "main" java.lang.NullPointerException
at com.TestException.main(TestException.java:7)
3. 常用方法
Exception类和其他普通类一样,有自己的属性和方法,为我们提供异常的相关信息。常用的方法有:
方法 | 说明 |
---|---|
public String getMessage() | 返回关于发生的异常的详细信息。这个消息在Throwable类的构造函数中初始化了。 |
public void printStackTrace() | 打印toString()结果和栈层次到System.err,即错误输出流。 |
【示例】
public class TestException {
public static void main(String[] args) {
Exception exp = new Exception("异常方法演示");
//打印异常信息
System.out.println("exp.getMessage()=" + exp.getMessage());
//跟踪异常栈信息
exp.printStackTrace();
}
}
4. 异常捕获与处理
程序在执行时如果发生异常,会自动的生成一个异常类对象,并提交给JAVA运行环境,这个过程称为抛出异常。程序也可以自行抛出异常。
当出JAVA运行环境接收到异常对象时,会寻找能处理这个异常的代码并按程序进行相关处理,这个过程称为捕获异常。
4.1 throws
【语法格式】
修饰符 返回类型 方法名(参数列表) throws 异常类名列表
一般用于类库段,并不能解决问题,依然会终止程序生命周期执行
public class Exception_03 {
public static void main(String[] args) throws FileNotFoundException {
m1();
System.out.println("----");
}
public static void m1() throws FileNotFoundException {
m2();
}
public static void m2() throws FileNotFoundException {
m3();
}
@SuppressWarnings("resource")
public static void m3() throws FileNotFoundException {
new FileInputStream("23");
}
}
如果上家提醒了你,要么你就使用try…catch…解决,要么你就提醒下家
throws 可以抛出多个异常,用 逗号 隔开
并且throws的时候,不需要考虑继承关系和先后顺序,因为只是个提醒
public class Exception_06 {
@SuppressWarnings("resource")
public static void main(String[] args) throws FileNotFoundException, IOException {
new FileInputStream("");
}
}
4.2 throw
throw的作用是抛出异常,抛出一个异常类的实例化对象。
【语法格式】
throw new XXXException();
【示例】
public class ExceptionDemo {
public static void main(String[] args) throws Exception {
Exception exp = new Exception("异常方法演示");
throw exp;
}
}
【总结】
- 这种抛出异常的方式一般在满足某条件时主动抛出,即满足用户定义的逻辑错误发生时执行。
- 含有throw语句的方法,或者调用其他类的有异常抛出的方法时,必须在定义方法时,在方法头中增加throws异常类名列表。
- 使用throws关键字声明的方法表示此方法不处理异常,而交给方法调用处进行处理。
4.3 try…catch
【语法格式】
try{
有潜在异常抛出的语句组
}catch(异常类名 异常形参){
异常处理语句组
}catch(异常类名 异常形参){
异常处理语句组
}catch(异常类名 异常形参){
异常处理语句组
}catch(异常类名 异常形参){
异常处理语句组
}finally{
语句组
}
其中:
- try用来捕获语句组中的异常
- catch用来处理异常可以有一个或多个,而且至少要有一个catch语句或finally语句
- finally中的语句组无论是否有异常都会执行
常用捕捉异常方式 :
- try…catch try…finally
- try…catch…finally
- try…catch1…catch2…finally(体现异常出现的大小顺序)
说明: 多重catch处理异常,大异常类在后,小异常类在前。
- catch 语句可以根据代码的异常写多个
- 从上往下,必须是从子类到父类,或者没有继承关系,要不然会因为多态的原因,把子类异常捕捉
- 虽然写了多个catch,但是只会有一个 执行,因为最先出错的语句之后的代码不执行,直接执行catch
public class Exception_04 {
@SuppressWarnings("resource")
public static void main(String[] args) {
try {
// FileNotFoundException
new FileInputStream("");
String s = null;
s.equals("");
} catch (FileNotFoundException e) {
System.out.println("找不到指定文件");
} catch (NullPointerException e) {
System.out.println("不能使用 null 调用成员属性");
} catch (Exception e) {
System.out.println("其他异常");
}
}
}
java1.7开始,有一个新的写法,一次catch 多个异常,但是 多个异常之间不能有继承关系,如果有就直接写父类
catch(异常类型1 | 异常类型2| 异常类型3|… 变量)
可以对多个异常,做相同的处理操作
实例 :
public class Exception_05 {
public static void main(String[] args) {
try {
// FileNotFoundException
new FileInputStream("");
String s = null;
// NullPotionException
s.equals("");
} catch (FileNotFoundException | NullPointerException e) {
System.out.println("1111");
// 空指针异常和找不到文件异常都会在这里执行
} catch (Exception e) {
System.out.println("2222");
}
}
}
4.4 Finally
finally : 必须执行的语句块
有时候我们需要在程序出错的情况下,有些代码必须执行,比如 开启的资源
-
finally语句块可以直接和try使用,也可以和try…catch…使用,但是不能单独使用
-
finally语句块一定会执行,有两种情况不会执行
1. 编译时异常,编译有错,压根不能运行,更别提代码了
2. System.exit() : 运行起来之后,这个finally唯一一种不执行的情况,这个等于直接关闭虚拟机
不管程序是否出错,finally都会执行
public class Exception_07 {
public static void main(String[] args) {
try {
int a = 10;
int b = 0;
// 执行到这里,JVM直接关闭,参数 0 表示正常关闭,1 表示非正常
// System.exit(1);
int c = a / b;
} finally {
System.out.println("123456");
}
try {
} catch (Exception e) {
// TODO: handle exception
} finally {
}
}
}
来看一种 finally 的特殊情况:
因为 finally语句块肯定会执行,当在finally语句块之外遇到return语句时,会执行 return 后的语句,但并不会返回值,因为 finally中还有return语句
public class Exception_08 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(m1());
}
public static int m1() {
int i = 10;
try {
// new FileInoutStream(""):
System.out.println("---");
// 这里的 return 不会执行,但是 i++ 会执行,因为 finally 比这个 return 优先级高
// 所以最终返回 11
return i++;
} catch (Exception e) {
return 1;
} finally {
System.out.println("===");
return i;
}
// return 1;
}
}
来看一下异常的继承机制 :
首先不能比原异常有更宽泛的异常:
import java.io.IOException;
public class Exception_09 {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
class SupClass {
public void m1() throws IOException {
}
}
class SubClass extends SupClass {
@Override
// 不能有更宽泛的异常
// public void m1() throws Exception{
//
// }
// 原异常的子类可以
// public void m1() throws FileNotFoundException{
//
// }
// 原异常也可以
public void m1() throws IOException {
// TODO Auto-generated method stub
super.m1();
}
}
4.5 jdk1.7新特性
- 多个异常可以在一个 catch 中同时捕捉,使用 | 隔开
- 自动关闭资源
语法 :
try (开启资源语句){
操作;
} catch (异常类型 变量){
处理代码;
}
public class Exception_10 {
public static void main(String[] args) {
// 传统关闭资源写法
FileInputStream fis = null;
try {
fis = new FileInputStream("D:/123.txt");
System.out.println(fis.read());
fis.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis == null) {
fis.close();
}
} catch (Exception e2) {
// TODO: handle exception
}
}
// 新特性写法,自动关闭资源
try (FileInputStream fis1 = new FileInputStream("D:/123.txt");) {
// TODO 操作
System.out.println(fis1.read() + "---");
} catch (Exception e) {
// TODO: handle exception
}
}
}
5自定义异常
系统定义的异常主要用来处理系统可以预见的常见运行错误,对于某个应用所特有的运行错误,需要编程人员根据特殊逻辑来创建自己的异常类。
【语法格式】
public class 自定义异常类名 extends Exception{
…
}
异常也是个类,所以我们也可以自己自定义一个异常
每个项目都会有一个自定义异常,来完成自己的异常提醒
那么对于这个异常自己肯定要有个判断,是编译时异常 还是运行时异常
-
如果是运行时异常,一般继承RunTimeException
比如 : NullPointerException , 数组越界 , 类型转换异常等 一般是因为程序员粗心导致的错误 -
如果是编译时异常,一般继承Exception
比如 : IOException , FileNotFoundException 等,需要在编译阶段进行处理,否则就报错
我们写的自定义异常,一般是编译时异常
规定 :
- 必须继承一个已有的异常类,一般就是Exception/RunTimeExcetption
- 有一个公共的无参构造
- 有一个有参构造(字符串类型)
在构造方法中,使用super(…) 调用父类的构造方法,把String值进行传递
传入的String的值,就是错误信息
示例异常类 :
public class NameLengthException extends Exception {
public NameLengthException() {
}
public NameLengthException(String mgs) {
super(mgs);
}
}
服务器端 :
public class UserService {
public int m1(String name) throws NameLengthException {
if (name.length() < 6) {
System.out.println("用户名长度不能小于六位");
throw new NameLengthException("用户名长度不能小于六位");
}
System.out.println("登录成功");
return 1;
}
}
客户端 :
public class Client {
public static void main(String[] args) {
UserService userService = new UserService();
// boolean b = userService.m1("123");
// if (b) {
// // 注册成功
// } else {
// // 注册失败
// }
try {
userService.m1("13456");
} catch (NameLengthException e) {
// TODO: handle exception
e.getMessage();
e.printStackTrace();
}
}
}