day22_Exception

  • 异常
  • 线程

分辨程序中异常和错误的区别

说出异常的分类

掌握虚拟机处理异常的方式

列举出四个常见的运行期异常

使用try-catch关键字处理异常

使用throws关键字处理异常

自定义异常类并处理之

说出进程、线程的概念

理解并发与并行的区别

开启新线程

异常

概念:异常即不正常。和正常情况有所区别,该异常部位功能受到影响。

程序异常指在程序运行过程中,出现非正常情况,最终会导致JVM的非正常停止。在Java等面向对象的编程语言中,异常本身就是一个类,产生异常就是创建异常对象并抛出了一个异常对象,Java处理异常的方式是:中断处理。

备注:异常指的并不是语法错误,语法错误并不能通过编译,不会产生字节码文件,根本不能进入运行

异常体系

​ 异常机制可以帮助我们找到程序中出现的问题,异常的根类是java.lang.Throwable,它的下面有两个子类:java.lang.Errorjava.lang.Exception,平常说的异常指的是 java.lang.Exception

Throwable体系:

  • ​ Error:严重错误,无法通过异常处理解决,好比绝症,只能事先预防
  • ​ Exception:异常,异常产生后程序员可以通过适当的代码进行纠正处理,使程序能够继续进行下去,好比感冒吃药。
 * java.lang.Throwable
 * 		|-----java.lang.Error:一般不编写针对性的代码进行处理。
 *       	|-----(虚拟机错误)VirtulMachineError
 *    			|-----StackOverFlowError
 *    			|-----(内存溢出异常)OutOfMemorError
 *    		|-----AWTError
 *    		|-----LinkageError
 * 		|-----java.lang.Exception:可以进行异常的处理
 * 			|------编译时异常(checked)
 * 					|-----IOException
 * 						|-----FileNotFoundException
 * 					|-----ClassNotFoundException
 * 			|------运行时异常(unchecked,RuntimeException)
 * 					|-----NullPointerException
 * 					|-----ArrayIndexOutOfBoundsException
 * 					|-----ClassCastException
 * 					|-----NumberFormatException
 * 					|-----InputMismatchException
 * 					|-----ArithmeticException
 *    				|-----NoSuchElementExceprion
 *    		|-----SQLException

异常的处理

​ Java异常处理的五个关键字:try catch finally throw throws

抛出异常throw

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

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

  1. ​ 创建一个异常对象,封装提示信息
  2. ​ 需要将这个异常对象告知给调用者,通过关键字throw就可以完成。throw抛出一个异常对象

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

使用格式:

throw new 异常类名 (参数)
示例:
    //给调用者抛出一个空指针异常
    throw new NullPointException("要访问的数组索引不存在");
	//索引越界
	throw new IndexOutOfBoundException("该索引超出引用范围");

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抛出了一个编译期异常,又没有通过捕获处理异常,那么必须通过throws进行声明,让调用者处理。

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

声明异常处理的格式:

修饰符  返回值类型  方法名(参数列表) throws XxxException1,XxxException2,...{//或多个异常种类
}
示例:
    public class ThrowsDemo{
     	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){
    //处理异常的逻辑代码
    //记录日志  //打印异常信息  //继续向上抛(不建议)
}

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

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

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

public class TryCatchDemo{
    public static void main(String []args){
        try{
            readFile("D:\a.txt");  
        }catch(FileNotFoundException e){//try中抛出的是什么异常类型,在括号中就定义什么异常类型
            //打印异常信息
            System.out.print(e);
        }
        
    }
    public static void readFile(String path) throws FileNotFoundException{
        if(!path.startsWith("C:")){
            throw new FileNotFoundException("文件找不到");
        }
    }
}

如何获取异常的信息

​ Throwable类中定义了一些常用的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 FinallyDemo01{
    public static void main(String []args){
        try{
            readFile("D:\a.txt");  
        }catch(FileNotFoundException e){//try中抛出的是什么异常类型,在括号中就定义什么异常类型
            //打印异常信息
            System.out.print(e);
        }
        finally{
            System.out.println("不管程序如何运行,此行代码一定被加载");
            System.out.println("此处可以进行资源关闭");
        }
    }
    
     public static void readFile(String path) throws FileNotFoundException{
        if(!path.startsWith("C:")){
            throw new FileNotFoundException("文件找不到");
        }
    }
}

备注:

​ 1.如果finally语句块中有return语句,永远返回的是finally语句块中的结果值

​ 2.当只有在try或者catch中调用退出JVM的相关方法,此时finally才不会被执行到,否则finally永远被执行

异常的注意事项

​ 当程序中出现了多个异常,那么又该如何捕获和处理呢?

  1. ​ 多个异常,分别处理
  2. ​ 多个异常一次捕获、多次处理
  3. ​ 多个异常一次捕获、一次处理

一般我们都是使用一次捕获、多次处理的方式

try {
    //可能出现异常的代码  多个异常
} catch (异常类型A e){ //当try中出现A异常的时候,用改catch来捕获
    //处理异常的逻辑 
}catch (异常类型B e){ //当try中出现B异常的时候,用改catch来捕获
    //处理异常的逻辑
}catch (异常类型C e){ //当try中出现C异常的时候,用改catch来捕获
    //处理异常的逻辑
}
...

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

子父重写时异常:

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

自定义异常

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

什么是自定义异常类?

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

​ 例如:自定义一个业务逻辑异常类: 注册异常 RegisterException

异常类定义的两种方式:

  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){
        String name = new Scanner(System.in).next();
        //校验账户是否出现异常
        try{
            checkName(name);
            System.out.println("注册成功!!!");
            names = Arrays.copyOf(name,names.length+1);
            names[names.length-1] = name;
        }catch(){
            e.printStackExceptionn;
        }
    }
    
 public static boolean checkName(String userName) throws RigisterException{
     for(String name : names){
         if(name.equals(userName)){
             throw new RigisterException("你的名字已经被注册过了");
         }
     }
 }
}
原文地址:https://www.cnblogs.com/mitoris/p/14106215.html