【JavaSE】【异常】

目录

1、异常的概念

1.常见的异常

1.1.算数异常

 1.2.数组越界异常

1.3.空指针异常

2.异常抛出机制

2、异常的体系结构 

 3、异常的分类

3.1编译时异常

3.2  运行时异常

 4、异常的处理

4.1 防御式编程

4.2 异常的抛出

 4.3 异常的捕获

1. 异常声明throws (throws + 异常类型)

2. try-catch捕获处理

3.  finally

4. 区别

5、自定义异常类

1、异常的概念

异常:指的是程序在执行过程中,出现的非正常情况,如果不处理最终会导致JVM的非正常停止。

1.常见的异常

1.1.算数异常

  public static void main(String[] args) {
        System.out.println(10/0);
    }
// 执行结果
Exception in thread "main" java.lang.ArithmeticException: / by zero

 1.2.数组越界异常

public static void main(String[] args) {
    int[] arr = {1,2,3};
    System.out.println(arr[100]);
}
// 执行结果
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100

1.3.空指针异常

public static void main(String[] args) {
    int[] arr = null;
    System.out.println(arr[100]);

}
// 执行结果
Exception in thread "main" java.lang.NullPointerException

2.异常抛出机制

Java中把不同的异常用不同的类表示,一旦发生某种异常,就创建该异常类型的对象,并且抛出(throw)。然后程序员可以捕获(catch)到这个异常对象,并处理;如果没有捕获(catch)这个异常对象,那么这个异常对象将会导致程序终止

2、异常的体系结构 

 Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception

 Throwable可分为两类:Error和Exception。

     分别对应着 java.lang.Error 与 java.lang.Exception 两个类。

1.  Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。一般不编写针对性的代码进行处理。

  • 例如:StackOverflowError(栈内存溢出)OutOfMemoryError(堆内存溢出,简称OOM)

2.  Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,需要使用针对性的代码进行处理,使程序继续运行。否则一旦发生异常,程序也会挂掉。例如

  • 空指针访问
  • 试图读取不存在的文件
  • 网络连接中断
  • 数组角标越界

 3、异常的分类

3.1编译时异常

 在程序编译期间发生的异常,称为编译时异常,也称为受检查异常(Checked Exception)

编译时报错:

Error:(17, 35) java: 未报告的异常错误java.lang.CloneNotSupportedException; 必须对其进行捕获或声明以便抛出

3.2  运行时异常

 在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常(Unchecked Exception)

 RunTimeException以及其子类对应的异常,都称为运行时异常。比如:

  • NullPointerException(空指针异常)
  • ArrayIndexOutOfBoundsException(数组越界异常)
  • ArithmeticException(算数异常)

注意:

注意:编译时出现的语法性错误,不能称之为异常。例如将 System.out.println 拼写错了, 写成了system.out.println. 此时编译过程中就会出错, 这是 "编译期" 出错。而运行时指的是程序已经编译通过得到class 文件了, 再由 JVM 执行过程中出现的错误.

 4、异常的处理

 在Java中,异常处理主要的5个关键字:throw、try、catch、final、throws。

4.1 防御式编程

 1. LBYL: Look Before You Leap. 在操作之前就做充分的检查. 即:事前防御型

 2. EAFP: It's Easier to Ask Forgiveness than Permission. "事后获取原谅比事前获取许可更容易". 也就是先操作, 遇到问题再处理. 即:事后认错型

4.2 异常的抛出

在Java中,可以借助throw关键字抛出一个指定的异常对象,将错误信息告知给调用者

public static void test(int[] a) {
    if(a == null){
        throw new NullPointerException();
    }
}

public static void main(String[] args) {
    //int[] arr = {1,2,3};
    int[] arr = null;
    test(arr);
}

注意事项:

  1. throw 必须写在方法体内部
  2. 抛出的对象必须是 Exception 或者 Exception 的子类对象
  3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类(运行时异常),则可以不用处理,直接交给JVM来处理
  4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
  5. 异常一旦抛出,其后的代码就不会执行

 4.3 异常的捕获

异常的捕获,也就是异常的具体处理方式:

  1. 异常声明throws throws + 异常类型)
  2. try-catch捕获处理 (try-catch-finally)

1. 异常声明throws (throws + 异常类型)

修饰符  返回值类型  方法名(参数列表) throws 异常类型1,异常类型2...{

}

 如果在编写方法体的代码时,某句代码可能发生某个`编译时异常`,不处理编译不通过,则此方法应`显示地`声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理

注意事项:

  1. throws 必须跟在方法的参数列表之后
  2. 声明的异常必须是 Exception 或者 Exception 的子类
  3. 方法内部如果抛出了多个异常,throws 之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可
  4. 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出
  5. 一旦方法体执行时出现异常,仍在代码处生成一个异常类的对象,此对象满足throws后的异常类型就会被抛出异常,异常出现的后续代码就不再执行.

2. try-catch捕获处理

throws 对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。如果真正要对异常进行处理,就需要try-catch。

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    try {
        int[] arr = null;
        System.out.println(arr.length);
        //
        System.out.println("上面抛异常,不在运行");
    }catch (ArithmeticException e){
        e.printStackTrace();
        System.out.println("捕捉了 ArithmeticException 异常");
    }catch (NullPointerException e){
        e.printStackTrace();
        System.out.println("捕捉了 NullPointerException 异常");
    }finally {
        scanner.close();
        System.out.println("finally一般用于资源的释放.......");
    }
    System.out.println("正常逻辑......");
}

public static void main(String[] args) {

    try (Scanner scanner = new Scanner(System.in);){
        int[] arr = null;
        System.out.println(arr.length);
        System.out.println("aaaa");     //上面抛出异常  后续代码不会执行
    }catch (ArithmeticException | NullPointerException e){
        e.printStackTrace();
        System.out.println("捕捉了 ArithmeticException | NullPointerException 异常");
    }catch (Exception e){
        e.printStackTrace();
        System.out.println("捕捉了 Exception 异常");
    }finally {
        //scanner.close();  try() 括起来  这一步可以省略
        System.out.println("finally一般用于资源的释放.......");
    }
    System.out.println("正常逻辑......");
}

如果多个异常的处理方式是完全相同, 也可以写成这样:

如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误:

 小结:

关于异常的处理方式

  • 异常的种类有很多, 我们要根据不同的业务场景来决定.
  • 对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果
  • 对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿
  • 对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试.

注意事项:

1. try 块内抛出异常位置之后的代码将不会被执行

2. 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM收到后中断程序----异常是按照类型来捕获的

3. try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获

4. 当try中存在多个异常时,从上往下执行,哪个先被捕获谁先抛出,同一时间只会抛出一个异常

public static void main(String[] args) {
    try {
        int[] arr = null;
        System.out.println(arr.length);
        System.out.println("aaaa");     //上面抛出异常  后续代码不会执行
    }catch (ArithmeticException e) {
        e.printStackTrace();
        System.out.println("捕捉了 ArithmeticException 异常");
    }finally {
        System.out.println("finally一般用于资源的释放.......");
    }
    System.out.println("正常逻辑......");
}

 由于 Exception 类是所有异常类的父类. 因此可以用这个类型表示捕捉所有异常.

  • 备注: catch 进行类型匹配的时候, 不光会匹配相同类型的异常对象, 也会捕捉目标异常类型的子类对象.
  • 如刚才的代码, NullPointerException 和 ArrayIndexOutOfBoundsException 都是 Exception 的子类,因此都能被捕获到

3.  finally

在写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源:网络连接、数据库连接、IO流等,在程序正常或者异常退出时,必须要对资源进进行回收。另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来解决这个问题的。

 注意finally中的代码一定会执行的,一般在finally中进行一些资源清理的扫尾工作。

public static int func() {
    try {
        return 10;
    } finally {
        return 20;
    }
}
public static void main(String[] args) {
        System.out.println(func());
}

//
20

finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行 finally).

但是如果finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.

4. 区别

  1. throws其实并没有解决异常,只是将异常抛给了调用者,由调用者使用try-catch-finally方式处,如果调用者也throws,一直到main方法依旧throws,,那么就交给JVM处理了,try-catch-finally真正处理掉了异常
  2. 在方法中throws碰到异常就不会继续向下执行,跳出方法,跟return有相似之处,因此在执行return和throws之前要执行finally,try-catch-finally在执行完之后继续执行try-catch之后的代码,try中出现异常,try中异常之后的代码就不执行
  3. throw手动抛出异常之后不能有其他代码

5、自定义异常类

可以通过创建自定义异常类来定义和使用特定于应用程序或领域的异常。Java中的异常是以类的形式存在的,所有的异常类都继承自java.lang.Exception类或其子类。

举例:

 创建两个异常类:密码异常类  和  用户名异常类  在编译时不需要强制捕获或声明它们

  1. 继承 RuntimeException(运行时异常)/  Exception,
  2. 重写 无参构造函数 调用父类构造函数
  3. 重写 有参构造函数 调用父类构造函数
public class PasswordExeption extends RuntimeException{
    public PasswordExeption() {
        super();
    }

    public PasswordExeption(String message) {
        super(message);
    }
}
public class UserNameException extends RuntimeException{
    public UserNameException() {
        super();
    }

    public UserNameException(String message) {
        super(message);
    }
}

测试:

public class Test2 {

    public String name = "admin";
    public String password = "123456";

    public void login(String name,String password){
        if(!this.name.equals(name)){
            System.out.println("用户名错误");
        }
        if(!this.password.equals(password)){
            System.out.println("密码错误");
        }
    }
    public void login1(String name,String password)
            throws UserNameException,PasswordExeption{
        if(!this.name.equals(name)){
            System.out.println("用户名错误");
            throw new UserNameException ("你的用户名错了");
        }
        if(!this.password.equals(password)){
            System.out.println("密码错误");
            throw new PasswordExeption("你的密码错了");
        }
    }

    public static void main(String[] args) {
        Test2 test2 = new Test2();
        //test2.login("admin1","1234567");
        try{
            test2.login1("admin1","1234567");
        }catch (UserNameException e) {
            e.printStackTrace();
            System.out.println("用户名错误");
        }catch (PasswordExeption e){
            e.printStackTrace();
            System.out.println("密码错误");
        }finally {

        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值