文章目录
- 一、初始异常
- 二、异常的层次
-
- 2.1 异常的体系结构
- 2.2 异常的分类
- 三、异常的处理
-
- 3.1 异常的捕获
- 3.2 异常的抛出
- 3.3 finally的使用
- 3.4 JVM处理异常
- 四、自定义异常类
一、初始异常
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出 java.lang.ArithmeticException 的异常。
java中常见异常:
1.数组越界异常
public static void main(String[] args) {
int[] arr = {1,2,3,4};
System.out.println(arr[5]);
}
2.算术异常
public static void main(String[] args) {
System.out.println(1/0);
}
3.输入异常
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();//这里输入1.0
}
4.空指针异常
public static void main(String[] args) {
int[] arr = null;
System.out.println(arr.length);
}
java中不同类型的异常,都有对象类型的提示.
二、异常的层次
2.1 异常的体系结构
所有的异常类是从 java.lang.Exception 类继承的子类。
1. Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception
2. Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表:StackOverflowError和OutOfMemoryError,一旦发生回力乏术。
3. Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说的异常就是Exception
2.2 异常的分类
1.检查性异常也称为编译时异常(Checked Exception):最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
2.运行时异常也称为非受检查异常(Unchecked Exception): 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。比如:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。
三、异常的处理
在Java中,异常处理主要的5个关键字:throw、try、catch、final、throws。
3.1 异常的捕获
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。Catch 语句包含要捕获异常类型的声明,使用 try/catch 的语法如下:
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
具体实例:
public static void main(String[] args) {
try{
int[] arr = {1,2,3};
arr[3] = 1;
}catch(ArrayIndexOutOfBoundsException e) {
e.printStackTrace();//打印异常信息
System.out.println("数组越界异常!");
}
System.out.println("out");
}
我们可以发现系统处理完异常后,程序正常进行到结束.
- try块内抛出异常位置之后的代码将不会被执行
- 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM收到后中断程序----异常是按照类型来捕获的
多重捕获块:
public static void main(String[] args) {
try{
int arr[] = {0,1,2};
arr[3] = 0;
System.out.println(2/arr[0]);
}catch(ArrayIndexOutOfBoundsException e) {
e.printStackTrace();//打印异常信息
System.out.println("数组越界异常!");
}catch(ArithmeticException e) {
e.printStackTrace();//打印异常信息
System.out.println("算术异常!");
}
System.out.println("out");
}
try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获
捕获多个异常:
try{
}catch(ArrayIndexOutOfBoundsException | NullPointerException e) {
}
如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则编译报错.
public static void main(String[] args) {
try{
}catch(Exception e) {
}catch(ArrayIndexOutOfBoundsException e) {
}
}
也可以一个catch捕获,不建议,代码可阅读性差:
public static void main(String[] args) {
try{
}catch(Exception e) {
}
}
阅读时不知道这里的异常到底是什么异常.
3.2 异常的抛出
在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者,比如:参数检测。在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。具体语法如下:
throw关键字:
public static int fun(int[] arr,int index) {
if(index < 0 || index >= arr.length) {
throw new ArrayIndexOutOfBoundsException("数组越界!");
}
if(null == arr) {
throw new NullPointerException("数组为空!");
}
return arr[index];
}
public static void main(String[] args) {
int[] arr = {0,1,2};
System.out.println(fun(arr, 3));
}
我们可以自己指定异常信息.
- throw必须写在方法体内部
- 抛出的对象必须是Exception 或者 Exception 的子类对象
- 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
- 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
- 异常一旦抛出,其后的代码就不会执行
throws关键字:
下面方法的声明抛出一个 RemoteException 异常:
public void deposit(double amount) throws RemoteException
{
throw new RemoteException();
}
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。例如,下面的方法声明抛出 RemoteException 和 InsufficientFundsException:
public void withdraw(double amount) throws RemoteException,
InsufficientFundsException
{
}
- throws必须跟在方法的参数列表之后
- 声明的异常必须是 Exception 或者 Exception 的子类
- 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型
具有父子关系,直接声明父类即可。- 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出
3.3 finally的使用
在写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源:网络连接、数据库连接、IO流等,在程序正常或者异常退出时,必须要对资源进进行回收。另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来解决这个问题的。
public static void main(String[] args) {
try{
int[] arr = {1,2,3};
arr[3] = 1;
}catch(ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}finally {
System.out.println("finally已执行!");
}
}
相信大家有一个疑问,try-catch后面的代码会执行,为什么还要使用finally?
public static int get() {
Scanner scanner = null;
int n = 0;
try{
scanner = new Scanner(System.in);
n = scanner.nextInt();
return n;
}catch(InputMismatchException e) {
e.printStackTrace();
}finally {
System.out.println("finally!");
}
System.out.println("try-catch执行完毕!");
if(null != scanner) {
scanner.close();
}
return n;
}
public static void main(String[] args) {
int n = get();
}
当我们输入的值为整型类型时,就会出现类型泄露.
这里执行到第二步程序就终止了,没有对scanner进行释放,只有在finally中加上才能实现程序的安全性.
finally的危险性:
public static int func1() {
try{
return 1;
}finally {
return 2;
}
}
public static void main(String[] args) {
System.out.println(func1());
}
这里输出的是1还是2?
finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.
3.4 JVM处理异常
如果我们一直将异常向上抛,抛给主方法还没有处理,系统将会抛给JVM处理,异常终止程序.try中的异常如果没有catch捕捉处理也是一样的.
public static void func2() {
int[] arr = {1,2,3};
arr[3] = 1;
}
public static void main(String[] args) {
func2();
System.out.println("main未处理异常!");
}
1.程序先执行 try 中的代码
2.如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
3.如果找到匹配的异常类型, 就会执行 catch 中的代码
4.如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
5.无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
6.如果上层调用者也没有处理的了异常, 就继续向上传递.
7.一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.
四、自定义异常类
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点:
1.所有异常都必须是 Throwable 的子类。
2.如果希望写一个检查性异常类,则需要继承 Exception 类。
3.如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
可以像下面这样定义自己的异常类:
class MyException extends Exception{
}
实现一个登录功能,自定义异常类
public class NameException extends Exception{
public NameException(String message) {
super(message);
}
}
public class PassException extends Exception{
public PassException(String message) {
super(message);
}
}
1.自定义异常通常会继承自 Exception 或者 RuntimeException
2.继承自 Exception 的异常默认是受查异常
3.继承自 RuntimeException 的异常默认是非受查异常.
private String Name = "zd";
private String Word = "000000";
public static void login(String Name,String Word)throws NameException,WordException {
if(!Name.equals(Name)) {
throw new UserNameException("用户名错误!");
}
if(!Word.equals(Word)) {
throw new PassWordException("密码错误!");
}
System.out.println("登录成功!");
}
public static void main(String[] args) {
try {
login("zd","000000");
}catch (NameException e) {
e.printStackTrace();
}catch (WordException e) {
e.printStackTrace();
}
}