异常和线程

本章内容

  • 异常
  • 线程

异常

异常的概念

​ 异常:不正常,有毛病。程序当中的异常指的是出现的不正常的情况,会导致JVM的非正常停止。

在java等面向对象的编程中,异常本身就是一个类。产生异常就是创建异常对象,并且抛出了一个异常对象。JAVA处理异常的方式很粗暴,直接中断处理。

备注:指的并不是语法错误,如果语法错误,编译无法通过,不会产生字节码文件,根本不会在JVM中运行

异常体系

异常机制其实是帮助我们找到程序中出现的问题,异常的根类是java.lang.Throwable。它的下面有两个子类,一个是java.lang.Error 和java.lang.Exception,平常说的异常是java.lang.Exception

Throwable体系
异常和线程

  • Error:严重错误,无法通过,好比绝症,只能事先预防

  • Exception:异常,异常产生后程序员可以通过代码的方式进行纠正处理,使程序能够继续进行下去。好比感冒,可以吃药,
    异常和线程

异常的处理:

java处理异常的关键字:try catch finally throw throws

抛出异常throw

当我们在编写程序的时候,需要考虑程序可能出现的问题,比如说在定义方法 的时候,方法需要接收参数,对于调用者来说当调用方法的时候,需要接收参数,首先需要考虑的是参数数据的合法校验,若数据不合法,就应该告诉调用者,应该传递合法数据。这是就需要使用抛出异常的方式来告诉调用者。

在java当中。提供了一个关键字throw。用来抛出一个指定的异常对象。步骤:

1、创建一个异常对象,封装一些提示信息

2、需要将这个异常对象告知给调用者,通过关键字throw就可以完成,throw抛出一个异常对象

throw一般用来方法内,用来抛出一个具体的异常对象、将这个异常对象抛出给调用者处,并结束当前方法的执行

使用格式:

throw new 异常类名(参数)

示例:

//给调用者抛出一个空指针异常
throw new NullPointerException("要访问的arr数组不存在,值为null");
    
  //索引越界异常
    throw new IndexOutOfBoundsException("该索引超出引用范围");

Objects非空判断

在该类中,有一些静态的实用方法,这些方法是null-safe(空指针安全)或者null-tolerant(容忍空指针),在源码中,对对象的null值进行了抛出异常的操作

  • public static T requireNonNull(T obj) 查看指定的引用对象,不是null值
public static <T> T requireNonNull(T obj,){
    if(obj==null){
        throw new NullPointerException();
        return obj;
    }
}

​ 声明异常处理—throws

声明异常:将问题标识出来,报告给调用者,如果方法内通过throw抛出一个编译期异常,又没有通过捕获处理(try…catch),那么必须通过throws进行声明,让调用者处理

关键字throws运用在方法的声明上,用于表示不处理异常,提醒调用者该方法的调用携带异常,谁调用谁处理

格式:

修饰符 返回值类型 方法名(参数列表) throws 异常类名1,异常类名2....{}
public class DemoThrows throws FileNotFoundException{
    public static void main(String []args){
        
        read("C:/a.txt");
        
    }
    public static void read(String path) throws FileNotFoundException{
        //如果你传递的路径不是以.txt结尾的,抛出调用者文件找不到异常
        if(!path.endsWith(".txt")){
            //抛出文件找不到异常
            throw new FileNotFoundException("找不到文件");
        }
    }
    
}

捕获异常 try…catch

如果异常出现的话,会立刻终止程序

  1. 如果使用throws关键字来声明式处理,由该方法的调用者来处理
  2. 在方法中使用try…catch语句块来处理异常

try…catch的方式就是捕获异常

  • 捕获异常: java中对异常有针对性的语句块进行捕获,可以对出现的异常进行指定的方式处理

    捕获异常的格式:

    try{
        //可能会出现异常的代码
        //..
    }catch(异常类型 e){
        //处理异常的逻辑代码
        //1.记录日志
        //2.打印异常信息
        //3.继续往上抛
    }
    

    try:该语句块中可能出现异常的代码

    catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理

    备注:try 和 catch 都不能单独使用,一般建议连用

public class TryCatchDemo{
    
    public static void main(String []args){
        try{
             readFile("D:");
        }catch(FileNotFoundException e){//括号中需要定义异常类型
            //try中抛出的是什么类型,在括号中就定义成什么异常类型
            //打印异常信息
            System.out.println(e);
        }
          System.out.println("程序往下加载");
       
    }
    public static void readFile(String path) throws FileNotFoundException{
        if(!path.startWith("C:")){
            throw new FileNotFoundException("传递的文件不再C盘,文件找不到");
        }
    }
    
}

如何获取异常的信息

Throwbale类中定义了一些常用的API方法

  • public String getMessage() 获取异常的描述信息和原因(提示给用户)
  • public String toString () 获取异常的类型,异常的描述信息
  • public void printStackTrace() 打印异常的跟踪栈信息并且输出到控制台中

包含了异常的类型,原因, 还包括异常出现的位置,在开发和调试阶段一般都是使用**printStackTrace()**方法

finally语句块

finally:有些特定的代码无论异常是否发生,都必须执行,当程序发生异常,会引发程序的跳跃性,会导致有一些代码加载不到,而finally语句块就是用来解决这一类问题。在finally语句块存放的代码一般都是一定会被执行的

比如说,在try中打开了一些物理资源(磁盘文件,网络连接,数据库的连接)我们一般在使用完毕之后,必须关闭。可以使用finally语句块来实现。

finally语句块的语法格式:

try{
    //
}catch(异常类型 e){
    ...
}
... 
    finally{
        ....
    }

备注:中间的catch语句块可以省略。finally不是单独使用,建议三者连用

public class Finally{
    
    public static void main(String []args){
        try{
            System.out.println("开启IO流");
            readFile("b.txt");
    }catch (IOException e){
            e.printStackTrace();
        }
        finally{
             System.out.println("关闭IO流");
        }
        System.out.println("Over");
 
        
    }
       public static void readFile(String path) throws IOException{
        if(!path.endsWith(".txt")){
            throw new IOException("文件的后缀名不正确");
        }
    
}

备注

1.如果finally语句块中有return,永远返回fianlly语句块中的内容

2.当只有在try中或者catch中调用退出虚拟机的情况下,finally才不会被执行到,否则永远会被执行

异常的注意事项

当程序中出现了多个异常该如何捕获、处理

1.多个异常分别处理

2.多个异常一次捕获,多次处理

3.多个异常一次捕获,一次处理

一般我们都是使用一次捕获,多次处理

try{
    //可能出现异常的代码
}catch(异常类型A e){
    //处理异常逻辑
}catch(异常类型B e){
    //处理异常逻辑
}
...
    ...

注意:这种异常处理方式,要求多个catch的异常不能相同,并且若异常存在子父类关系,那么子类异常的处理在父类异常的上面,父类异常在下面

  • 运行时异常被抛出可以不处理,即不捕获也不声明
  • 如果finally中有return语句,永远返回finally语句块中的结果值
  • 如果父类抛出多个异常,子类重写父类方法时,抛出的需要和父类相同的异常,或者是父类异常的子类或者不抛出。
  • 父类方法如果没有抛出异常,子类重写该方法时也不可能抛出异常 ,此时,如果子类方法内产生了异常,不能声明抛出,只能捕获异常。

自定义异常

概述:

java中提供了不同的异常类,jdk官方提供的异常类始终是有限的,大多数情况下我们需要根据自己的业务需要来定义异常类,例如:年龄负数异常,成绩负数,登录异常,注册异常等等

什么是自定义异常类?

在开发中根据自己的业务的异常情况来定义异常类

我想自定义一个业务逻辑异常:注册异常

如何定义?

  1. 自定义一个编译期异常,自定义类继承于java.lang.Exception
  2. 自定义一个运行期异常类,自定义类继承于java.lang.RunTimeException
//首先,定义一个注册异常类 RegisterException
public class RegisterException extends RunTimeException{
    //空参构造
    public RegisterException(){
        
    }
    //有参构造
    public RegisterException(String message){
        
        super(message);
    }
    
}

public class Demo{
    //使用数组模拟数据库,已存在多个账户名称
    public static String []names={"小王","小张","小赵"};
    public static void main(String []args){
        Scanner sc=new Scanner(System.in);
        String name=sc.next();   
        //校验账户是否已被注册
        try{
            checkName(name);
            System.out.println("注册成功");
        }catch( RegisterException e){
            //异常的处理逻辑
            e.printSteakTrace();       
        }
       

    }
    public static boolean checkName(String username) throws RegisterException {
        for(String name:names){
            if(name.equals(username)){
                //表明名字已经注册过了,就跑出注册异常
                throw new RegisterException("名字已被注册");
            }
        }
        return true;
    }
    
}
上一篇:JAVA 知识梳理


下一篇:异常的接口声明