Java中的异常
- [一 异常的概念](#一 异常的概念)
- 异常的分类
- 异常的处理
-
- throws异常的声明
- throw异常处理
- [try - catch捕获异常](#try - catch捕获异常)
- finally关键字
- [四 自定义异常](#四 自定义异常)
前言
在编程的时候,难免会遇到一些特殊情况,这可能会导致中止,这时候就要想着在运行的过程中能不能将这个问题捕获到,方便后面解决,这时候就要引入异常这个概念了
一 异常的概念
在Java中将在代码执行过程中不正常的程序称为异常
Throwable :是异常体系的顶层类,其派⽣出两个⼦类Error和Exception
Error (错误):指的是Java虚拟机⽆法解决的严重问题,⽐如StackOverflowError和OutOfMemoryError,通常是JVM虚拟机内部问题
Exception (异常):异常产⽣后程序员可以通过代码进⾏处理,使程序继续执⾏
我们通常把Exception称为异常,Error是错误
例如:
算数异常
java
public class Test {
public static void main(String[] args) {
System.out.println(10/0);
}
}
除数不可以为0
运行结果中编译器会报出ArithmeticException算数异常
空指针异常
java
//空指针异常
public class Test {
public static void main(String[] args) {
int[] arr = null;
System.out.println(arr.length);
}
}
这里的arr数组是空的,不可以求其数组长度
NullPointerException空指针异常
数组下标访问异常
java
//数组下标越界访问异常
public class Test {
public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr[3]);
}
}
这里的下标是[0,2],没有3下标,这时候会出现数组下标访问异常
ArrayIndexOutOfBoundsException数组下标越界访问异常
异常的分类
Java中主要分为编译时异常和运行时候异常
编译时异常(受检查异常)
字如其名,也就是在编译时候发生的异常 ,也叫受检查异常
在没有运行编译器也会用红色波浪线提示
例如:

这里子类重写Object父类的方法clone方法
这里说这个异常为进行捕获出来
如果没有捕获或者说明这里就会报错
下面时运行的结果,就算没有运行编译器也会用红色波浪线提示
像下面这样进行声明了就不会报错了
java
public class Person{
private String name;
public int age;
@Override
//这里进行异常的声明或者捕获
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
运行时异常 (非受查异常)
在代码运行的时候编译器就会报错 ,在编译时并不会报错
RunTimeException 以及其⼦类对应的异常,都称为运⾏时异常 NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException等等,这些异常只有在运行时候才会报错,编译时可以通过的,这就是与编译时异常的区别
异常的处理
throws异常的声明
throws就是在方法中声明可能会出现的异常,用来提醒编译者,但是这个只是声明,并不是将其处理
修饰符 返回值类型 ⽅法名(参数列表) throws 异常类型1,异常类型2 等等
{ }
java
public class Test {
public static void fun(int[] arr)throws Exception{
System.out.println(arr.length);
}
public static void main(String[] args) throws Exception{
int[] arr = null;
fun(arr);
}
}

这里只是声明了异常并没有处理,相当于只是将这个异常逐渐传递下去,并没有得到真正的解决
就像这里主函数调用了含有异常声明的方法,主函数也要进行声明,相当于谁使用这个方法都要声明
如果不声明编译器就会报错
java
1.throws必须跟在⽅法的参数列表之后
2. 声明的异常必须是Exception或者Exception的⼦类
3. ⽅法内部如果抛出了多个异常,throws之后必须跟多个异常类型
4. 如果声明多个异常是同一个父类,可以直接声明父类一异常
5. 但是throws并么有处理异常,只是仅仅声明了
throw异常处理
throw new 异常类型();
java
public class Test {
public static void fun(int[] arr,int index){
if(arr==null){
throw new NullPointerException("空指针异常");
}
if(index<0||index>=arr.length){
throw new ArrayIndexOutOfBoundsException("数组下标越界异常");
}
System.out.println("后面的代码");
}
public static void main(String[] args) {
int[] arr = {1,2,3};
fun(arr,3);
}
}
这里一旦异常抛出,后面的代码就不会执行了,就像这里的3是超过数组下标,所以这里会抛出数组下标异常 ,后面的代码不会执行
运行结果如下
java
1.throw必须在方法内部
2.如果是RunTimeException或者其子类可以不用处理这些会在运行时由JVM来处理
3.异常一旦抛出,后面的代码就不会执行了
try - catch捕获异常
Java中为了捕获异常有了 try catch finally等关键字
java
try{
// 将可能出现异常的代码放在这⾥
//这里要捕获的异常类型与try中的异常一致
}catch(要捕获的异常类型 e){
//异常处理完后,后面的代码会执行
}catch(异常类型 e){
// 对异常进⾏处理
}finally{
// finally中代码一定会执行
}
例如
java
public class Test {
public static void main(String[] args) {
try{
int[] arr = {1,2,3};
System.out.println(arr[3]);
}catch (NullPointerException e){
e.printStackTrace();//处理异常
}catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
//异常被捕获以后后面的代码会正常执行
System.out.println("后面的代码执行了");
}
}
这里是下标越界的异常,被捕获了处理了,如果没有捕获,这里后面的代码就不会执行了
运行结果如下
例如下面如果没有捕获和处理异常,异常后面的代码不会执行
就像这里try中放的是出现的是数组下标访问异常,而我们这里捕获的是空指针异常,所以这里并不会处理异常,因此后面的代码就不执行了
那这时候可以在其后面放其可能出现异常的父类进行捕获,这样就不会出现这样的问题了
java
public class Test {
public static void main(String[] args) {
try{
int[] arr = {1,2,3};
System.out.println(arr[3]);
}catch (NullPointerException e){
e.printStackTrace();
}catch (Exception e){//可以捕捉所有异常,因为其是异常的父类
e.printStackTrace();
}
System.out.println("后面的代码执行了");
}
}
这里的空指针异常和数组下标访问异常都是Exception的子类,可以在后面放父类异常,防止没有捕获到
运行结果如下
但是不建议只是用父类Exception来进行捕获,所以尽量将其放在最后进行捕获父类异常,用起来兜底,防止自己没有考虑周全
try如果捕获到了异常,其try中后面的代码就不会执行了
java
try - catch注意事项
1.try中的异常如果被捕获到了异常,try中异常之后的代码不会执行
2.catch捕获的异常与抛出类型的异常不匹配,异常就不会被捕获
3.异常之间也有父子关系,捕获子类异常在捕获父类异常之后
finally关键字
有些时候我们需要一些代码是必须要执行到,无论是存在否异常,一些程序都要执行 ,那因此就要引出finally关键字
java
语法格式:
try{
// 可能会发⽣异常的代码
}catch(异常类型 e){
// 对捕获到的异常进⾏处理
}finally{
// 此处的语句⽆论是否发⽣异常,都会被执⾏到
}
java
public class Test {
public static void main(String[] args) {
try{
int[] arr = {1,2,3};
System.out.println(arr[3]);
}catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}
finally {
System.out.println("finally中的代码被执行了");
}
System.out.println("后面的代码执行了");
}
}
这里finally中的语句会执行,try-catch中的语句会执行
运行结果如下
既然都可以执行,那为什么还要finally语句呢
java
public class Test {
public static int fun(){
Scanner scanner = new Scanner(System.in);
try {
int sc = scanner.nextInt();
return sc;
}catch (InputMismatchException e){
e.printStackTrace();
}finally {
System.out.println("finally中的代码");
}
System.out.println("最后面的代码");
scanner.close();
return 0;
}
public static void main(String[] args) {
int n = fun();
System.out.println(n);
}
这里就算有return 语句,finally中代码一定会被执行 ,而try-catch-finally后面的代码不会执行了
1.先执行try中的代码
2.如果try中的代码有异常,那就在try中后面代码不会执行,在catch寻找是否有
与try中异常相匹配的
3.如果没有在catch中匹配到对应异常,如果没有异常将会向上传递
4.上层如果也没有处理异常,异常将会继续向上传递,一直到main函数为止
5.无论是否匹配到异常,finally中的代码都会被执行
四 自定义异常
虽然Java提供了很多异常,有时候一些异常不能满足我们的需求,这是我们就可以自定义异常来满足需求
这里我们模拟实现一个用户名和密码的登录
java
public class Test {
private String userId = "三三";
private String password = "060610";
public void login(String userId,String password) {
if(!this.userId.equals(userId)){
System.out.println("用户名错误");
}
if(!this.password.equals(password)){
System.out.println("密码错误");
}
System.out.println("登录成功");
}
public static void main(String[] args) {
Test test = new Test();
test.login("三三","060610");
}
}
这里密码和用户名都正确,这里会登录成功
运行结果如下
那我们可以在其用户名错误或者密码错误的时候抛出异常 吗
当然可以,这时候我们就要自定义一个异常,然后继承Exception或者RunTimeException
下面是两个自定义异常类
java
//继承基类异常
//用户名异常
//这里继承的是受查异常
class UserIdException extends Exception{
public UserIdException(String message){
super(message);
}
}
//密码异常
class PasswordException extends Exception{
public PasswordException(String message){
super(message);
}
}
Test类中的代码使用上面的自定义异常
java
class UserIdException extends Exception{
public UserIdException(String message){
super(message);
}
}
class PasswordException extends Exception{
public PasswordException(String message){
super(message);
}
}
public class Test {
private String userId = "三三";
private String password = "060610";
public void login(String userId,String password) {
try {
if(!this.userId.equals(userId)){
throw new UserIdException("用户名错误"+userId);
}
if(!this.password.equals(password)){
throw new PasswordException("密码错误"+password);
}
//如果没有异常就会登录成功
System.out.println("登录成功");
}catch (UserIdException e){
e.printStackTrace();
}catch (PasswordException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
Test test = new Test();
test.login("三三","060610");
}
}
这里用户名和密码都正确,所以是登录成功
如果用户名不对,就会爆出用户名异常,登录就不成功
自定义异常都是继承Exception或者RunTimeException
继承Exception就是受查异常(编译时异常)
继承RunTimeException就是非受查异常(运行时异常)
到这里就结束了,预知后事如何,请听下回分解